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.
This commit is contained in:
keni
2015-03-31 09:50:02 -04:00
parent cbd1657976
commit 8ee2d5976e
18 changed files with 435 additions and 41 deletions

4
.gitattributes vendored
View File

@@ -1,5 +1,5 @@
*.[ch] filter=NHtext merge=NHsubst
*.sh filter=NHtext merge=NHsubst
*.[ch] NHSUBST
*.sh NHSUBST
* text=auto
*.hqx -text
*.sln -text

View File

@@ -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

9
DEVEL/hooksdir/NHadd Executable file → Normal file
View File

@@ -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: $!";

376
DEVEL/hooksdir/nhsub Normal file
View File

@@ -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<nhsub> - NetHack git command for substitution variables
=head1 SYNOPSIS
C<git nhsub [-v[v[v]] [-n] [-f|-F] [--] [file...]>
=head1 DESCRIPTION
C<nhsub> rewrites the specified files by doing variable substitution for
variables starting with the prefix specified in the repository's
C<nethack.substprefix> configuration variable. C<nhsub> is also invoked
internally from the implementation of the C<nhadd> and C<nhcommit>
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<nhadd> or C<nhcommit>.
=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<git add> and C<git commit>.
=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<nhadd> or C<nhcommit>.
=back

View File

@@ -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');

6
dat/.gitattributes vendored
View File

@@ -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

8
doc/.gitattributes vendored
View File

@@ -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

View File

@@ -1 +1 @@
*.p filter=NHtext merge=NHsubst
*.p NHSUBST

View File

@@ -1 +1 @@
NHDeflts filter=NHtext merge=NHsubst
NHDeflts NHSUBST

View File

@@ -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

View File

@@ -1 +1 @@
Makefile.* filter=NHtext merge=NHsubst
Makefile.* NHSUBST

View File

@@ -1 +1 @@
Makefile.* filter=NHtext merge=NHsubst
Makefile.* NHSUBST

View File

@@ -1 +1 @@
Makefile.* filter=NHtext merge=NHsubst
Makefile.* NHSUBST

View File

@@ -1 +1 @@
* filter=NHtext merge=NHsubst
* NHSUBST

View File

@@ -1 +1 @@
Makefile.* filter=NHtext merge=NHsubst
Makefile.* NHSUBST

View File

@@ -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

View File

@@ -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

4
util/.gitattributes vendored
View File

@@ -1,2 +1,2 @@
*.pl filter=NHtext merge=NHsubst
*.[ly] filter=NHtext merge=NHsubst
*.pl NHSUBST
*.[ly] NHSUBST