3 # groffer - display groff files
5 # Source file position: <groff-source>/contrib/groffer/subs.pl
6 # Installed position: <prefix>/lib/groff/groffer/subs.pl
8 # Copyright (C) 2006-2014 Free Software Foundation, Inc.
9 # Written by Bernd Warken <groff-bernd.warken-72@web.de>.
11 # This file is part of `groffer', which is part of `groff'.
13 # `groff' is free software; you can redistribute it and/or modify it
14 # under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 2 of the License, or
16 # (at your option) any later version.
18 # `groff' is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see
25 # <http://www.gnu.org/licenses/gpl-2.0.html>.
27 ########################################################################
32 ########################################################################
34 ########################################################################
39 # Decompress or cat the <file>.
41 # Return: the decompressed file as array
45 die "cat_z(): one argument is needed; you used $n;"
49 die "cat_z(): `$file' is not a readable file;" unless -f $file && -r $file;
50 return () if -z $file;
53 if ($main::Has_Compression) {
54 if ($main::Has_bzip) {
55 # test whether bz2 compressed, shell return must be inverted
56 unless ( system("bzip2 -t $file 2>$main::Dev_Null") ) {
57 @res = `bzip2 -c -d $file 2>$main::Dev_Null`;
60 # if not compressed with gz, gzip will act like `cat'
61 @res = `gzip -c -d -f $file 2>$main::Dev_Null`;
66 open $fh, "<$file" or die "cat_z(): could not open $file";
78 # Remove the temporary directory and restore the system.
82 chdir $main::Start_Dir;
83 if ($main::Debug{'KEEP'} && -d $main::tmpdir) {
84 my $glob = File::Spec->catfile($main::tmpdir, '*');
85 unlink glob($glob); # rm $tmpdir/*
94 # Split the path and return the directory name part
96 # Return: string of directory name
100 die "get_filename(): one argument is needed; you used $n;" unless $n == 1;
101 return '' unless $_[0];
103 my ($dirname, $filename) = &split_path($_[0]);
109 # get_filename(<path>)
111 # Split the path and return the file name part
113 # Return: string of file name
117 die "get_dirname(): one argument is needed; you used $n;" unless $n == 1;
118 return '' unless $_[0];
120 my ($dirname, $filename) = &split_path($_[0]);
128 # Test whether X Windows is running.
131 return 1 if $ENV{'DISPLAY'};
137 # list_has(<list_ref>, <string>)
139 # Determine if <list_ref> has <string> as element.
143 die "list_has(): 2 arguments are needed; you used $n;"
146 my $list_ref = $_[0];
148 die "list_has(): first argument must be an array reference;"
149 unless ref($list_ref) eq 'ARRAY';
151 foreach ( @$list_ref ) {
152 return 1 if $_ eq $string;
159 # path_uniq(<dir>...)
161 # make path having unique existing directories
167 next if exists $h{$_};
177 # print_hash(<hash_or_ref>)
179 # print the elements of a hash or hash reference
185 print "empty hash\n;";
188 if (ref($_[0]) eq 'HASH') {
191 warn 'print_hash(): the argument is not a hash or hash reference;';
196 warn 'print_hash(): the arguments are not a hash;';
206 print "empty hash\n";
209 print "hash (ignore the ^ characters):\n";
210 for my $k (sort keys %$hr) {
226 # print_times(<string>)
228 # print the time, result: user, system, child process user, child system
234 # print STDERR "$s: @t\n";
237 BEGIN { &print_times("start"); }
238 END { &print_times("end"); }
244 # Split the path into directory and file name parts
246 # Return: array with 2 elements consisting of directory and file name
250 die "split_path(): one argument is needed; you used $n;" unless $n == 1;
252 return () unless $arg;
254 my $basename = basename($arg);
255 if ( $basename ne $arg ) { # path with dir
256 # fileparse() is from File::Basename
257 my($filename, $dirname, $suffix) = fileparse($arg);
258 return ($dirname, $basename);
273 if ! system("echo -n '' | soelim -r 2>$main::Dev_Null >$main::Dev_Null");
276 # to_tmp (<filename>)
278 # Print file (decompressed) to the temporary cat file with handling .SO
283 die "to_tmp(): one argument is needed; you used $n;"
287 return 1 unless $arg;
288 die "to_tmp(): $arg is not an existing file;" unless -f $arg;
289 die "to_tmp(): could not read file $arg;" unless -r $arg;
292 $tmp_file_base = File::Spec->catfile($main::tmpdir, ',file')
293 unless $tmp_file_base;
294 $tmp_so_base = File::Spec->catfile($main::tmpdir, ',so')
297 open $main::fh_cat, ">>$main::tmp_cat" or
298 die "to_tmp(): could not open temporary cat file";
300 if ($main::Opt{'WHATIS'}) {
301 &whatis_filename($arg);
306 my $dir = &get_dirname($arg);
308 my ($fh_file, $tmp_file, $fh_tmp, $tmp_tmp);
310 $tmp_file = $tmp_file_base . $nr_file;
311 $tmp_tmp = File::Spec->catfile($main::tmpdir, ',tmp');
313 print STDERR "file: $arg\n" if $main::Debug{'FILENAMES'};
315 if ($main::Filespec_Is_Man) {
316 my ($fh_so, $tmp_so);
318 open $fh_file, ">$tmp_file" or
319 die "to_tmp(): could not open $tmp_file;";
320 foreach ( &cat_z($arg) ) {
325 open $fh_file, "<$tmp_file" or
326 die "to_tmp(): could not open $tmp_file;";
328 foreach (<$fh_file>) {
329 if (/^[\.']\s*so\s/) {
337 if ( @list && $main::Debug{'KEEP'} ) {
338 my $f = $tmp_file . '+man';
343 DO_MAN_SO: foreach (@list) {
344 # start of _do_man_so() in shell version
347 $soname =~ s/\\\s/ /g;
350 my $path = File::Spec->rootdir();
351 if ($soname =~ m#^$path#) { # absolute path name
352 next DO_MAN_SO if -f $soname;
353 foreach ('.gz', '.Z', '.bz2') {
354 my $name = $soname . $_;
360 next DO_MAN_SO unless $sofound;
361 } else { # relative to man path
362 LOOP: foreach my $ext ('', '.gz', '.Z', '.bz2') {
363 foreach my $p ( @{$main::Man{'PATH'}} ) {
364 my $f = File::Spec->catfile($p, "$soname$ext");
366 $sofound = $f if -f $f;
371 next DO_MAN_SO unless $sofound;
374 print STDERR "file from .so: $so\n" if $main::Debug{'FILENAMES'};
378 $tmp_so = $tmp_so_base . $nr_so;
379 unlink $tmp_so if -e $tmp_so;
380 open $fh_so, ">$tmp_so" or
381 die "to_tmp(): could not open $tmp_so;";
382 foreach ( &cat_z($sofound) ) {
389 open $fh_file, "<$tmp_file" or
390 die "to_tmp(): could not open $tmp_file;";
391 open $fh_tmp, ">$tmp_tmp" or
392 die "to_tmp(): could not open $tmp_tmp;";
393 foreach (<$fh_file>) {
394 s#^([\.'])\s*so\s+($so|$esc|$soname)\s*\n$#${1}so $tmp_so\n#s;
400 unlink $tmp_file if -e $tmp_file;
401 rename $tmp_tmp, $tmp_file;
402 # end of _do_man_so() in shell version
405 if ( @list && $main::Debug{'KEEP'} ) {
406 my $f = $tmp_file . '+tmp';
410 unlink $tmp_tmp if -e $tmp_tmp;
411 rename $tmp_file, $tmp_tmp;
412 system("soelim -I$dir $soelim_r $tmp_tmp >$tmp_file");
413 unlink $tmp_tmp if -e $tmp_tmp;
415 } else { # $Filespec_Is_Man is empty
416 open $fh_tmp, ">$tmp_tmp" or
417 die "to_tmp(): could not open $tmp_tmp;";
418 foreach (cat_z $arg) {
423 system("soelim -I$dir $soelim_r $tmp_tmp >$tmp_file");
425 system("soelim $soelim_r $tmp_tmp >$tmp_file");
428 } # if ($Filespec_Is_Man)
431 my $grog = `grog $tmp_file`;
432 die "to_tmp(): grog error on $tmp_file;" if $?;
434 print STDERR "grog output: $grog\n" if $main::Debug{'GROG'};
435 if ($grog =~ /^.*\s-m.*$/) {
437 $grog =~ s/ -m / -m/g;
438 $grog =~ s/ -mm([^ ]) / -m$1/g;
439 foreach my $g (split / /, $grog) {
441 my $ref = \@main::Macro_Packages;
442 if ( &list_has($ref, $g) ) {
443 if (! $main::Macro_Pkg) {
444 $main::Macro_Pkg = $g;
445 } elsif ($main::Macro_Pkg eq $g) {
447 } elsif ($main::Macro_Pkg =~ /^-m/) {
448 warn "to_tmp(): Ignore $arg because it needs $g " .
449 "instead of $main::Macro_Pkg";
450 unlink $tmp_file unless $main::Debug{'KEEP'};
452 } elsif ($main::Macro_Pkg ne $g) {
453 die "to_tmp(): \$Macro_Pkg does not start with -m: " .
455 } # if (! $main::Macro_Pkg)
461 open $fh_file, "<$tmp_file" or
462 die "to_tmp(): could not open $tmp_file for reading;";
463 open $main::fh_cat, ">>$main::tmp_cat" or
464 die "to_tmp(): could not open $main::tmp_cat for appending;";
465 foreach (<$fh_file>) {
466 print $main::fh_cat $_;
471 unless ( $main::Debug{'KEEP'} ) {
473 foreach ( glob("$tmp_so_base*") ) {
482 # to_tmp_line (<text>...)
484 # Print array of lines with <text> to the temporary cat file. \n is added
485 # if a line does not end with \n.
490 open $main::fh_cat, ">>$main::tmp_cat" or
491 die "to_tmp_line(): could not open temporary cat file";
495 print $main::fh_cat "$line\n";
505 # Print usage information for --help.
512 Usage: groffer [option]... [filespec]...
514 Display roff files, standard input, and/or Unix manual pages with an X
515 Window viewer or in several text modes. All input is decompressed
516 on-the-fly with all formats that gzip can handle.
519 "filename" name of a readable file
520 "-" for standard input
521 "man:name(n)" man page "name" in section "n"
522 "man:name.n" man page "name" in section "n"
523 "man:name" man page "name" in first section found
524 "name(n)" man page "name" in section "n"
525 "name.n" man page "name" in section "n"
526 "n name" man page "name" in section "n"
527 "name" man page "name" in first section found
528 where `section' is a single character out of [1-9on], optionally followed
529 by some more letters that are called the `extension'.
531 -h --help print this usage message.
532 -T --device=name pass to groff using output device "name".
533 -v --version print version information.
534 -V display the groff execution pipe instead of formatting.
535 -X display with "gxditview" using groff -X.
536 -Z --ditroff --intermediate-output
537 generate groff intermediate output without
538 post-processing and viewing, like groff -Z.
539 All other short options are interpreted as "groff" formatting options.
541 The most important groffer long options are
543 --apropos=name start man's "apropos" program for "name".
545 "apropos" for "name" in man's data sections 4, 5, 7.
547 "apropos" for "name" in development sections 2, 3, 9.
549 "apropos" for "name" in man's program sections 1, 6, 8.
550 --auto choose mode automatically from the default mode list.
551 --default reset all options to the default value.
552 --default-modes=mode1,mode2,...
553 set sequence of automatically tried modes.
554 --dvi display in a viewer for TeX device independent format.
555 --groff process like groff, disable viewing features.
556 --help display this helping output.
557 --html display in a web browser.
558 --man check file parameters first whether they are man pages.
559 --mode=auto|dvi|groff|html|pdf|ps|source|text|tty|utf8|www|x|X
561 --no-man disable man-page facility.
562 --no-special disable --all, --apropos*, and --whatis
563 --pager=program preset the paging program for tty mode.
564 --pdf display in a PDF viewer.
565 --ps display in a Postscript viewer.
566 --source output as roff source.
567 --text output in a text device without a pager.
568 --to-stdout output the content of the mode file without display.
569 --tty display with a pager on text terminal even when in X.
570 --utf8 unicode text mode
571 --viewer choose a viewer for the actual device mode
572 --whatis display the file name and description of man pages
573 --www same as --html.
574 --x --X display with "gxditview" using an X* device.
576 The usual X Windows toolkit options transformed into GNU long options:
577 --background=color, --bd=size, --bg=color, --bordercolor=color,
578 --borderwidth=size, --bw=size, --display=Xdisplay, --fg=color,
579 --fn=font, --font=font, --foreground=color, --geometry=geom, --iconic,
580 --resolution=dpi, --rv, --title=text, --xrm=resource
582 Long options of GNU "man":
583 --all, --ascii, --ditroff, --extension=suffix, --locale=language,
584 --local-file=name, --location, --manpath=dir1:dir2:...,
585 --sections=s1:s2:..., --systems=s1,s2,..., --where, ...
587 Development options that are not useful for normal usage:
588 --debug, --debug-filenames, --debug-grog, --debug-keep, --debug-params,
589 --debug-tmpdir, --do-nothing, --print=text
593 # The following mode-viewer options were replaced by a single --viewer
594 # option. The old options are only ignored:
595 # --dvi-viewer=prog choose the viewer program for dvi mode.
596 # --html-viewer=program
597 # choose the web browser for html mode.
598 # --pdf-viewer=prog choose the viewer program for pdf mode.
599 # --ps-viewer=prog choose the viewer program for ps mode.
600 # --tty-viewer=prog select a pager for tty mode; same as --pager.
601 # --www-viewer=prog same as --html-viewer
602 # --x-viewer=prog choose viewer program for x mode (X mode).
603 # --X-viewer=prog same as "--xviewer".
611 # Get version information from version.sh and print a text with this.
615 my $program_version = '';
616 my $last_update = '';
617 my $groff_version_preset = '';
619 die "$main::File_version_sh does not exist;"
620 unless -f "$main::File_version_sh";
622 open $fh, "<$main::File_version_sh";
625 if (/^\s*_PROGRAM_VERSION\s*=\s*['"]*([^'"]*)['"]*\s*;?\s*$/) {
626 $program_version = $1;
629 if (/^\s*_GROFF_VERSION_PRESET\s*=\s*['"]*([^'"]*)['"]*\s*;?\s*$/) {
630 # this setting of the groff version is only used before make is run,
631 # otherwise @VERSION@ will set it, see groffer.sh.
632 $groff_version_preset = $1;
638 if ($main::Groff_Version) {
639 $groff_version = $main::Groff_Version;
641 $groff_version = $groff_version_preset;
644 groffer $program_version is part of groff version $groff_version.
645 GNU groff and groffer come with ABSOLUTELY NO WARRANTY.
646 You may redistribute copies of groff and its subprograms
647 under the terms of the GNU General Public License.
653 # where_is_prog(<program>)
655 # Test whether <program> without its arguments exists or is a program
658 # Arguments : 1, <program> can have spaces and arguments.
659 # Return : a hash with `dir', `file', `fullname', `args' if
660 # argument exists or is a program in $PATH, empty hash else.
663 scalar @_ eq 1 or die "where_is_prog(): only one argument is allowed";
665 return () unless $p1;
670 return () unless $p1;
674 return () unless $noarg;
677 if ($p1 =~ /^.* -.*$/) {
679 $args =~ s#^$noarg ##;
681 $args = '' unless defined $args;
685 # test whether $noarg has directory, so it is not tested with $PATH
686 my ($dir, $name) = &split_path($noarg);
687 $result{'dir'} = $dir;
690 if (-f $noarg && -x $noarg) {
691 $result{'args'} = $args;
692 $result{'file'} = $name;
693 $result{'fullname'} = File::Spec->catfile($dir, $name);
697 if ($dir) { # $noarg has name with directory
698 # now $wip_noarg (with directory) is not an executable file
700 # test name with space
702 my @base = split(/ /, $name);
706 my $base = join(' ', @base);
707 my $file = File::Spec->catpath($dir, $base);
708 if (-f $file && -x $file) {
709 my $baseargs = join(' ', @baseargs);
712 $args = "$baseargs $args";
716 $result{'args'} = $args;
717 $result{'file'} = $base;
718 $result{'fullname'} = $file;
722 unshift(@baseargs, $n);
724 } # end of test name with space
728 # now $noarg doesn't have a directory part
734 # test path with $win_noarg as a whole, evt. with spaces
735 foreach my $d (@main::Path) {
736 my $file = File::Spec->catfile($d, $name);
737 if (-f $file && -x $file) {
738 $result{'args'} = $args;
739 $result{'fullname'} = $file;
740 ($result{'dir'}, $result{'file'}) = &split_path($file);
743 } # foreach (@main::Path)
747 my @base = split(/ /, $name);
751 my $base = join(' ', @base);
752 foreach my $d (@maon::Path) {
753 my $file = File::Spec->catfile($d, $base);
754 if (-f $file && -x $file) {
755 my $baseargs = join(' ', @baseargs);
757 $args = "$baseargs $args";
761 $result{'args'} = $args;
762 $result{'fullname'} = $file;
763 ($result{'dir'}, $result{'file'}) = &split_path($file);
766 } # foreach (@main::Path)
768 unshift(@baseargs, $n);
770 } # test $name on space
778 # stop for checking temp files, etc.
788 ########################################################################