1#!/usr/local/bin/perl -- -*-perl-*-
2
3# helpfiles:  make help files for Z-shell builtins from the manual entries.
4
5# Create help files for zsh commands for the manpage file of zshbuildins.1
6# passed as the first arg.
7# The second arg is the directory in which the help files will be created.
8# Assumes no other files are present.
9# No overwriting check;  `.' becomes `dot', `:' becomes `colon'.
10
11# Any command claiming to be `same as <foo>' or `equivalent to <foo>'
12# has its help file appended to the end of <foo>'s and replaced by a
13# link to <foo>.  (Arguably the help file should be put at the start
14# instead.)
15
16# If a third arg is given, the symlink is not created, but a
17# list of symlinks is put into the file specified by that arg.
18
19# Example usage:
20#    cd ~/zsh-4.0.1				# or wherever
21#    mkdir Help
22#    cd Help
23#    man zshbuiltins | colcrt - | helpfiles
24#    run-help() {
25#      typeset zhelp=~/zsh-4.0.1/Help		# or wherever
26#      [[ $1 = . ]] && 1=dot
27#      [[ $1 = : ]] && 1=colon
28#      if [[ $1 = compctl ]]; then
29#         man zshcompctl
30#      elif [[ -f $zhelp/$1 ]]; then
31#    	  ${=PAGER:-more} $zhelp/$1
32#      else
33#    	  man $1
34#      fi
35#    }
36# now <Esc>-h works for shell builtins.
37
38sub Usage {
39    print(STDERR "Usage: helpfiles zshbuiltins.1 dest-dir [link-file]\n");
40    exit(1);
41}
42
43sub Info {
44    print('helpfiles: ', @_, "\n");
45}
46
47sub Die {
48    print(STDERR 'helpfiles: ', @_, "\n");
49    exit(1);
50}
51
52&Usage() unless(@ARGV);
53$manfile = shift(@ARGV);
54&Usage() unless(@ARGV);
55$destdir = shift(@ARGV);
56$linkfile = ((@ARGV) ? shift(@ARGV) : '');
57unless(-d $destdir) {
58    mkdir($destdir) || &Die("$destdir is not a directory and cannot be created");
59}
60
61foreach (keys %ENV) {
62	delete($ENV{$_}) if(/^((LC_)|(LESS)|(MAN))/);
63}
64$ENV{'LANG'} = 'C';
65$ENV{'MANWIDTH'} = '80';
66$ENV{'GROFF_NO_SGR'} = ''; # We need "classical" formatting of man pages.
67
68$mantmp = $destdir . '/man.tmp';
69$coltmpbase = 'col.tmp';
70$coltmp = $destdir . '/' . $coltmpbase;
71$args = "$manfile >$mantmp";
72unlink($mantmp);
73&Info('attempting man ', $args);
74if(system('man ' . $args) || !(-s $mantmp)) {
75    unlink($mantmp);
76    &Info('attempting nroff -man ', $args);
77    if(system('nroff -man ' . $args) || !(-s $mantmp)) {
78        unlink($mantmp);
79        &Die('man and nroff -man both failed for ', $manfile);
80    }
81}
82$args = "$mantmp >$coltmp";
83unlink($coltmp);
84&Info('attempting colcrt ', $args);
85if(system('colcrt ' . $args) || !(-s $coltmp)) {
86    unlink($coltmp);
87    &Info('attempting col -bx <', $args);
88# The x is necessary so that spaces don't turn into tabs, which messes
89# up the calculations of indentation on machines which randomly wrap lines
90# round to the previous line (so you see what we're up against).
91    if(system('col -bx <' . $args) || !(-s $coltmp)) {
92        unlink($mantmp);
93        unlink($coltmp);
94        &Die('colcrt and col -bx both failed');
95    }
96}
97unlink($mantmp) || &Die('cannot remove tempfile ', $mantmp);
98
99unless(open(MANPAGE, '<', $coltmp)) {
100    unlink($coltmp);
101    &Die('generated tmpfile cannot be read');
102}
103
104unless($linkfile eq '') {
105    open(LINKFILE, '>', $linkfile) || &Die("cannot open $linkfile for writing")
106}
107
108chdir($destdir) || &Die("cannot cd to $destdir");
109
110while (<MANPAGE>) {
111    last if /^\s*SHELL BUILTIN COMMANDS/;
112    /zshbuiltins/ && $zb++;
113    last if ($zb && /^\s*DESCRIPTIONS/);
114}
115
116$print = 0;
117
118sub namesub {
119    local($cmd) = shift;
120    if ($cmd =~ /^\w*$/ && lc($cmd) eq $cmd) {
121	$cmd;
122    } elsif ($cmd eq '.') {
123	'dot';
124    } elsif ($cmd eq ':') {
125	'colon';
126    } else {
127	undef;
128    }
129}
130
131sub getsame {
132    local($_) = shift;
133    if (/same\s*as\s*(\S+)/i || /equivalent\s*to\s*(\S+)/i) {
134	local($name) = $1;
135	($name =~ /[.,]$/) && chop($name);
136	return $name;
137    } else {
138	return undef;
139    }
140}
141
142sub newcmd {
143    local($_) = shift;
144    local($cmd);
145    # new command
146    if (defined($cmd = &namesub($_))) {
147	# in case there's something nasty here like a link..
148	unlink $cmd;
149	open (OUT, ">$cmd");
150	select OUT;
151	$print = 1;
152    } else {
153	$print = 0;
154    }
155}
156
157sub doprint {
158    local($_) = shift;
159
160    s/^$indentstr//o;		# won't work if too many tabs
161    print;
162}
163
164while (<MANPAGE>) { last unless /^\s*$/; }
165
166/^(\s+)(\S+)/;
167$indentstr = $1;
168$indent = length($1);
169&newcmd($2);
170print if $print;
171
172BUILTINS: while (<MANPAGE>) {
173    next if /^\w/;
174
175    undef($undented);
176    if (/^\s*$/ || ($undented = (/^(\s*)/  && length($1) < $indent))) {
177	$undented && &doprint($_);
178	while (defined($_ = <MANPAGE>) && /(^\w)|(^\s*$)/) {
179	    # NAME is the start of the next section when in zshall.
180	    # (Historical note: we used to exit on the page header,
181	    # but text from the old section can continue to the
182	    # new page).
183	    last BUILTINS if /^\s*NAME\s*$/;
184	    last BUILTINS if /^STARTUP\/SHUTDOWN FILES/;
185	    last if /^zsh.*\s\d$/; # GNU nroff -man end-of-page marker
186	}
187        if (/^\s*Page/ || /^zsh.*\s\d$/) {
188	    do {
189		$_ = <MANPAGE>;
190	    } while (defined($_) && /^\s*$/);
191	    if (/^\s*ZSHBUILTINS/) {
192		do {
193		    $_ = <MANPAGE>;
194		} while (defined($_) && /^\s*$/);
195	    }
196	}
197	if (/^(\s*)/ && length($1) < $indent) {
198	    # This may be just a bug on the SGI, or maybe something
199	    # more sinister (don\'t laugh, this is nroff).
200	    s/^\s*/ /;
201	    $defer = $_;
202	    do {
203		$_ = <MANPAGE>;
204	    } while (defined($_) && /^\s*$/);
205	    last unless defined($_);
206	}
207	if (/^(\s+)(\S+)/ && length($1) == $indent) {
208	    &newcmd($2);
209	}  else {
210	    print "\n";
211	}
212        if ($print) {
213	    if (defined($defer)) {
214		chop;
215		&doprint("$_$defer");
216		undef($defer);
217	    } else {
218		&doprint($_);
219	    }
220	}
221    } else {
222	&doprint($_) if $print;
223    }
224}
225
226select STDOUT;
227close OUT;
228close(MANPAGE);
229unlink($coltmpbase) || &Die('cannot remove tempfile ', $coltmpbase);
230
231foreach $file (<*>) {
232    open (IN, $file);
233    if ($sameas = (&getsame($_ = <IN>) || &getsame($_ = <IN>))) {
234	defined($sameas = &namesub($sameas)) || next;
235#	print "$file is the same as $sameas\n";
236	seek (IN, 0, 0);
237
238	# Copy this to base builtin.
239	open (OUT, ">>$sameas");
240	select OUT;
241	print "\n";
242	while (<IN>) { print; }
243	close IN;
244	select STDOUT;
245	close OUT;
246
247	# Make this a link to that.
248	unlink $file;
249	if($linkfile eq '') {
250	    symlink ($sameas, $file);
251	} else {
252	    print(LINKFILE "$sameas $file\n");
253	}
254    }
255}
256close(LINKFILE) unless($linkfile eq '');
257
258# Make one sanity check
259&Die('not all files were properly generated') unless(-r 'ztcp');
260
261__END__
262