2019-05-15 16:59:40 +02:00
|
|
|
#!/usr/bin/env perl
|
2019-05-15 16:55:03 +02:00
|
|
|
#
|
|
|
|
# psgen -- Postscript generator for code portion of source books
|
|
|
|
#
|
|
|
|
# Reads in a list of files/dirs from <filelist>, runs munge on each of
|
|
|
|
# them, and generates a single postscript file to stdout. The page numbers
|
|
|
|
# for each file/dir are put into the file <pagenums>.
|
|
|
|
#
|
|
|
|
# usage: psgen [ options... ] <filelist> <pagenums> <volume #> > foo.ps
|
|
|
|
# -l<firstLogicalPage>
|
|
|
|
# -p<firstPhysicalPage>
|
|
|
|
# -f<font>
|
|
|
|
# -D<defs> (passed to yapp)
|
|
|
|
# -P<productNumber>
|
|
|
|
# -o<mungedOutFile>
|
|
|
|
# -e (auto edit errors)
|
|
|
|
#
|
|
|
|
# $Id: psgen,v 1.18 1997/11/13 21:44:16 colin Exp $
|
|
|
|
#
|
|
|
|
|
2019-05-15 18:56:59 +02:00
|
|
|
use File::Basename;
|
|
|
|
|
2019-05-15 16:55:03 +02:00
|
|
|
$bookRoot = $ENV{"BOOKROOT"} || ".";
|
2019-05-15 18:56:59 +02:00
|
|
|
$toolsDir = dirname(__FILE__);
|
2019-05-15 16:55:03 +02:00
|
|
|
$psDir = "$bookRoot/ps";
|
|
|
|
$editor = $ENV{"EDITOR"} || "vi";
|
|
|
|
|
|
|
|
# Configuration settings - external file names
|
|
|
|
$mungeProg = "$toolsDir/munge";
|
|
|
|
$yappProg = "$toolsDir/yapp";
|
|
|
|
$preambleFile = "$psDir/prolog.ps";
|
|
|
|
$tempFile = "/tmp/psgen-$$";
|
|
|
|
|
|
|
|
# Parse arguments
|
|
|
|
$firstLogPage = $firstPhysPage = 0;
|
|
|
|
$productNumber = 1;
|
|
|
|
$font = "OCRB";
|
|
|
|
$autoEdit = 0;
|
|
|
|
while ($#ARGV >= 0 && $ARGV[0] =~ /^-/)
|
|
|
|
{
|
|
|
|
$_ = shift @ARGV;
|
|
|
|
if (/^--$/)
|
|
|
|
{
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
elsif (/^-l(\d+)$/)
|
|
|
|
{
|
|
|
|
$firstLogPage = $1;
|
|
|
|
}
|
|
|
|
elsif (/^-p(\d+)$/)
|
|
|
|
{
|
|
|
|
$firstPhysPage = $1;
|
|
|
|
}
|
|
|
|
elsif (/^-f(.+)$/)
|
|
|
|
{
|
|
|
|
$font = $1;
|
|
|
|
}
|
|
|
|
elsif (/^-D(.+)$/)
|
|
|
|
{
|
|
|
|
$yappDefs .= " " . $_;
|
|
|
|
}
|
|
|
|
elsif (/^-P(\d+)$/)
|
|
|
|
{
|
|
|
|
$productNumber = $1;
|
|
|
|
}
|
|
|
|
elsif (/^-o(.+)$/)
|
|
|
|
{
|
|
|
|
$mungedOutFile = $1;
|
|
|
|
}
|
|
|
|
elsif (/^-e$/)
|
|
|
|
{
|
|
|
|
$autoEdit = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
&Error("Unrecognized option: '$_'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$fileListFile = shift @ARGV || die "Missing file list argument (arg 1)";
|
|
|
|
$pageNumFile = shift @ARGV || die "Missing page number file argument (arg 2)";
|
|
|
|
$volume = shift @ARGV || die "Missing volume number argument (arg 3)";
|
|
|
|
|
|
|
|
# Determine initial page numbers
|
|
|
|
{
|
|
|
|
my $nextLogPage = 1;
|
|
|
|
my $nextPhysPage = 3;
|
|
|
|
my $volNum = 0; # Which volume's page numbers we're reading
|
|
|
|
|
|
|
|
if ($volume > 1)
|
|
|
|
{
|
|
|
|
open(OLDPAGENUMS, "<$pageNumFile") || die;
|
|
|
|
while (<OLDPAGENUMS>)
|
|
|
|
{
|
|
|
|
if (/^Volume\s+(\d+)$/)
|
|
|
|
{
|
|
|
|
$volNum = $1;
|
|
|
|
}
|
|
|
|
elsif (/^Next:\s+(\d+)\s*$/ && $volNum == $volume - 1)
|
|
|
|
{
|
|
|
|
$nextLogPage = $1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(OLDPAGENUMS);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unlink($pageNumFile);
|
|
|
|
}
|
|
|
|
$firstLogPage = $nextLogPage if ($firstLogPage == 0);
|
|
|
|
$firstPhysPage = $nextPhysPage if ($firstPhysPage == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Names of PostScript operators invoked. These are the interface
|
|
|
|
# between this file and the $preambleFile.
|
|
|
|
$oddPageStartPS = "OddPageStart";
|
|
|
|
$evenPageStartPS = "EvenPageStart";
|
|
|
|
$oddPageEndPS = "OddPageEnd";
|
|
|
|
$evenPageEndPS = "EvenPageEnd";
|
|
|
|
$dirPagePS = "DirPage";
|
|
|
|
# This is short because it's emitted every line
|
|
|
|
$linePS = "L";
|
|
|
|
|
|
|
|
# Handle an error from munge.
|
|
|
|
# A result of 0 means to retry, 1 means to exit
|
|
|
|
sub MungeError
|
|
|
|
{
|
|
|
|
my $result = 1;
|
|
|
|
|
|
|
|
open(FILEH, "<$tempFile") || die;
|
|
|
|
while (<FILEH>)
|
|
|
|
{
|
|
|
|
print STDERR;
|
|
|
|
if (/ in (.*) line (\d+)$/)
|
|
|
|
{
|
|
|
|
my ($fileName, $lineNumber) = ($1, $2);
|
|
|
|
|
|
|
|
if ($autoEdit)
|
|
|
|
{
|
|
|
|
my @statResult = stat($fileName);
|
|
|
|
my $oldMTime = $statResult[9];
|
|
|
|
|
|
|
|
system("'$editor' '+$lineNumber' '$fileName' 1>&2");
|
|
|
|
@statResult = stat($fileName);
|
|
|
|
$result = ($statResult[9] == $oldMTime);
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(FILEH);
|
|
|
|
unlink($tempFile) || die "Couldn't unlink $tempFile";
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub CopyFileToPS
|
|
|
|
{
|
|
|
|
local $fileName = $_[0];
|
|
|
|
local $args = "'-I$psDir' '-Dfont=$font'";
|
|
|
|
local $_;
|
|
|
|
|
|
|
|
$args .= $yappDefs;
|
|
|
|
open(FILEH, "$yappProg $args '$fileName' |") || die;
|
|
|
|
while (<FILEH>)
|
|
|
|
{
|
|
|
|
print PSOUT $_;
|
|
|
|
}
|
|
|
|
close(FILEH) || exit(1);
|
|
|
|
1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Wrap a string in parens as required by PostScript, with proper quoting.
|
|
|
|
sub StringPS
|
|
|
|
{
|
|
|
|
local $str = $_[0];
|
|
|
|
|
|
|
|
$str =~ s/([\\()])/\\$1/g;
|
|
|
|
"(" . $str . ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
# Emit a start of page. The Postscript DSC %%Page: header
|
|
|
|
# (followed by logical page number, then physical) and
|
|
|
|
# the top-of-page function (which is passed the page number as a string)
|
|
|
|
sub PageStartPS
|
|
|
|
{
|
|
|
|
local $pageNum = $_[0];
|
|
|
|
|
|
|
|
"%%Page: " . ($pageNum + $firstLogPage) . " " .
|
|
|
|
($pageNum + $firstPhysPage) . "\n" .
|
|
|
|
&StringPS($pageNum + $firstLogPage) .
|
|
|
|
((($pageNum + $firstLogPage) % 2) ? $oddPageStartPS
|
|
|
|
: $evenPageStartPS) . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub PageEndPS
|
|
|
|
{
|
|
|
|
local $pageNum = $_[0];
|
|
|
|
|
|
|
|
((($pageNum + $firstLogPage) % 2) ? $oddPageEndPS : $evenPageEndPS) . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
# Save the page number to a table-of-contents file
|
|
|
|
sub SavePageNum
|
|
|
|
{
|
|
|
|
local ($fileName, $pageNum) = @_;
|
|
|
|
|
|
|
|
print PAGENUMS ($pageNum + $firstLogPage), ": $fileName\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
# The main code.
|
|
|
|
|
|
|
|
open(PSOUT, ">-") || die;
|
|
|
|
open(FILELIST, "<$fileListFile") || die;
|
|
|
|
open(PAGENUMS, ">>$pageNumFile") || die;
|
|
|
|
if ($mungedOutFile ne "")
|
|
|
|
{
|
|
|
|
open(MUNGEDOUT, ">$mungedOutFile") || die;
|
|
|
|
}
|
|
|
|
|
|
|
|
print PAGENUMS "Volume $volume\n";
|
|
|
|
|
|
|
|
&CopyFileToPS($preambleFile);
|
|
|
|
|
|
|
|
$fileNumber = 0;
|
|
|
|
$pageNum = 0; # This is 0-based, since it is added to $first{Log,Phys}Page
|
|
|
|
$enable = 0;
|
|
|
|
|
|
|
|
while (<FILELIST>)
|
|
|
|
{
|
|
|
|
/^([VDTB])(\S*)\s+(.*)/ || die "Illegal file list line $.";
|
|
|
|
|
|
|
|
local ($fileType, $options, $arg) = ($1, $2, $3);
|
|
|
|
|
|
|
|
if ($fileType eq "V")
|
|
|
|
{
|
|
|
|
@args = split(/\s+/, $arg);
|
|
|
|
if ($enable = ($args[0] == $volume))
|
|
|
|
{
|
|
|
|
$defaultTabWidth = int($args[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elsif ($fileType eq "D")
|
|
|
|
{
|
|
|
|
next unless $enable; # Do nothing if we're in the wrong volume
|
|
|
|
$dirName = $arg;
|
|
|
|
&SavePageNum($dirName, $pageNum);
|
|
|
|
print PSOUT &PageStartPS($pageNum);
|
|
|
|
print PSOUT &StringPS($dirName), $dirPagePS, "\n";
|
|
|
|
print PSOUT &PageEndPS($pageNum);
|
|
|
|
$pageNum++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
my $done = 0;
|
|
|
|
|
|
|
|
$fileNumber++;
|
|
|
|
$fileName = $arg;
|
|
|
|
next unless $enable; # Do nothing if we're in the wrong volume
|
|
|
|
&SavePageNum($fileName, $pageNum);
|
|
|
|
$quotedFileName = $fileName;
|
|
|
|
$quotedFileName =~ s/'/\\'/g;
|
|
|
|
$tabWidth = ($options =~ /(\d)/) ? $1 : $defaultTabWidth;
|
|
|
|
$args = ($fileType eq "B") ? "-b" : "";
|
|
|
|
$args .= " -$tabWidth -p$productNumber -f$fileNumber";
|
|
|
|
while (!$done)
|
|
|
|
{
|
|
|
|
if (open(FILE, "$mungeProg $args '$quotedFileName' 2>$tempFile |"))
|
|
|
|
{
|
|
|
|
$line = <FILE>;
|
|
|
|
print MUNGEDOUT $line;
|
|
|
|
|
|
|
|
while ($line ne "")
|
|
|
|
{
|
|
|
|
print PSOUT &PageStartPS($pageNum);
|
|
|
|
|
|
|
|
while ($line ne "" and $line !~ /^\f/)
|
|
|
|
{
|
|
|
|
chop $line;
|
|
|
|
print PSOUT &StringPS($line), $linePS, "\n";
|
|
|
|
$line = <FILE>;
|
|
|
|
print MUNGEDOUT $line;
|
|
|
|
}
|
|
|
|
$line =~ s/^\f//;
|
|
|
|
|
|
|
|
print PSOUT &PageEndPS($pageNum);
|
|
|
|
$pageNum++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (close(FILE))
|
|
|
|
{
|
|
|
|
$done = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$done = &MungeError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$done = &MungeError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($done == 1)
|
|
|
|
{
|
|
|
|
die;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Print PostScript DSC trailer with the correct number of pages
|
|
|
|
print PSOUT "%%Trailer\n%%Pages: ", $pageNum, "\n%%EOF\n";
|
|
|
|
|
|
|
|
print PAGENUMS "Pages: ", $pageNum, "\n";
|
|
|
|
print PAGENUMS "Next: ", ((($pageNum+1) & ~1) + $firstLogPage), "\n";
|
|
|
|
|
|
|
|
close(PAGENUMS) || die;
|
|
|
|
close(FILELIST) || die;
|
|
|
|
close(PSOUT) || die;
|
|
|
|
|
|
|
|
if ($mungedOutFile ne "")
|
|
|
|
{
|
|
|
|
close(MUNGEDOUT) || die;
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# vi: ai ts=4
|
|
|
|
# vim: si
|
|
|
|
#
|