#!/usr/bin/perl -w # # BuildLinuxMan.pl : Build Linux manpages book # Deri James (& Brian Inglis) : 15 Dec 2022 # # Params:- # # $1 = Directory holding the man pages # # (C) Copyright 2022, Deri James # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details # (http://www.gnu.org/licenses/gpl-2.0.html). # use strict; use File::Basename; my $inTS=0; my $inBlock=0; my %Sections= ( "1" => "General Commands Manual", "2" => "System Calls Manual", "2type" => "System Calls Manual (types)", "3" => "Library Functions Manual", "3const" => "Library Functions Manual (constants)", "3head" => "Library Functions Manual (headers)", "3type" => "Library Functions Manual (types)", "4" => "Kernel Interfaces Manual", "5" => "File Formats Manual", "6" => "Games Manual", "7" => "Miscellaneous Information Manual", "8" => "System Manager's Manual", "9" => "Kernel Developer's Manual", ); my $dir=shift || '.'; my $dir2=$dir; $dir2=~tr[.][_]; my %files; my %aliases; foreach my $al (`find "$dir"/man*/ -type f \\ | grep "\\.[[:digit:]]\\([[:alpha:]][[:alnum:]]*\\)\\?\\>\$" \\ | xargs grep '^\\.so' /dev/null;`) { #$al=~tr[.][_]; $al=~m/^$dir\/man\d[a-z]*\/(.*):\.\s*so\s*man\d[a-z]*\/(.*)/o; $aliases{$1}=$2; } while (my ($k,$v)=each %aliases) { while (exists($aliases{$v})) { $v=$aliases{$v}; } } foreach my $fn (`find "$dir"/man*/ -type f \\ | grep "\\.[[:digit:]]\\([[:alpha:]][[:alnum:]]*\\)\\?\\>\$";`) { $fn=~s/\n//; my ($nm,$sec)=GetNmSec($fn,qr/\.\d[a-z]*/); $files{"${nm}.$sec"}=[$fn,(exists($aliases{"${nm}.$sec"}))?$aliases{"${nm}.$sec"}:"${nm}.$sec"]; } my $Section=''; BuildBook(); sub BuildBook { print ".pdfpagenumbering D . 1\n.nr PDFOUTLINE.FOLDLEVEL 0\n.defcolor pdf:href.colour rgb 0.00 0.25 0.75\n.pdfinfo /Title \"The shadow man-pages Book\"\n.special TinosR UnifontR S\n"; foreach my $bkmark (sort sortman keys %files) { BuildPage($bkmark); } } sub BuildPage { my $bkmark=shift; my $fn=$files{$bkmark}->[0]; my ($nm,$sec,$srt)=GetNmSec($bkmark,qr/\.[\da-z]+/); my $title= "$nm\\($sec\\)"; print ".\\\" >>>>>> $nm($sec) <<<<<<\n.lf 0 $bkmark\n"; # If this is an alias, just add it to the outline panel. # if new section add top level bookmark if ($sec ne $Section) { print ".nr PDFOUTLINE.FOLDLEVEL 1\n"; print ".pdfbookmark 1 $Sections{$sec}\n"; print ".nr PDFOUTLINE.FOLDLEVEL 2\n"; $Section=$sec; } if (exists($aliases{$bkmark})) { print ".eo\n.device ps:exec [/Dest /$aliases{$bkmark} /Title ($title) /Level 2 /OUT pdfmark\n.ec\n.fl\n"; return; } if (open(F,'<',$fn)) { while () { if (m/^\.\\"/) { print $_; next; } chomp; # This code is to determine whether we are within a tbl block and in a text block # T{ and T}. This is fudge code particularly for the syscalls(7) page. $inTS=1 if m/\.TS/; $inTS=0,$inBlock=0 if m/\.TE/; next if !$_; # s/^\s+//; s/\\-/-/g if /^\.[BM]R\s+/; if (m/^\.BR\s+([-\w\\.]+)\s+\((.+?)\)(.*)/ or m/^\.MR\s+([-\w\\.]+)\s+(\w+)\s+(.*)/ or m/^\\fB([-\w\\.]+)\\fR\((.+?)\)(.*)$/) { my $bkmark="$1"; my $sec=$2; my $after=$3; $after=~s/\s\\".*//; my $dest=$bkmark; $dest=~s/\\-/-/g; if (exists($files{"${bkmark}.$sec"})) { my $dest=$files{"${bkmark}.$sec"}->[1]; $_=".pdfhref L -D \"$dest\" -A \"$after\" -- \\fI$bkmark\\fP($sec)"; } else { $_=".IR $bkmark ($sec)\\c\n$after"; } } s/^\.BI \\fB/.BI /; s/^\.BR\s+(\S+)\s*$/.B $1/; s/^\.BI\s+(\S+)\s*$/.B $1/; s/^\.IR\s+(\S+)\s*$/.I $1/; # Fiddling for syscalls(7) :-( if ($inTS) { my @cols=split(/\t/,$_); foreach my $c (@cols) { $inBlock+=()=$c=~m/T\{/g; $inBlock-=()=$c=~m/T\}/g; my $mtch=$c=~s/\s*\\fB([-\w.]+)\\fP\((\w+)\)/doMR($1,$2)/ge; $c="T{\n${c}\nT}" if $mtch and !$inBlock; } $_=join("\t",@cols); s/\n\n/\n/g; } s/\\&\././ if m/^.TH /; if (m/^\.TH\s+"?([-\w\\.]+)"?\s+"?(\w+)"?/) { print "$_\n"; # Add a level two bookmark. We don't set it in the TH macro since the name passed # may be different from the filename, i.e. file = unimplemented.2, TH = UNIMPLEMENTED 2 print ".pdfbookmark -T $bkmark 2 $nm($sec)\n"; next; } print "$_\n"; } close(F); } } sub doMR { my $nm=shift; my $sec=shift; if (exists($files{"${nm}.$sec"})) { return("\n.pdfhref L -D \"$files{\"${nm}.$sec\"}->[1]\" -A \"\\c\" -- \\fI$nm\\fP($sec)\n"); } else { return("\\fI$nm\\fP($sec)"); } } sub GetNmSec { my ($nm,$pth,$sec)=fileparse($_[0],$_[1]); $sec=substr($sec,1); my $srt=$nm; $srt=~s/\..+?$//; $srt=~s/^_+//; $srt=$1.sprintf("%04d",$2) if $srt=~m/^(.+)(\d+)$/; #$srt="$sec/$srt"; return($nm,$sec,$srt); } # add rpmvercmp #use RPM::VersionSort; #use Sort::Versions; sub sortman { # Sort - ignore case but frig it so that intro is the first entry. my (undef,$s1,$c)=GetNmSec($a,qr/\.\d[a-z]*/); my (undef,$s2,$d)=GetNmSec($b,qr/\.\d[a-z]*/); my $cmp=$s1 cmp $s2; return $cmp if $cmp; return -1 if ($c=~m/^intro/ and $d!~m/^intro/); return 1 if ($d=~m/^intro/ and $c!~m/^intro/); $c=~tr[-_(][!" ]; $d=~tr[-_(][!" ]; $cmp=lc($c) cmp lc($d); return($c cmp $d) if $cmp == 0; return($cmp); } sub strhex { my $res=''; foreach my $c (split('',$_[0])) { $res.=sprintf("%02X",ord($c)); } return($res); }