From 8ee2d5976e9e5843668f244793966f431f25fa0c Mon Sep 17 00:00:00 2001 From: keni Date: Tue, 31 Mar 2015 09:50:02 -0400 Subject: [PATCH] NHDT substitution version 2. Re-run nhgitset.pl to install. "perldoc DEVEL/hooksdir/nhsub" for details. General docs still to come. Quick notes: - "git nhsub" lets you apply substitutions to a file without involving any version control. - When doing nhadd/nhcommit, the working directory WILL reflect the results of the substitutions. Let's see what this breaks. --- .gitattributes | 4 +- DEVEL/.gitattributes | 6 +- DEVEL/hooksdir/NHadd | 9 +- DEVEL/hooksdir/nhsub | 376 ++++++++++++++++++++++++++++++++++ DEVEL/nhgitset.pl | 27 ++- dat/.gitattributes | 6 +- doc/.gitattributes | 8 +- sys/amiga/.gitattributes | 2 +- sys/mac/.gitattributes | 2 +- sys/msdos/.gitattributes | 8 +- sys/os2/.gitattributes | 2 +- sys/share/.gitattributes | 2 +- sys/unix/.gitattributes | 2 +- sys/unix/hints/.gitattributes | 2 +- sys/vms/.gitattributes | 2 +- sys/wince/.gitattributes | 6 +- sys/winnt/.gitattributes | 8 +- util/.gitattributes | 4 +- 18 files changed, 435 insertions(+), 41 deletions(-) mode change 100755 => 100644 DEVEL/hooksdir/NHadd create mode 100644 DEVEL/hooksdir/nhsub diff --git a/.gitattributes b/.gitattributes index 894a91c8e..9af92ddc6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ -*.[ch] filter=NHtext merge=NHsubst -*.sh filter=NHtext merge=NHsubst +*.[ch] NHSUBST +*.sh NHSUBST * text=auto *.hqx -text *.sln -text diff --git a/DEVEL/.gitattributes b/DEVEL/.gitattributes index 3e157372b..cc0b542f3 100644 --- a/DEVEL/.gitattributes +++ b/DEVEL/.gitattributes @@ -1,4 +1,4 @@ -Developer.txt filter=NHtext merge=NHsubst -nhgitset.pl filter=NHtext merge=NHsubst -hookdir/* filter=NHtext merge=NHsubst +Developer.txt NHSUBST +nhgitset.pl NHSUBST +hooksdir/* NHSUBST * text=auto diff --git a/DEVEL/hooksdir/NHadd b/DEVEL/hooksdir/NHadd old mode 100755 new mode 100644 index 55a86ee08..55138e738 --- a/DEVEL/hooksdir/NHadd +++ b/DEVEL/hooksdir/NHadd @@ -1,14 +1,19 @@ #!/usr/bin/perl # wrapper for nhadd and nhcommit aliases -# $NHDT-Date$ +# $NHDT-Date: 1427408239 2015/03/26 22:17:19 $ %ok = map { $_ => 1 } ('add', 'commit'); die "Bad subcommand '$ARGV[0]'" unless $ok{$ARGV[0]}; +# we won't fail on a failure, so just system() +$rv = system('.git/hooks/nhsub',"--$ARGV[0]",@ARGV[1..$#ARGV]); +if($rv){ + print "warning: nhsub failed: $rv $!\n"; +} + 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: $!"; diff --git a/DEVEL/hooksdir/nhsub b/DEVEL/hooksdir/nhsub new file mode 100644 index 000000000..b8cdd9f67 --- /dev/null +++ b/DEVEL/hooksdir/nhsub @@ -0,0 +1,376 @@ +#!/usr/bin/perl +# nhsub +# $NHDT-Date: 1427408239 2015/03/26 22:17:19 $ + +# Note: was originally called nhdate; the rename is not reflected in the code. + +use strict; +my %opt; #cmd v n f F (other single char, but we don't care) +my $mode; # a c d f (add, commit, date, date -f) + +#SO how do we know if a file has changed? +#(git status: git status --porcelain --ignored -- FILES. +#maybe + -z but it's a question of rename operations - probably doesn't +# matter, but need to experiment. + +# key: [dacf] first character of opt{cmd} (f if nhsub -f or add -f) +# first 2 chars of "git status --porcelain --ignored" +# (see "git help status" for table) +# No default. Undef means something unexpected happened. +my %codes = ( + 'f M'=>1, 'f D'=>1, # [MD] not updated + 'a M'=>0, 'a D'=>0, + 'd M'=>0, 'd D'=>0, + 'c M'=>0, 'c D'=>0, + +# M [ MD] updated in index + + 'dA '=>1, 'dAM'=>1, 'dAD'=>1, + 'aA '=>1, 'aAM'=>1, 'aAD'=>1, + 'cA '=>1, 'cAM'=>1, 'cAD'=>1, + 'fA '=>1, 'fAM'=>1, 'fAD'=>1, + # A [ MD] added to index + + 'dD '=>0, 'dDM'=>0, + 'aD '=>1, 'aDM'=>1, + 'cD '=>0, 'cDM'=>0, + 'fD '=>1, 'fDM'=>1, + # D [ M] deleted from index + +# R [ MD] renamed in index + +# C [ MD] copied in index + + 'aM '=>1, 'aA '=>1, 'aR '=>1, 'aC '=>1, + 'fM '=>1, 'fA '=>1, 'fR '=>1, 'fC '=>1, + # [MARC] index and work tree matches + + 'd M'=>1, 'dMM'=>1, 'dAM'=>1, 'dRM'=>1, 'dCM'=>1, + 'a M'=>1, 'aMM'=>1, 'aAM'=>1, 'aRM'=>1, 'aCM'=>1, + 'c M'=>1, 'cMM'=>1, 'cAM'=>1, 'cRM'=>1, 'cCM'=>1, + 'f M'=>1, 'fMM'=>1, 'fAM'=>1, 'fRM'=>1, 'fCM'=>1, + # [ MARC] M work tree changed since index + + 'd D'=>0, 'dMD'=>0, 'dAD'=>0, 'dRD'=>0, 'dCD'=>0, + 'a D'=>0, 'aMD'=>0, 'aAD'=>0, 'aRD'=>0, 'aCD'=>0, + 'c D'=>0, 'cMD'=>0, 'cAD'=>0, 'cRD'=>0, 'cCD'=>0, + 'f D'=>0, 'fMD'=>0, 'fAD'=>0, 'fRD'=>0, 'fCD'=>0, + # [ MARC] D deleted in work tree + + # ------------------------------------------------- + # DD unmerged, both deleted + # AU unmerged, added by us + # UD unmerged, deleted by them + # UA unmerged, added by them + # DU unmerged, deleted by us + # AA unmerged, both added + # UU unmerged, both modified + # ------------------------------------------------- + 'a??'=>1, 'f??'=>1, # ?? untracked + 'd??'=>0, 'c??'=>0, + + 'f!!'=>1, # !! ignored + 'a!!'=>0, 'd!!'=>0, 'c!!'=>0 +); + +# OS hackery +my $PDS = '/'; +if ($^O eq "MSWin32") +{ + $PDS = '\\'; +} + +# pick up the prefix for substitutions in this repo +my $PREFIX = &git_config('nethack','substprefix'); +print "PREFIX: '$PREFIX'\n" if($opt{v}); + +my @rawlist = &cmdparse(@ARGV); +push(@rawlist,'.') if($#rawlist == -1); + +while(@rawlist){ + my $raw = shift @rawlist; + if(-f $raw){ + &schedule_work($raw); + next; + } + if(-d $raw){ + if($raw =~ m!$PDS.git$!o){ + print "SKIP $raw\n" if($opt{v}>=2); + next; + } + opendir RDIR,$raw or die "Can't opendir: $raw"; + local($_); # needed until perl 5.11.2 + while($_ = readdir RDIR){ + next if(m/^\.\.?$/); + if(m/^\./ && $opt{f}){ + print " IGNORE-f: $raw$PDS$_\n" if($opt{v}>=2); + next; + } + push(@rawlist, $raw.$PDS.$_); + } + closedir RDIR; + } + # ignore other file types +} + +# XXX could batch things up - later + +sub schedule_work { + my($file) = @_; + print "CHECK: '$file'\n" if($opt{v}>=2); + local($_) = `git status --porcelain --ignored -- $file`; + my $key = $mode . join('',(m/^(.)(.)/)); + if(length $key == 1){ + # Hack. An unmodified, tracked file produces no output from + # git status. Treat as ignored. + $key .= '!!'; + } + $key =~ s/-/ /g; # for Keni's locally mod'ed git + if(!exists $codes{$key}){ + die "I'm lost.\nK='$key' F=$file\nST=$_"; + } + if($codes{$key}==0){ + if($opt{v}>=2){ + print " IGNORE: $_" if(length); + print " IGNORE: !! $file\n" if(!length); + } + return; + } + if($opt{F}){ + my $ign = `git check-ignore $file`; + if($ign !~ m/^\s*$/){ + print " IGNORE-F: $ign" if($opt{v}>=2); + return; + } + } +# FALLTHROUGH and continue +#print "ACCEPT TEST\n"; # XXXXXXXXXX TEST +#return; + + my $attr = `git check-attr NHSUBST -- $file`; + if($attr =~ m/NHSUBST:\s+(.*)/){ +# XXX this is a bug in git. What if the value of an attribute is the +# string "unset"? Sigh. + if(! $opt{F}){ + if($1 eq "unset" || $1 eq "unspecified"){ + print " NOATTR: $attr" if($opt{v}>=2); + return; + } + } + &process_file($file); + return; + } + die "Can't parse check-attr return: $attr\n"; +} + +sub process_file { + my($file) = @_; + print "DOFIL: $file\n" if($opt{v}>=1); + + # For speed we read in the entire file then do the substitutions. + local($_) = ''; + my $len; + open INFILE, "<", $file or die "Can't open $file: $!"; + while(1){ + # On at least some systems we only get 64K. + my $len = sysread(INFILE, $_, 999999, length($_)); + last if($len == 0); + die "read failed: $!" unless defined($len); + } + close INFILE; + + local $::current_file = $file; # used under handlevar + # $1 - var and value (including trailing space but not $) + # $2 - var + # $4 - value or undef +#s/\$$PREFIX-(([A-Za-z][A-Za-z0-9_]*)(: ([^\N{DOLLAR SIGN}]+))?)\$/&handlevar($2,$4)/eg; +my $count = s/\$$PREFIX-(([A-Za-z][A-Za-z0-9_]*)(: ([^\x24]+))?)\$/&handlevar($2,$4)/eg; +# XXX had o modifier, why? + return unless($count>0); + return if($opt{n}); + + my $ofile = $file . ".nht"; + open(TOUT, ">", $ofile) or die "Can't open $ofile"; + die "write failed: $!" unless defined syswrite(TOUT, $_); + close TOUT or die "Can't close $ofile"; + rename $ofile, $file or die "Can't rename $ofile to $file"; +} + +sub cmdparse { + my(@in) = @_; + + # What are we doing? + $opt{cmd} = 'date'; # really nhsub + if($in[0] eq '--add'){ + $opt{cmd} = 'add'; + shift @in; + } + if($in[0] eq '--commit'){ + $opt{cmd} = 'commit'; + shift @in; + } + +# add: -n -v +# commit: --dry-run -v +# nhsub: -n -v + while($in[0] =~ m/^-/){ + local($_) = $in[0]; + if($_ eq '--'){ + shift @in; + last; + } + if(m/^--/){ + if($opt{cmd} eq 'commit' && $_ eq '--dry-run'){ + $opt{'n'} = 1; + } + shift @in; + next; + } + if(m/^-(.*)/){ + foreach my $single ( split(//,$1) ){ + # don't do -v here from add/commit + if($single ne 'v'){ + $opt{$single}++; + } elsif($opt{cmd} eq 'date'){ + $opt{$single}++; + } + } + } + shift @in; + } + + ($mode) = ($opt{cmd} =~ m/^(.)/); + $mode = 'f' if($opt{cmd} eq 'date' && ($opt{f}||$opt{F})); + $mode = 'f' if($opt{cmd} eq 'add' && $opt{f}); + + return @in; # this is our file list +} + +sub git_config { + my($section, $var) = @_; + my $raw = `git config --local --get $section.$var`; + $raw =~ s/[\r\n]*$//g; + return $raw if(length $raw); + die "Missing config var: [$section] $var\n"; +} + +sub handlevar { + my($var, $val) = @_; +# print "HIT '$var' '$val'\n" if($debug2); + + my $subname = "PREFIX::$var"; + if(defined &$subname){ + no strict; + print " SUBIN: $var '$val'\n" if($opt{v}>=3); + $val =~ s/\s+$//; + $val = &$subname($val); + print " SUBOT: $var '$val'\n" if($opt{v}>=3); + } 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) = @_; + # 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)); + 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) = @_; + $val = `git symbolic-ref -q --short HEAD`; + $val =~ s/[\n\r]*$//; + $val =~ s/^\*\s*//; + $val = "(unknown)" unless($val =~ m/^[[:print:]]+$/); + return $val; +} + +sub Revision { + my($val) = @_; + my @val = `git log --follow --oneline $::current_file`; + my $ver = 0+$#val; + $ver = 0 if($ver < 0); + $val = "1.$ver"; + return $val; +} +__END__ + +=head1 NAME + +C - NetHack git command for substitution variables + +=head1 SYNOPSIS + +C + +=head1 DESCRIPTION + +C rewrites the specified files by doing variable substitution for +variables starting with the prefix specified in the repository's +C configuration variable. C is also invoked +internally from the implementation of the C and C +commands. + +The program re-writes those files listed on the command line; if the file +is actually a directory, the program recurses into that directory tree. +Not all files found are re-written; some are ignored and those with no +substitution variables are not re-written. Unless changed by the options, +files that have not changed are not affected. + +If no files are listed on the command line, the current directory is +checked as if specified as C<.>. +Files listed directly on the command line are always checked. +The C<.git> directory is never processed. + +The following command line options are available: + +=over + +=item C<-v[v[v]]> + +Verbose output; may be (usefully) specified up to 3 times. Not available +when invoked as part of C or C. + +=item C<-n> + +Do not write any files. + +=item C<-f> + +Force, version 1: +Perform substitution even if the file has not changed, +except no dot files are processed unless listed directly on the command line. +This prevents accidents with editor temprorary files while recursing. Note +that this overloads the C<-f> option of C and C. + +=item C<-F> + +Force, version 2: +Perform substitution even if the file has not changed, +even if the NHSUBST attribute is not set for the +file, and only if the file is not ignored by git. Not available +when invoked as part of C or C. + +=back diff --git a/DEVEL/nhgitset.pl b/DEVEL/nhgitset.pl index 183c73d69..08068484a 100755 --- a/DEVEL/nhgitset.pl +++ b/DEVEL/nhgitset.pl @@ -3,7 +3,7 @@ # 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_new = 3; my $version_old = 0; # current version, if any (0 is no entry ergo new repo) use Cwd; @@ -100,16 +100,29 @@ print STDERR "Installing aliases\n" if($opt_v); $addpath = catfile(curdir(),'.git','hooks','NHadd'); &add_alias('nhadd', "!$addpath add"); &add_alias('nhcommit', "!$addpath commit"); +my $nhsub = catfile(curdir(),'.git','hooks','nhsub'); +&add_alias('nhsub', "!$nhsub"); print STDERR "Installing filter/merge\n" if($opt_v); -if($^O eq "MSWin32"){ - $cmd = '.git\\\\hooks\\\\NHtext'; -} else { - $cmd = catfile(curdir(),'.git','hooks','NHtext'); +# XXXX need it in NHadd to find nhsub??? +# removed at version 3 +#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"); +if($version_old == 1 or $version_old == 2){ + print STDERR "Removing filter.NHtext\n" if($opt_v); + system('git','config','--unset','filter.NHtext.clean') unless($opt_n); + system('git','config','--unset','filter.NHtext.smudge') unless($opt_n); + system('git','config','--remove-section','filter.NHtext') unless($opt_n); + + print STDERR "Removing NHtext\n" if($opt_v); + unlink catfile(curdir(),'.git','hooks','NHtext') unless($opt_n); } -&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'); diff --git a/dat/.gitattributes b/dat/.gitattributes index 72aa05edd..b59108e49 100644 --- a/dat/.gitattributes +++ b/dat/.gitattributes @@ -1,3 +1,3 @@ -*.def filter=NHtext merge=NHsubst -*.des filter=NHtext merge=NHsubst -*.txt filter=NHtext merge=NHsubst +*.def NHSUBST +*.des NHSUBST +*.txt NHSUBST diff --git a/doc/.gitattributes b/doc/.gitattributes index 9ca881076..a9a06f4c8 100644 --- a/doc/.gitattributes +++ b/doc/.gitattributes @@ -1,5 +1,5 @@ -*.mn filter=NHtext merge=NHsubst -*.6 filter=NHtext merge=NHsubst -fixes.* filter=NHtext merge=NHsubst -window.doc filter=NHtext merge=NHsubst +*.mn NHSUBST +*.6 NHSUBST +fixes.* NHSUBST +window.doc NHSUBST diff --git a/sys/amiga/.gitattributes b/sys/amiga/.gitattributes index 7a28030a9..7735c64ea 100644 --- a/sys/amiga/.gitattributes +++ b/sys/amiga/.gitattributes @@ -1 +1 @@ -*.p filter=NHtext merge=NHsubst +*.p NHSUBST diff --git a/sys/mac/.gitattributes b/sys/mac/.gitattributes index 62b9832d7..8f8defc69 100644 --- a/sys/mac/.gitattributes +++ b/sys/mac/.gitattributes @@ -1 +1 @@ -NHDeflts filter=NHtext merge=NHsubst +NHDeflts NHSUBST diff --git a/sys/msdos/.gitattributes b/sys/msdos/.gitattributes index baef52e46..7e8941f2d 100644 --- a/sys/msdos/.gitattributes +++ b/sys/msdos/.gitattributes @@ -1,4 +1,4 @@ -*.BC filter=NHtext merge=NHsubst -*.bat filter=NHtext merge=NHsubst -Makefile.* filter=NHtext merge=NHsubst -Install.* filter=NHtext merge=NHsubst +*.BC NHSUBST +*.bat NHSUBST +Makefile.* NHSUBST +Install.* NHSUBST diff --git a/sys/os2/.gitattributes b/sys/os2/.gitattributes index 8cffb77ae..31e18fe84 100644 --- a/sys/os2/.gitattributes +++ b/sys/os2/.gitattributes @@ -1 +1 @@ -Makefile.* filter=NHtext merge=NHsubst +Makefile.* NHSUBST diff --git a/sys/share/.gitattributes b/sys/share/.gitattributes index 8cffb77ae..31e18fe84 100644 --- a/sys/share/.gitattributes +++ b/sys/share/.gitattributes @@ -1 +1 @@ -Makefile.* filter=NHtext merge=NHsubst +Makefile.* NHSUBST diff --git a/sys/unix/.gitattributes b/sys/unix/.gitattributes index 8cffb77ae..31e18fe84 100644 --- a/sys/unix/.gitattributes +++ b/sys/unix/.gitattributes @@ -1 +1 @@ -Makefile.* filter=NHtext merge=NHsubst +Makefile.* NHSUBST diff --git a/sys/unix/hints/.gitattributes b/sys/unix/hints/.gitattributes index a079959f1..db77844f7 100644 --- a/sys/unix/hints/.gitattributes +++ b/sys/unix/hints/.gitattributes @@ -1 +1 @@ -* filter=NHtext merge=NHsubst +* NHSUBST diff --git a/sys/vms/.gitattributes b/sys/vms/.gitattributes index 8cffb77ae..31e18fe84 100644 --- a/sys/vms/.gitattributes +++ b/sys/vms/.gitattributes @@ -1 +1 @@ -Makefile.* filter=NHtext merge=NHsubst +Makefile.* NHSUBST diff --git a/sys/wince/.gitattributes b/sys/wince/.gitattributes index e034edc5e..fbe75bcd4 100644 --- a/sys/wince/.gitattributes +++ b/sys/wince/.gitattributes @@ -1,3 +1,3 @@ -*.ce filter=NHtext merge=NHsubst -*.mak filter=NHtext merge=NHsubst -*.bat filter=NHtext merge=NHsubst +*.ce NHSUBST +*.mak NHSUBST +*.bat NHSUBST diff --git a/sys/winnt/.gitattributes b/sys/winnt/.gitattributes index 2a38b029c..88f970c96 100644 --- a/sys/winnt/.gitattributes +++ b/sys/winnt/.gitattributes @@ -1,4 +1,4 @@ -Install.nt filter=NHtext merge=NHsubst -Makefile.* filter=NHtext merge=NHsubst -*.rc filter=NHtext merge=NHsubst -*.bat filter=NHtext merge=NHsubst +Install.nt NHSUBST +Makefile.* NHSUBST +*.rc NHSUBST +*.bat NHSUBST diff --git a/util/.gitattributes b/util/.gitattributes index 15e23d268..8b2657e61 100644 --- a/util/.gitattributes +++ b/util/.gitattributes @@ -1,2 +1,2 @@ -*.pl filter=NHtext merge=NHsubst -*.[ly] filter=NHtext merge=NHsubst +*.pl NHSUBST +*.[ly] NHSUBST