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