--- /dev/null
+*.[ch] filter=NHtext merge=NHsubst
+* text=auto
+*.hqx -text
+*.sln -text
+*.vcxproj -text
--- /dev/null
+ ___ _
+ | \ _____ _____| |___ _ __ ___ _ _
+ | |) / -_) V / -_) / _ \ '_ \/ -_) '_|
+ |___/\___|\_/\___|_\___/ .__/\___|_|
+ |_|
+
+$NHDT-Date$
+
+Welcome to the NetHack Infrastructure Developer's Guide.
+
+This is the info you need if you are developing code for NetHack.
+(This information is from DevTeam. If you are working with a variant please
+check for additional documentation for that variant.)
+
+For information on building NetHack, see README in the top level directory.
+For information on playing NetHack, see the Guidebook in the doc directory.
+
+DANGER! WORK IN PROGRESS! Known issues marked XXX.
+
+CONTENTS
+1. email
+2. git repositories
+3. bug reporting
+4. git configuration
+5. variable expansion
+6. reserved names
+------------------------------------------------------------------------------
+1. email
+Email to devteam@nethack.org will usually get a response, but it may take a
+while. Please do not send save files, binary screen grabs, or other large
+things.
+------------------------------------------------------------------------------
+2. git repositories
+The public NetHack git repository is available (read-only) on SourceForge at:
+ git://git.code.sf.net/p/nethack/NHsource
+------------------------------------------------------------------------------
+3. bug reporting
+Please use the form at http://www.nethack.org/common/contact.html (or send
+us an email if that's more appropriate).
+------------------------------------------------------------------------------
+4. git configuration
+A. If you have never set up git on this machine before:
+ (This assumes you will only be using git for NetHack. If you are going to
+ use it for other projects as well, think before you type.)
+ Tell git what name (or nicname) and email address to use for you:
+ git config --global user.name "MY NAME"
+ git config --global user.email USER@EXAMPLE.COM
+ You probably want to set up a credential cache.
+ Mac OS X:
+ git config --global credential.helper osxkeychain
+XXX windows
+XXX linux
+B. Specify the prefix for variable substitution:
+ (This assumes you are not a member of DevTeam or any variant's development
+ team. If you are, this may be wrong. Look for more specific documentation.)
+ Decide where you want to put this info; it should NOT be inside the
+ tree you cloned from git. I use ~/nethack/GITADDDIR; for that base,
+ create the needed directories and edit the file:
+ ~/nethack/GITADDDIR/DOTGIT/PRE
+ Put this in it (if your OS is not Unix-like you may need to change
+ the first line):
+ #!/bin/sh
+ git config nethack.substprefix MINE
+C. Configure the repository:
+ - cd to the top level of the repository
+ - tell the repository about the directory you created above:
+ git config nethack.gitadddir FULL_PATH_TO_GITADDDIR
+ so for the example above:
+ git config nethack.gitadddir ~/nethack/GITADDDIR
+ - do the automated setup:
+ perl DEVEL/nhgitset.pl
+ If it complains, fix what it complains about. nhgitset.pl accepts
+ the following options:
+ -v verbose
+ -n dry run
+ You can re-run nhgitset.pl as often as needed; occasionally we will
+ update it and ask you to run it again.
+D. aliases
+ Two aliases are installed by nhgitset.pl:
+ nhadd
+ nhcommit
+ These two commands take the same options as the normal git add and commit
+ commands but perform RCS/CVS-style variable substitution. Note that the
+ substitutions do not show up in the working directory.
+
+ Note that nothing terrible will happen if you do not use the nh* versions
+ of the commands.
+
+ Supported substitutions:
+ MINE-Date the commit time and date
+ Experimental substitutions:
+ MINE-Revision CVS style revision number
+ MINE-Branch the current git branch
+
+That's it. If you need to do something more when setting up your repository,
+keep reading. Otherwise, you are done with this section.
+
+1) to run your own hooks in addition to ours:
+ name your hook
+ WHEN-HOOKNAME
+ where WHEN is
+ PRE (run your code before the NetHack hook)
+ POST (run your code after the NetHack hook)
+ and HOOKNAME is the normal git name of the hook.
+ Be sure to test carefully since the composition of two bits of code may or
+ may not do what you want.
+2) to install other bits on setup:
+ Put additional files in the GITADDDIR tree. Use "DOTGIT" instead of
+ ".git". If a file called PRE, POST, or INSTEAD exists in a
+ subdirectory of GITADDDIR, it is run before the copy, after the copy,
+ or instead of the copy. No copy operation is attempted in the DOTGIT
+ directory; use a script and standard git commands to change the
+ contents as needed.
+3) NB: In all namespaces, anything that matches m/^nh/i or m/^nethack/i is
+ reserved.
+------------------------------------------------------------------------------
+5. variable expansion
+A. Introduction
+ We have implemented an RCS/CVS/SVN style variable expansion mechanism.
+ References of either of the formats:
+ $PREFIX-VARNAME$
+ $PREFIX-VARNAME: VALUE $
+ will be handled (if enabled).
+
+ The PREFIX is the value in the git config variable nethack.substprefix.
+ VARNAME is one of:
+ Date
+ Branch (experimental)
+ Revision (experimental)
+ other names will give a warning.
+
+B. Enabling variable expansion
+ Variable expansion is controlled by the .gitattributes file.
+
+ To enable variable expansion:
+ pattern filter=NHtext merge=NHsubst
+ To disable variable expansion:
+ pattern -filter
+
+ More information: "git help gitattributes"
+
+C. Oddities
+ To trigger variable expansion, you _must_ use "git nhadd" or "git nhcommit"
+ instead of "git add" or "git commit." Nothing terrible will happen if you
+ use the wrong one, but the values will not be updated.
+
+ Due to the way this abuses git filters, the updated values are not visible
+ in your working tree.
+
+D. Using your own hooks
+ You can use your own hooks - put them in .git/hooks as usual BUT name them
+ as follows:
+ WHEN-HOOKNAME
+ where WHEN is:
+ PRE (execute the code before the NetHack hook)
+ POST (execute the code after the NetHack hook)
+ and HOOKNAME is the normal git name for the hook.
+
+ Test carefully - interactions between hooks can be nasty.
+------------------------------------------------------------------------------
+6. reserved names
+ Anything that matches m/^nh/i or m/^nethack/i is reserved in all
+ namespaces (environment, file names, git config, etc).
+------------------------------------------------------------------------------
--- /dev/null
+#!/usr/bin/perl
+# wrapper for nhadd and nhcommit aliases
+# $NHDT-Date$
+
+%ok = map { $_ => 1 } ('add', 'commit');
+
+die "Bad subcommand '$ARGV[0]'" unless $ok{$ARGV[0]};
+
+if(length $ENV{GIT_PREFIX}){
+ chdir($ENV{GIT_PREFIX}) or die "Can't chdir $ENV{GIT_PREFIX}: $!";
+}
+
+$ENV{NHMODE} = 1;
+exec "git", @ARGV or die "Can't exec git: $!";
--- /dev/null
+#
+# NHgithook.pm
+# NetHack Git Hook Module
+# $NHDT-Date$
+
+package NHgithook;
+use Cwd;
+
+###
+### CONFIG
+###
+my $trace = 0;
+my $tracefile = "/tmp/nhgitt.$$";
+
+# OS hackery
+my $DS = quotemeta('/');
+if ($^O eq "MSWin32")
+{
+ $DS = quotemeta('\\');
+}
+
+our %saved_env;
+our @saved_argv;
+our $saved_input;
+
+sub saveSTDIN {
+ @saved_input = <STDIN>;
+
+ if($trace){
+ print TRACE "STDIN:\n";
+ print TRACE $saved_input;
+ print TRACE "ENDSTDIN\n";
+ }
+
+ tie *STDIN, 'NHIO::STDIN', @saved_input;
+}
+
+# XXX this needs a re-write (don't tie and untie, just set NEXT=0)
+# (the sensitive thing is @foo = <STDIN> )
+sub resetSTDIN{
+ my $x = tied(*STDIN);
+ my %x = %$x;
+ my $data = @$x{DATA};
+ untie *STDIN;
+ tie *STDIN, 'NHIO::STDIN', $data;
+}
+
+# don't need this now
+#sub restore {
+# open STDIN, "<", \$saved_input or die "reopen STDIN: $!";
+# @ARGV = @saved_argv;
+# %ENV = %saved_env;
+#}
+
+sub PRE {
+ &do_hook("PRE");
+}
+
+sub POST {
+ &do_hook("POST");
+}
+
+# PRIVATE
+sub do_hook {
+ my($p) = @_;
+ my $hname = $0;
+ $hname =~ s!^((.*$DS)|())(.*)!$1$p-$4!;
+ if(-x $hname){
+ print TRACE "START $p: $hname\n" if($trace);
+
+ open TOHOOK, "|-", $hname or die "open $hname: $!";
+ print TOHOOK <STDIN>;
+ close TOHOOK or die "close $hname: $! $?";
+
+ print TRACE "END $p\n" if($trace);
+ }
+}
+
+sub trace_start {
+ return unless($trace);
+ my $self = shift;
+ open TRACE, ">>", $tracefile;
+ print TRACE "START CLIENT PID:$$ ARGV:\n";
+ print TRACE "CWD: " . cwd() . "\n";
+ print TRACE "[0] $0\n";
+ my $x1;
+ for(my $x=0;$x<scalar @ARGV;$x++){
+ $x1 = $x+1;
+ print TRACE "[$x1] $ARGV[$x]\n";
+ }
+ print TRACE "ENV:\n";
+ foreach my $k (sort keys %ENV){
+ next unless ($k =~ m/(^GIT_)|(^NH)/);
+ print TRACE " $k => $ENV{$k}\n";
+ }
+}
+
+BEGIN {
+ %saved_env = %ENV;
+ @saved_argv = @ARGV;
+ &trace_start;
+}
+
+###
+### ugly mess so we can re-read STDIN
+###
+package NHIO::STDIN;
+sub TIEHANDLE {
+ my $class = shift;
+ my %fh;
+ # XXX yuck
+ if(ref @_[0]){
+ $fh{DATA} = @_[0];
+ } else {
+ $fh{DATA} = \@_;
+ }
+ $fh{NEXT} = 0;
+ return bless \%fh, $class;
+}
+
+sub READLINE {
+ my $self = shift;
+ return undef if($self->{EOF});
+ if(wantarray){
+ my $lim = $#{$self->{DATA}};
+ my @ary = @{$self->{DATA}}[$self->{NEXT}..$lim];
+ my @rv = @ary[$self->{NEXT}..$#ary];
+ $self->{EOF} = 1;
+ return @rv;
+ } else{
+ my $rv = $self->{DATA}[$self->{NEXT}];
+ if(length $rv){
+ $self->{NEXT}++;
+ return $rv;
+ } else {
+ $self->{EOF} = 1;
+ return undef;
+ }
+ }
+}
+
+sub EOF {
+ $self = shift;
+ return $self->{EOF};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+NHgithook - common code for NetHack git hooks (and other git bits)
+
+=head1 SYNOPSIS
+
+ BEGIN {
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ if ($^O eq "MSWin32")
+ {
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+
+ push(@INC, $ENV{GIT_DIR}.$PDS."hooks"); # for most hooks
+ push(@INC, ($0 =~ m!^(.*)$DS!)[0]); # when the above doesn't work
+
+ $gitdir = `git rev-parse --git-dir`; # and when the above really doesn't work
+ $gitdir =~ s/[\r\n]*$/;
+ push(@INC, $gitdir.$PDS."hooks");
+ }
+ use NHgithook;
+
+ &NHgithook::saveSTDIN;
+ &NHgithook::PRE;
+ (core hook code)
+ &NHgithook::POST;
+
+=head1 DESCRIPTION
+
+Buffers call information so multiple independent actions may be coded for
+Git hooks and similar Git callouts.
+
+=head1 SETUP
+
+Changing the C<$trace> and C<$tracefile> variables requires editing the
+module source. Setting C<$trace> enables tracing, logs basic information,
+and leaves the C<TRACE> filehandle open for additional output; output to this
+filehandle must be guarded by C<$NHgithook::trace>. Setting
+C<$tracefile> specifies the file used for trace output. Note that C<$$>
+may be useful since multiple processes may be live at the same time.
+
+=head1 FUNCTIONS
+
+ NHgithook::saveSTDIN reads STDIN until EOF and saves it
+ NHgithook::PRE runs the PRE hook, if it exists
+ NHgithook::POST runs the POST hook, if it exists
+
+=head1 BUGS
+
+Some features not well tested, especially under Windows.
+
+=head1 AUTHOR
+
+Kenneth Lorber (keni@his.com)
--- /dev/null
+#!/usr/bin/perl
+#
+# NHsubst
+# $NHDT-Date$
+# git merge driver for substitutions (like RCS/CVS)
+# driver line: .... %O %A %B %L
+use strict;
+
+my $debug = 0;
+my $rawin = 0; # feed diff to stdin for testing (do NOT set $debug=1)
+
+# We want TRACE open so we don't need to test $debug everywhere, but we skip
+# this first block because it's expensive and dumpfile() hangs with $rawin.
+my $sink = ($^O eq "MSWin32") ? "NUL" : "/dev/null";
+my $dbgfile = ($^O eq "MSWin32") ? "$ENV{TEMP}.$$" : "/tmp/trace.$$";
+open TRACE, ">>", ($debug==0)? $sink : $dbgfile;
+if($debug){
+ print TRACE "START CLIENT ARGV:\n";
+ print TRACE "[0] $0\n";
+ my $x1;
+ for(my $x=0;$x<scalar @ARGV;$x++){
+ $x1 = $x+1;
+ print TRACE "[$x1] $ARGV[$x]\n";
+ }
+ print TRACE "ENV:\n";
+ foreach my $k (sort keys %ENV){
+ next unless ($k =~ m/^GIT_/);
+ print TRACE " $k => $ENV{$k}\n";
+ }
+ print TRACE "CWD: " . `pwd`;
+ &dumpfile($ARGV[0], "[0O]");
+ &dumpfile($ARGV[1], "[1A]");
+ &dumpfile($ARGV[2], "[2B]");
+ print TRACE "L=$ARGV[3]\n";
+ print TRACE "END\n";
+}
+
+my $mark_len = $ARGV[3];
+$mark_len = 3 if($mark_len==0 && $rawin);
+
+my $mark_start = '<' x $mark_len;
+my $mark_middle = '=' x $mark_len;
+my $mark_end = '>' x $mark_len;
+
+my $PREFIX;
+# pick up the prefix for substitutions in this repo
+if($rawin){
+ $PREFIX = "TEST";
+} else {
+ $PREFIX = `git config --local --get nethack.substprefix`;
+ chomp($PREFIX);
+}
+
+my @out;
+my $cntout;
+if($rawin){
+ @out = <STDIN>;
+} else {
+ #system "git merge-file -p .... > temp
+ my $tags = "-L CURRENT -L ANCESTOR -L OTHER"; # XXX should "CURRENT" be "MINE"?
+ @out = `git merge-file -p $tags $ARGV[1] $ARGV[0] $ARGV[2]`;
+ #NB: we don't check the exit value because it's useless
+ print TRACE "MERGE-FILE START\n".join("",@out)."MERGE-FILE END\n";
+}
+
+($cntout,@out) = &edit_merge(@out);
+
+if($rawin){
+ print "COUNT: $cntout\n";
+ print @out;
+} else {
+ # spit @out to $ARGV[1] (careful: what about EOL character?)
+ open OUT, ">$ARGV[1]" or die "Can't open $ARGV[1]";
+ print OUT @out;
+ close OUT;
+
+ print TRACE "WRITING START ($ARGV[1])\n".join("",@out)."WRITING END\n";
+ &dumpfile($ARGV[1], "READBACK");
+}
+print TRACE "COUNT: $cntout\n";
+
+exit( ($cntout>0) ? 1 : 0);
+
+#git merge-file [-L <current-name> [-L <base-name> [-L <other-name>]]]
+# [--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>]
+# [--[no-]diff3] <current-file> <base-file> <other-file>
+#The `merge.*.driver` variable's value is used to construct a command to run to merge ancestor's
+# version (%O), current version (%A) and the other branches' version (%B). These three tokens are
+# replaced with the names of temporary files that hold the contents of these versions when the
+# command line is built. Additionally, %L will be replaced with the conflict marker size (see
+# below).
+
+# keep failing so we don't need to keep changing the setup while building this script
+
+sub dumpfile {
+ my($file, $tag) = @_;
+ print TRACE "FILE $tag START\n";
+ print TRACE `hexdump -C $file`;
+ print TRACE "FILE END\n";
+}
+
+sub edit_merge {
+ my(@input) = @_;
+ # $::count is a bit ugly XXX
+ local $::count = 0; # we need the number of conflicts for exit()
+ my @out;
+
+ local $_;
+ while($_ = shift @input){
+ if(m/^$mark_start /){
+ print TRACE "FOUND A CONFLICT\n";
+ my @conflict;
+ push(@conflict, $_);
+ while($_ = shift @input){
+ push(@conflict, $_);
+ if(m/^$mark_end /){
+ last;
+ }
+ }
+ push(@out, &edit_conflict(@conflict));
+ } else {
+ push(@out, $_);
+ }
+ }
+ print TRACE "RETURN count=$::count\n";
+ return($::count, @out);
+}
+
+sub edit_conflict {
+ my(@in) = @_;
+
+ print TRACE "EDIT START: " . scalar(@in)."\n";
+ if($debug){
+ foreach my $x (@in){ my $xx = $x; chomp($xx); print TRACE "-$xx-\n"; }
+ }
+ print TRACE "EDIT END INPUT\n";
+
+ # one-line change - use as base case to develop the code
+ # ours ARGV[1] top-of-diff
+ # theirs ARGV[2] bottom-of-diff
+ # simple conflict:
+ # [0] <<<<<<< d1
+ # [1] $$PREFIX-Date: 1 ...
+ # [2] =======
+ # [3] $$PREFIX-Date: 3 ...
+ # [4] >>>>>>> d3
+ if(scalar(@in) == 5 && $in[2] =~ m/^$mark_middle/){
+ my $back = &merge_one_line_maybe($in[1],$in[3]); # (ours, theirs)
+ if(!defined $back){
+ $::count++; # leave the conflict
+ return @in;
+ } else {
+ return ($back);
+ }
+ # NOTREACHED
+ } else {
+# XXX LATER
+# Start at the top of both sections and work downwards. As long as the lines can be merged,
+# push them out and keep going. If there are lines left, we will still have a conflict but
+# we can try to make it smaller. Push out the start-conflict marker. Start at the
+# bottom of both section and work upwards. As long as the lines can be merged, reverse push out
+# the merged line and keep going. (We know there will be lines left at some point.) Push out
+# remaining (middle) lines from OURS. Push out mark_middle. Push out remaining middle lines
+# from THEIRS. Push out end-conflict marker. $::count++; return (@a,$b,@c,$d,@e,$f,@g)
+# @a
+# $b = <<<
+# @c
+# $d = ===
+# @e
+# $f = >>>
+# @g
+ }
+ # not matched - return the unchanged conflict
+ $::count++;
+ return @in;
+}
+
+# XXX This is expensive. Add a quick check for "anything that looks like a subst var" and just
+# declare the lines unmergeable if it fails.
+sub merge_one_line_maybe {
+ my($ours, $theirs) = @_;
+
+ my $more = 1;
+ my $fail = 0;
+ my $out = '';
+ # TYPES:
+ # 0 no match
+ # 1 unexpanded var
+ # 2 expanded var
+ # 3 non-var text
+ my($ourstype, $theirtype);
+ my($oursvar, $theirvar);
+ my($oursval, $theirval);
+
+ while($more){
+ ($ourstype, $theirtype) = (0,0);
+ ($oursvar, $theirvar) = (undef, undef);
+ ($oursvar, $theirvar) = (undef, undef);
+ # unexpanded var
+ if($ours =~ m/\G\$$PREFIX-([A-Z][a-z]+)\$/gc){
+ $ourstype = 1;
+ $oursvar = $1;
+ }
+ if($theirs =~ m/\G\$$PREFIX-([A-Z][a-z]+)\$/gc){
+ $theirtype = 1;
+ $theirvar = $1;
+ }
+ # expanded var
+ unless($ourstype){
+ if($ours =~ m/\G\$$PREFIX-([A-Za-z]+):\s+(.*?)\s\$/gc){
+ $ourstype = 2;
+ $oursvar = $1;
+ $oursval = $2;
+ }
+ }
+ unless($theirtype){
+ if($theirs =~ m/\G\$$PREFIX-([A-Za-z]+):\s+(.*?)\s\$/gc){
+ $theirtype = 2;
+ $theirvar = $1;
+ $theirval = $2;
+ }
+ }
+ # non-var text
+ unless($ourstype){
+ if($ours =~ m/\G(\$?[^\x24]*)/gc){
+ $ourstype = 3;
+ $oursval = $1;
+ }
+ }
+ unless($theirtype){
+ if($theirs =~ m/\G(\$?[^\x24]*)/gc){
+ $theirtype = 3;
+ $theirval = $1;
+ }
+ }
+
+ # are we done?
+ if(pos($ours)==length $ours && pos($theirs) == length $theirs){
+ $more = 0;
+ }
+ if($ourstype == 0 && $theirtype == 0){
+ die "NHsubst MERGE FAILED - aborted infinite loop\n";
+ }
+
+ # now see if ours and their match or can be resolved
+ # text
+ if($ourstype == 3 && $theirtype == 3){
+ if($oursval eq $theirval){
+ $out .= $oursval;
+ next;
+ }
+ return undef;
+ }
+ if($ourstype == 3 || $theirtype == 3){
+ return undef;
+ }
+# XXX we could do better: on failure of one field, return 2 lines with the fields we _can_ fix
+# substituted into those lines, leaving only the fail-to-match bits for the user to
+# deal with. Later.
+ # vars (all 4 cases)
+ if($oursvar ne $theirvar){
+ return undef;
+ }
+ my $m = merge_one_var_maybe($oursvar, $oursval, $theirval);
+ if(! defined $m){
+ return undef;
+ }
+ $out .= $m;
+ }
+ return $out;
+}
+
+# return undef if we can't merge the values; $NAME: VALUE $ or $NAME$ (as appropriate) if we can.
+sub merge_one_var_maybe {
+ my($varname, $oursval, $theirval) = @_;
+ my $resolvedas;
+ {
+ no strict;
+ my $fn = "PREFIX::$varname";
+ if(defined &$fn){
+ $resolvedas = &$fn($PREFIX,$varname,$oursval, $theirval);
+ } else {
+ $resolvedas = undef; # can't resolve
+ }
+ }
+
+ if(!defined $resolvedas){
+ $::count++; # we have an externally visible conflict
+ return undef;
+ } else {
+ return $resolvedas;
+ }
+ # NOTREACHED
+}
+
+package PREFIX;
+# Resolve the conflict of a single var's 2 values. Return undef to leave the conflict.
+sub Date {
+ my($PREFIX, $varname, $mine, $theirs) = @_;
+ my $m = ($mine =~ m/(\d+)/)[0];
+ my $t = ($theirs =~ m/(\d+)/)[0];
+ return undef unless ($m>0) && ($t>0);
+
+ return "\$$PREFIX-$varname: " . (($m>$t)?$mine:$theirs) .' $';
+}
+
+#sub Header {
+#sub Author {
+
+sub Branch {
+ my($PREFIX, $varname, $mine, $theirs) = @_;
+ return "\$$PREFIX-$varname: $mine \$";
+}
+
+sub Revision {
+ my($PREFIX, $varname, $mine, $theirs) = @_;
+ return "\$$PREFIX-$varname: $mine \$";
+}
+__END__
+
+TEST 1:
+<<< d1
+$TEST-Date: 1 $
+===
+$TEST-Date: 3 $
+>>> d3
+
+TEST 2:
+nothing
+at all
+
+TEST 3:
+<<< d1
+a line
+===
+one line
+two lines
+>>> d3
+
+TEST 4:
+<<< d1
+$TEST-Date: 1 $ yes
+===
+$TEST-Date: 1 $ no
+>>> d3
+
+TEST 5:
+<<< d1
+$TEST-Date: 3 $ yes
+===
+$TEST-Date: 1 $ yes
+>>> d3
+
+TEST 6:
+<<< d1
+$TEST-Date: 3 $ yes$TEST-Date: 4 $
+===
+$TEST-Date: 1 $ yes$TEST-Date: 5 $
+>>> d3
+
+TEST 7:
+<<< d1
+$TEST-Branch: mine $
+===
+$TEST-Branch: theirs $
+>>> d3
--- /dev/null
+#!/usr/bin/perl
+#
+# NHtext
+# $NHDT-Date$
+# clean/smudge filter for handling substitutions
+use strict;
+
+my $debug = 0;
+
+my $sink = ($^O eq "MSWin32")? "NUL" :"/dev/null";
+my $dbgfile = ($^O eq "MSWin32") ? "$ENV{TEMP}.$$" : "/tmp/trace.$$";
+open TRACE, ">>", ($debug==0)? $sink : $dbgfile;
+print TRACE "START CLIENT ARGV:\n";
+print TRACE "[0] $0\n";
+my $x1;
+for(my $x=0;$x<scalar @ARGV;$x++){
+ $x1 = $x+1;
+ print TRACE "[$x1] $ARGV[$x]\n";
+}
+print TRACE "ENV:\n";
+foreach my $k (sort keys %ENV){
+ next unless ($k =~ m/^(GIT_|NH)/);
+ print TRACE " $k => $ENV{$k}\n";
+}
+print TRACE "CWD: " . `pwd`;
+print TRACE "END\n";
+
+# pick up the prefix for substitutions in this repo
+my $PREFIX = `git config --local --get nethack.substprefix`;
+chomp($PREFIX);
+
+my $submode = 0; # ok to make non-cleaning changes to file
+my $mode;
+
+if($ARGV[0] eq "--clean"){
+ $mode = "c";
+ if(0 == 0+$ENV{NHMODE}){
+ $submode = 1; # do NOT add extra changes to the file
+ print TRACE "SKIPPING\n";
+ }
+} elsif($ARGV[0] eq "--smudge"){
+ $mode = "s";
+} else {
+ warn "Unknown mode '$ARGV[0]'\n";
+ exit 1;
+}
+
+# XXX for now, there isn't any - if we get called, we subst. No options for now.
+# get relevent config info
+#XXX
+#git check-attr -a $ARGV[1]
+
+# process stdin to stdout
+
+while(<STDIN>){
+ print TRACE "IN: $_";
+ # $1 - var and value (not including trailing $)
+ # $2 - var
+ # $4 - value or undef
+# s/\$$PREFIX-(([A-Za-z][A-Za-z0-9_]*)(: ([^\N{DOLLAR SIGN}]+))?)\s*\$/&handlevar($2,$4)/eg;
+ s/\$$PREFIX-(([A-Za-z][A-Za-z0-9_]*)(: ([^\x24]+))?)\s*\$/&handlevar($2,$4)/eg;
+ print;
+ print TRACE "OT: $_";
+}
+
+sub handlevar {
+ my($var, $val) = @_;
+
+ my $subname = "PREFIX::$var";
+ if(defined &$subname){
+ no strict;
+ $val = &$subname($val,$mode,$submode);
+ } else {
+ warn "No handler for \$$PREFIX-$var\n";
+ }
+
+ if(length $val){
+ return "\$$PREFIX-$var: $val \$";
+ } else {
+ return "\$$PREFIX-$var\$";
+ }
+}
+
+package PREFIX;
+use POSIX qw(strftime);
+
+# On push, put in the current date because we changed the file.
+# On pull, keep the current value so we can see the last change date.
+sub Date {
+ my($val, $mode, $submode) = @_;
+ if($mode eq "c"){
+ if($submode==0){
+ # we add this to make merge easier for now XXX
+ my $now = time; # not %s below - may not be portable
+ # YYYY/MM/DD HH:MM:SS
+ $val = "$now " . strftime("%Y/%m/%d %H:%M:%S", gmtime($now));
+ }
+ }
+ if($mode eq "s"){
+ $val =~ s/\s*$//; # XXX why do I need this?
+ }
+ return $val;
+}
+
+#sub Header {
+#}
+#sub Author {
+#}
+
+# NB: the standard-ish Revision line isn't enough - you need Branch/Revision -
+# but we split it into 2 so we can use the standard processing code on Revision
+# and just slip Branch in.
+sub Branch {
+ my($val, $mode, $submode) = @_;
+ if($mode eq "c"){
+ if($submode==0){
+ $val = `git branch --no-color --contains`;
+ chomp($val); #XXX
+ $val =~ s/^\*\s*//;
+ }
+ }
+ if($mode eq "s"){
+#XXX do we need this now?
+ $val =~ s/\s*$//; # XXX why do I need this?
+ }
+ return $val;
+}
+
+sub Revision {
+ my($val, $mode, $submode) = @_;
+ if($mode eq "c"){
+ if($submode==0){
+ my $file = $ARGV[1];
+ my @val = `git log --follow --oneline $file`;
+ $val = sprintf("1.%d",0+$#val);
+ }
+ }
+ if($mode eq "s"){
+#XXX do we need this here?
+ $val =~ s/\s*$//; # XXX why do I need this?
+ }
+ return $val;
+}
+
+__END__
--- /dev/null
+.git/hooks
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::saveSTDIN;
+&NHgithook::PRE;
+&NHgithook::resetSTDIN;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::saveSTDIN;
+&NHgithook::PRE;
+&NHgithook::resetSTDIN;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+#STARTUP-START
+BEGIN {
+ # OS hackery has to be duplicated in each of the hooks :/
+ # first the directory separator
+ my $DS = quotemeta('/');
+ my $PDS = '/';
+ # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+ # temporarily removed because inconsistent behavior
+ # if ($^O eq "msys")
+ # {
+ # $/ = "\r\n";
+ # $\ = "\r\n";
+ # }
+ if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $PDS = '\\';
+ }
+ $gitdir = `git rev-parse --git-dir`;
+ chomp $gitdir;
+ push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+#STARTUP-END
+
+&NHgithook::PRE;
+&NHgithook::POST;
+exit 0;
--- /dev/null
+#!/usr/bin/perl
+# $NHDT-Date$
+
+# value of nethack.setupversion we will end up with when this is done
+# version 1 is reserved for repos checked out before versioning was added
+my $version_new = 2;
+my $version_old = 0; # current version, if any (0 is no entry ergo new repo)
+
+use Cwd;
+use Getopt::Std;
+
+# Activestate Perl doesn't include File::Spec. Grr.
+BEGIN {
+ eval "require File::Spec::Functions";
+ if($@){
+ die <<E_O_M;
+File::Spec not found. (If you are running ActiveState Perl please run:
+ cpan File::Spec
+and re-run this program.
+E_O_M
+ }
+ File::Spec::Functions->import;
+}
+
+exit 1 unless(getopts('nvf')); # TODO: this can probably have better output
+
+# OS hackery
+my $DS = quotemeta('/'); # Directory Separator (for regex)
+my $DSP = '/'; # ... for printing
+# Temporarily disabled; there's something weird about msys
+# msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+#if($^O eq "msys"){
+# $/ = "\r\n";
+# $\ = "\r\n";
+# # NB: We don't need to do anything about File::Spec. It doesn't know
+# # about msys but it defaults to Unix, so we'll be ok.
+#}
+if($^O eq "MSWin32"){
+ $DS = quotemeta('\\');
+ $DSP = '\\';
+}
+
+# make sure we're at the top level of a repo
+if(! -d ".git"){
+ die "This is not the top level of a git repository.\n";
+}
+
+my $vtemp = `git config --local --get nethack.setupversion`;
+chomp($vtemp);
+if($vtemp > 0){
+ $version_old = 0+$vtemp;
+ if($version_old != $version_new){
+ print STDERR "Migrating from setup version $version_old to $version_new\n" if($opt_v);
+ }
+}
+# legacy check:
+if(length $vtemp == 0){
+ if(`git config --get merge.NHsubst.name` =~ m/^Net/){
+ $version_old = 1;
+ print STDERR "Migrating to setup version 1\n" if($opt_v);
+ }
+}
+
+my $gitadddir = `git config --get nethack.gitadddir`;
+chomp($gitadddir);
+if(length $gitadddir){
+ if(! -d $gitadddir){
+ die "nethack.gitadddir has invalid value '$gitadddir'\n";
+ }
+}
+print STDERR "nethack.gitadddir=$gitadddir\n" if($opt_v);
+
+# This is (relatively) safe because we know we're at R in R/DEVEL/nhgitset.pl
+my $srcdir = ($0 =~ m!^(.*)$DS!)[0];
+
+if(! -f catfile($srcdir, 'nhgitset.pl')){
+ die "I can't find myself in '$srcdir'\n";
+}
+
+print STDERR "Copying from: $srcdir\n" if($opt_v);
+
+if($opt_f || $version_old==0){
+ print STDERR "Configuring line endings\n" if($opt_v);
+ unlink catfile('.git','index') unless($opt_n);
+ system("git reset") unless($opt_n);
+ system("git config --local core.safecrlf true") unless($opt_n);
+ system("git config --local core.autocrlf false") unless($opt_n);
+} elsif($version_old <2){
+ my $xx = `git config --get --local core.safecrlf`;
+ if($xx !~ m/true/){
+ print STDERR "\nNeed to 'rm .git${DSP}index;git reset'.\n";
+ print STDERR " When ready to proceed, re-run with -f flag.\n";
+ exit 2;
+ }
+}
+
+
+
+print STDERR "Installing aliases\n" if($opt_v);
+$addpath = catfile(curdir(),'.git','hooks','NHadd');
+&add_alias('nhadd', "!$addpath add");
+&add_alias('nhcommit', "!$addpath commit");
+
+print STDERR "Installing filter/merge\n" if($opt_v);
+
+if($^O eq "MSWin32"){
+ $cmd = '.git\\\\hooks\\\\NHtext';
+} else {
+ $cmd = catfile(curdir(),'.git','hooks','NHtext');
+}
+&add_config('filter.NHtext.clean', "$cmd --clean %f");
+&add_config('filter.NHtext.smudge', "$cmd --smudge %f");
+
+$cmd = catfile(curdir(),'.git','hooks','NHsubst');
+&add_config('merge.NHsubst.name', 'NetHack Keyword Substitution');
+&add_config('merge.NHsubst.driver', "$cmd %O %A %B %L");
+
+print STDERR "Running directories\n" if($opt_v);
+
+foreach my $dir ( glob("$srcdir$DS*") ){
+ next unless(-d $dir);
+
+ my $target = catfile($dir, 'TARGET');
+ next unless(-f $target);
+
+ open TARGET, '<', $target or die "$target: $!";
+ my $targetpath = <TARGET>;
+ # still have to eat all these line endings under msys, so instead of chomp use this:
+ $targetpath =~ s![\r\n]!!g;
+ close TARGET;
+ print STDERR "Directory $dir -> $targetpath\n" if($opt_v);
+
+ my $enddir = $dir;
+ $enddir =~ s!.*$DS!!;
+ if(! &process_override($enddir, "INSTEAD")){
+ &process_override($enddir, "PRE");
+ my $fnname = "do_dir_$enddir";
+ if(defined &$fnname){
+ &$fnname($dir, $targetpath);
+ }
+ &process_override($enddir, "POST");
+ }
+}
+
+&check_prefix; # for variable substitution
+
+if($version_old != $version_new){
+ print STDERR "Setting version to $version_new\n" if($opt_v);
+ if(! $opt_n){
+ system("git config nethack.setupversion $version_new");
+ if($?){
+ die "Can't set nethack.setupversion $version_new: $?,$!\n";
+ }
+ }
+}
+
+exit 0;
+
+sub process_override {
+ my($srcdir, $plname) = @_;
+ return 0 unless(length $gitadddir);
+
+ my $plpath = catfile($gitadddir, $srcdir, $plname);
+#print STDERR " ",catfile($srcdir, $plname),"\n"; # save this for updating docs - list of overrides
+ return 0 unless(-x $plpath);
+
+ print STDERR "Running $plpath\n" if($opt_v);
+ # current directory is top of target repo
+
+ unless($opt_n){
+ system("$plpath $opt_v") and die "Callout $plpath failed: $?\n";
+ }
+ return 1;
+}
+
+sub add_alias {
+ my($name, $def) = @_;
+ &add_config("alias.$name",$def);
+}
+
+sub add_config {
+ my($name, $val) = @_;
+ system('git', 'config', '--local', $name, $val) unless($opt_n);
+}
+
+sub check_prefix {
+ my $lcl = `git config --local --get nethack.substprefix`;
+ chomp($lcl);
+ if(0==length $lcl){
+ my $other = `git config --get nethack.substprefix`;
+ chomp($other);
+ if(0==length $other){
+ print STDERR "ERROR: nethack.substprefix is not set anywhere. Set it and re-run.\n";
+ exit 2;
+ } else {
+ &add_config('nethack.substprefix', $other);
+ print STDERR "Copying prefix '$other' to local repository.\n" if($opt_v);
+ }
+ $lcl = $other; # for display below
+ }
+ print "\n\nUsing prefix '$lcl' - PLEASE MAKE SURE THIS IS CORRECT\n\n";
+}
+
+sub do_dir_DOTGIT {
+if(1){
+ # We are NOT going to mess with config now.
+ return;
+} else {
+ my($srcdir, $targetdir) = @_;
+#warn "do_dir_DOTGIT($srcdir, $targetdir)\n";
+ my $cname = "$srcdir/config";
+ if(-e $cname){
+ print STDERR "Appending to .git/config\n" if($opt_v);
+ open CONFIG, ">>.git/config" or die "open .git/config: $!";
+ open IN, "<", $cname or die "open $cname: $!";
+ my @data = <IN>;
+ print CONFIG @data;
+ close IN;
+ close CONFIG;
+ } else {
+ print STDERR " Nothing to add to .git/config\n" if($opt_v);
+ }
+# XXX are there other files in .git that we might want to handle?
+# So just in case:
+ for my $file ( glob("$srcdir/*") ){
+ next if( $file =~ m!.*/TARGET$! );
+ next if( $file =~ m!.*/config$! );
+ die "ERROR: no handler for $file\n";
+ }
+}
+}
+
+sub do_dir_hooksdir {
+ my($srcdir, $targetdir) = @_;
+
+ for my $path ( glob("$srcdir$DS*") ){
+
+ next if( $path =~ m!.*${DS}TARGET$! );
+
+ my $file = $path;
+
+ $file =~ s!.*$DS!!;
+
+ $file = catfile($targetdir, $file);
+
+ next if($opt_n);
+
+ open IN, "<", $path or die "Can't open $path: $!";
+ open OUT, ">", "$file" or die "Can't open $file: $!";
+ while(<IN>){
+ print OUT;
+ }
+ close OUT;
+ close IN;
+
+ if(! -x $file){
+ chmod 0755 ,$file;
+ }
+ }
+}
+
+__END__
+(can we change the .gitattributes syntax to include a comment character?)
+maybe [comment] attr.c:parse_attr_line
+grr - looks like # is the comment character
+
+
+
+=head1 NAME
+
+nhgitset.pl - Setup program for NetHack git repositories
+
+=head1 SYNOPSIS
+
+ cd THE_REPO
+ [git config nethack.gitadddir GITADDDIR]
+ perl SOME_PATH/DEVEL/nhgitset.pl [-v][-n][-f]
+
+=head1 DESCRIPTION
+
+nhgitset.pl installs NetHack-specific setup after a C<git clone> (or after
+changes to the desired configuration, which are installed by re-running
+nhgitset.pl).
+
+The follwing options are available:
+
+B<-f> Force. Do not use this unless the program requests it.
+
+B<-n> Make no changes.
+
+B<-v> Verbose output.
+
+=head1 CONFIG
+
+nhgitset.pl uses the following non-standard C<git config> variables:
+
+nethack.gitadddir
+
+ DOTGIT/INSTEAD
+ DOTGIT/PRE
+ DOTGIT/POST
+ hooksdir/INSTEAD
+ hooksdir/PRE
+ hooksdir/POST
+
+nethack.setupversion
+
+nethack.substprefix
+
+
+=head1 EXIT STATUS
+
+0 Success.
+
+1 Fail.
+
+2 Intervention required.