#!/bin/sh
# pre-commit hook for ngcpcfg, to store metadata
# implementation heavily based on etckeeper's pre-commit.d/30store-metadata

set -e

# ensure we're in the appropriate git directory (/etc/ngcp-config/ by default),
# we don't rely on ${NGCPCTL_MAIN} just in case we're invoked directly,
# e.g. via `/etc/ngcp-config/.git/hooks/pre-commit` (outside of ngcpcfg)
cd "$(dirname "${0}")/../../"

if ! git rev-parse --git-dir >/dev/null 2>&1 ; then
  echo "Warning: $(pwd) is not a git repository, ignoring $0"
  exit 0
fi

filter_ignore() {
  if [ -e ".gitignore" ]; then
    listfile="$(mktemp -t ngcpcfg-git.XXXXXXXXXX)"
    (git ls-files -oi --exclude-standard; git ls-files -oi --exclude-standard --directory) | sort | uniq > "$listfile" || true
    sed 's/^\.\///' | grep -xFvf "$listfile"
    rm -f "$listfile"
    unset listfile
  else
    cat -
  fi
}

shellquote() {
  # Single quotes text, escaping existing single quotes.
  sed -e "s/'/'\"'\"'/g" -e "s/^/'/" -e "s/$/'/"
}

generate_metadata() {
  # This function generates the script commands to fix any file
  # ownerships that aren't owner=root, group=root, as well as to
  # store the permissions of files.
  # The script is produced on stdout.  Errors go to stderr.
  #
  # The script can use a 'maybe' function, which only runs a command
  # if the file in its last argument exists.

  # We want files in the directory containing VCS data
  # but we want find to ignore the VCS files themselves.
  #
  # (Note that when using this, the find expression must end with
  # -print or -exec, else the excluded directories will actually be
  # printed!)

  # Keep the sort order the same at all times.
  LC_COLLATE=C
  export LC_COLLATE

  # git does not track directories,
  # so empty directories must be stored specially.
  find . -path ./.git -prune -o -type d -empty -print |
    sort | shellquote | sed -e "s/^/mkdir -p /"

  # Store things that don't have the default user or group.
  # Store all file modes, in case the user has an unusual umask.
  find . -path ./.git -prune -o \( -type f -or -type d \) -print | filter_ignore | sort | perl -ne '
          BEGIN { $q=chr(39) }
          sub uidname {
                  my $want=shift;
                  if (exists $uidcache{$want}) {
                          return $uidcache{$want};
                  }
                  my $name=scalar getpwuid($want);
                  return $uidcache{$want}=defined $name ? $name : $want;
          }
          sub gidname {
                  my $want=shift;
                  if (exists $gidcache{$want}) {
                          return $gidcache{$want};
                  }
                  my $name=scalar getgrgid($want);
                  return $gidcache{$want}=defined $name ? $name : $want;
          }
          chomp;
          my @stat=stat($_);
          my $mode = $stat[2];
          my $uid = $stat[4];
          my $gid = $stat[5];
          s/$q/$q"$q"$q/g; # escape single quotes
          s/^/$q/;
          s/$/$q/;
          if ($uid != $>) {
                  printf "maybe chown $q%s$q %s\n", uidname($uid), $_;
          }
          if ($gid != $)) {
                  printf "maybe chgrp $q%s$q %s\n", gidname($gid), $_;
          }
          printf "maybe chmod %04o %s\n", $mode & 07777, $_;
  '
}

ngcpcfg_perms=$(mktemp -t ngcpcfg-perms.XXXXXXXXXX)
chown root:root "${ngcpcfg_perms}"
# Make sure the file is not readable by others, since it can leak
# information about contents of non-readable directories
chmod 700 "${ngcpcfg_perms}"

echo "# Generated by ngcpcfg. Do not edit." > "${ngcpcfg_perms}"
echo >> "${ngcpcfg_perms}"
generate_metadata >> "${ngcpcfg_perms}"
mv "${ngcpcfg_perms}" .ngcpcfg_perms

git add .ngcpcfg_perms
