3 # Copyright (c) March 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 # /usr/bin/catman - preformat man pages
29 # $FreeBSD: src/gnu/usr.bin/man/catman/catman.perl,v 1.14.2.5 2002/01/05 16:20:52 phantom Exp $
30 # $DragonFly: src/gnu/usr.bin/man/catman/Attic/catman.perl,v 1.2 2003/06/17 04:25:46 dillon Exp $
36 usage: catman [-f|-force] [-h|-help] [-L|-locale] [-p|-print] [-r|remove]
37 [-v|-verbose] [directories ...]
44 $force = 0; # force overwriting existing catpages
45 $verbose = 0; # more warnings
46 $print = 0; # show only, do nothing
47 $remove = 0; # unlink forgotten man/catpages
48 $locale = 0; # go through localized man directories only
50 # choose localized man directories suffix.
51 $local_suffix = $ENV{'LC_ALL'} || $ENV{'LC_CTYPE'} || $ENV{'LANG'};
53 # if no argument for directories given
54 @defaultmanpath = ( '/usr/share/man' );
56 $exit = 0; # exit code
57 $ext = ".gz"; # extension
63 $SIG{'TRAP'} = 'Exit';
64 $SIG{'QUIT'} = 'Exit';
65 $SIG{'TERM'} = 'Exit';
68 $ENV{'PATH'} = '/bin:/usr/bin';
72 unlink($tmp) if $tmp ne ""; # unlink if a filename
73 die "$0: die on signal SIG@_\n";
79 while($_ = $argv[0], /^-/) {
82 if (/^--?(f|force)$/) { $force = 1 }
83 elsif (/^--?(p|print)$/) { $print = 1 }
84 elsif (/^--?(r|remove)$/) { $remove = 1 }
85 elsif (/^--?(v|verbose)$/) { $verbose = 1 }
86 elsif (/^--?(L|locale)$/) { $locale = 1 }
89 warn "Localized man directory suffix is $local_suffix\n"
90 if $verbose && $locale;
92 return &absolute_path(@argv) if $#argv >= 0;
93 return @defaultmanpath if $#defaultmanpath >= 0;
95 warn "Missing directories\n"; &usage;
98 # make relative path to absolute path
100 local(@dirlist) = @_;
101 local($pwd, $dir, @a);
105 foreach $dir (@dirlist) {
107 chop($pwd = `pwd`) if (!$pwd || $pwd !~ /^\//);
108 push(@a, "$pwd/$dir");
117 # e.g.: //usr///home// -> /usr/home
121 $dir =~ s|/+|/|g; # delete double '/'
122 $dir =~ s|/$||; # delete '/' at end
123 $dir =~ s|/(\.\/)+|/|g; # delete ././././
125 $dir =~ s|/+|/|g; # delete double '/'
126 $dir =~ s|/$||; # delete '/' at end
127 $dir =~ s|/\.$||; # delete /. at end
128 return $dir if $dir ne "";
135 local($subdir, $catdir);
136 local($dev,$ino) = (stat($dir))[01];
139 if ($dir_visit{$dev,$ino}) {
140 warn "$dir already parsed: $dir_visit{$dev,$ino}\n";
143 $dir_visit{$dev,$ino} = $dir;
145 # Manpath, /usr/local/man or
146 # localized manpath /usr/local/man/{$LC_CTYPE|$LANG}
147 if (($dir =~ /man$/) ||
148 (($locale) && ($dir =~ /man/) && ($dir =~ $local_suffix))) {
149 warn "open manpath directory ``$dir''\n" if $verbose;
150 if (!opendir(DIR, $dir)) {
151 warn "opendir ``$dir'':$!\n"; $exit = 1; return 0;
154 warn "chdir to: $dir\n" if $verbose;
155 chdir($dir) || do { warn "$dir: $!\n"; $exit = 1; return 0 };
157 foreach $subdir (sort(readdir(DIR))) {
158 if ($subdir =~ /^man\w+$/) {
159 $subdir = "$dir/$subdir";
160 &catdir_create($subdir) && &parse_subdir($subdir);
165 # subdir, /usr/local/man/man1
166 } elsif ($dir =~ /man\w+$/) {
167 local($parentdir) = $dir;
168 $parentdir =~ s|/[^/]+$||;
169 warn "chdir to: $parentdir\n" if $verbose;
170 chdir($parentdir) || do {
171 warn "$parentdir: $!\n"; $exit = 1; return 0 };
173 &catdir_create($dir) && &parse_subdir($dir);
175 warn "Assume ``$dir'' is not a man directory.\n";
180 # create cat subdirectory if neccessary
181 # e.g.: man9 exist, but cat9 not
184 local($catdir) = $subdir;
186 $catdir = &man2cat($subdir);
189 if (!chmod(0755, $catdir)) {
190 warn "Cannot write $catdir, chmod: $!\n";
197 warn "mkdir ``$catdir''\n" if $verbose || $print;
199 unlink($catdir); # be paranoid
200 if (!mkdir($catdir, 0755)) {
201 warn "Cannot make $catdir: $!\n";
209 # I: /usr/share/man/man9
210 # O: /usr/share/man/cat9
214 $man =~ s/man(\w+)$/cat$1/;
220 local($file, $f, $catdir, $catdir_short, $mandir, $mandir_short);
221 local($mtime_man, $mtime_cat);
225 $catdir = &man2cat($mandir);
227 ($mandir_short = $mandir) =~ s|.*/(.*)|$1|;
228 ($catdir_short = $catdir) =~ s|.*/(.*)|$1|;
230 warn "open man directory: ``$mandir''\n" if $verbose;
231 if (!opendir(D, $mandir)) {
232 warn "opendir ``$mandir'': $!\n"; $exit = 1; return 0;
235 foreach $file (readdir(D)) {
236 # skip current and parent directory
237 next if $file eq "." || $file eq "..";
240 if ($file !~ /^[\w\-\+\[\.:]+\.\w+$/) {
241 &garbage("$mandir/$file", "Assume garbage")
242 unless -d "$mandir/$file";
246 if ($file !~ /\.gz$/) {
247 if (-e "$mandir/$file.gz") {
248 &garbage("$mandir/$file",
249 "Manpage unused, see compressed version");
252 warn "$mandir/$file is uncompressed\n" if $verbose;
258 if (!(($mtime_man = ((stat("$mandir_short/$file"))[9])) && -r _ && -f _)) {
260 warn "Cannot read file: ``$mandir/$file''\n";
262 if ($remove && -l "$mandir/$file") {
263 &garbage("$mandir/$file", "Assume wrong symlink");
267 warn "Ignore subsubdirectory: ``$mandir/$file''\n"
274 # Assume catpages always compressed
275 if (($mtime_cat = ((stat("$catdir_short/$cfile"))[9]))
277 if ($mtime_man > $mtime_cat || $force) {
278 &nroff("$mandir/$file", "$catdir/$cfile");
280 warn "up to date: $mandir/$file\n" if $verbose;
281 #print STDERR "." if $verbose;
284 &nroff("$mandir/$file", "$catdir/$cfile");
289 if (!opendir(D, $catdir)) {
290 warn "opendir ``$catdir'': $!\n"; return 0;
293 warn "open cat directory: ``$catdir''\n" if $verbose;
294 foreach $file (readdir(D)) {
295 next if $file =~ /^(\.|\.\.)$/; # skip current and parent directory
297 if ($file !~ /^[\w\-\+\[\.:]+\.\w+$/) {
298 &garbage("$catdir/$file", "Assume garbage")
299 unless -d "$catdir/$file";
303 if ($file !~ /\.gz$/ && $read{"$file.gz"}) {
304 &garbage("$catdir/$file",
305 "Catpage unused, see compressed version");
306 } elsif (!$read{$file}) {
307 # maybe a bug in man(1)
308 # if both manpage and catpage are uncompressed, man reformats
309 # the manpage and puts a compressed catpage to the
310 # already existing uncompressed catpage
311 ($f = $file) =~ s/\.gz$//;
313 # man page is uncompressed, catpage is compressed
315 &garbage("$catdir/$file", "Catpage without manpage");
322 local($file, @text) = @_;
324 warn "@text: ``$file''\n";
326 warn "unlink $file\n";
328 unlink($file) || warn "unlink $file: $!\n" ;
334 local($man,$cat) = @_;
335 local($nroff) = "nroff -T" . $dev_name . " -man | col";
336 local($dev, $ino) = (stat($man))[01];
339 if ($link{"$dev.$ino"}) {
340 warn "Link: $link{\"$dev.$ino\"} -> $cat\n" if $verbose || $print;
342 return if $print; # done
343 unlink($cat); # remove possible old link
345 unless (link($link{"$dev.$ino"}, $cat)) {
346 warn "Link $cat: $!\n";
351 $cat = "$cat$ext" if $cat !~ /$ext$/;
352 warn "Format: $man -> $cat\n" if $verbose || $print;
355 # man page is compressed
356 if ($man =~ /$ext$/) {
357 $nroff = "zcat $man | tbl | $nroff";
359 $nroff = "tbl $man | $nroff";
363 $tmp = "$cat.$tmp"; # for cleanup after signals
364 system("$nroff | gzip > $cat.tmp");
366 # assume a fatal signal to nroff
367 &Exit("INT to system() function") if ($? == 2);
369 rename("$cat.tmp", $cat);
374 # dev/ino from manpage, path from catpage
375 $link{"$dev.$ino"} = $cat;
378 # Set correct [gn]roff output device name ([ng]roff's "-T" option)
380 # Choose default output device name.
384 # Use "nroff -Tkoi8-r -man" to format russian manpages (if catman "-L"
385 # option specified only).
386 if ($local_suffix =~ '\.KOI8-R$') {
387 $dev_name = "koi8-r";
389 # Use "nroff -Tlatin1 -man" to format ISO 8859-1 manpages
390 elsif ($local_suffix =~ '\.ISO_?8859-15?$') {
391 $dev_name = "latin1";
394 warn "nroff output device name is $dev_name\n" if $verbose;
401 if (-e $dir && -d _ && -r _ && -x _) {
402 warn "``$dir'' is not writable for you,\n" .
403 "can only write to existing cat subdirs (if any)\n"
404 if ! -w _ && $verbose;
405 &parse_dir(&stripdir($dir));
407 warn "``$dir'' is not a directory or not read-/searchable for you\n";
412 # convert locale name to short notation (ru_RU.KOI8-R -> ru.KOI8-R)
413 sub short_locale_name {
416 $lname =~ s|_[A-Z][A-Z]||;
417 warn "short locale name is $lname\n" if $verbose && $locale;
424 warn "Don't start this program as root, use:\n" .
425 "echo $0 @ARGV | nice -5 su -m man\n" unless $>;
428 @argv = &parse(split(/[ :]/, join($", @ARGV)));
430 foreach $dir (@argv) {
432 if ($local_suffix ne "") {
433 &process_dir($dir.'/'.$local_suffix);
434 &process_dir($dir.'/'.&short_locale_name($local_suffix));