InterpressPageImpl.mesa
Copyright Ó 1986, 1989, 1990 by Xerox Corporation. All rights reserved.
Michael Plass, March 4, 1991 10:34 am PST
Last tweaked by Mike Spreitzer on May 23, 1989 9:28:53 am PDT
Provides commands for making an Interpress package by splitting out and combining pages of other Interpress masters.
Tim Diebert: November 12, 1990 8:58 am PST
DIRECTORY
Commander USING [CommandProc, Register],
CommandTool USING [ArgumentVector, Parse],
Convert USING [RopeFromInt],
FS USING [ComponentPositions, Copy, Delete, Error, ExpandName, FileInfo],
Imager USING [Context],
ImagerInterpress USING [Close, Create, DoPage, Ref],
Interpress USING [Close, DoPage, Master, Open],
IO,
Rope USING [Cat, Concat, Equal, Length, ROPE, Substr, ToRefText];
InterpressPageImpl: CEDAR PROGRAM
IMPORTS Commander, CommandTool, Convert, FS, ImagerInterpress, Interpress, IO, Rope
~ BEGIN
Copied Types
ROPE: TYPE ~ Rope.ROPE;
Command parsing aids
CmdTokenBreak: PROC [char: CHAR] RETURNS [IO.CharClass] = {
IF char = '← THEN RETURN [break];
IF char = ' OR char = '\t OR char = ', OR char = '; OR char = '\n THEN RETURN [sepr];
RETURN [other];
};
GetCmdToken: PROC [stream: IO.STREAM] RETURNS [rope: ROPE] = {
rope ← NIL;
rope ← stream.GetTokenRope[CmdTokenBreak ! IO.EndOfStream => CONTINUE].token;
};
ExpandedName: TYPE ~ RECORD [fullFName: ROPE, cp: FS.ComponentPositions];
ExpandName: PROC [inputName: ROPE] RETURNS [e: ExpandedName] ~ INLINE {
[e.fullFName, e.cp] ← FS.ExpandName[inputName];
};
Extracting pages
PageRange: TYPE ~ RECORD [startPage, nPages: INT];
SpecError: PUBLIC ERROR [offset: INT] ~ CODE;
DoExtract: PUBLIC PROC [inputFile, outputFile: ROPE, pageSpec: LIST OF PageRange, log: IO.STREAM] RETURNS [success: BOOLEANTRUE] ~ {
logProc: PROC [class: INT, code: ATOM, explanation: ROPE] ~ {
IO.PutRope[log, "\n *** Interpress Error "];
IO.PutRope[log, explanation];
success ← FALSE;
};
n: ExpandedName ~ ExpandName[FS.FileInfo[inputFile].fullFName];
master: Interpress.Master ~ Interpress.Open[n.fullFName, logProc];
out: ImagerInterpress.Ref ~ ImagerInterpress.Create[outputFile];
IO.PutRope[log, "Reading "];
IO.PutRope[log, n.fullFName];
IO.PutRope[log, " . . . "];
FOR each: LIST OF PageRange ← pageSpec, each.rest UNTIL each=NIL DO
pageRange: PageRange ~ each.first;
FOR i: INT IN [pageRange.startPage..MIN[pageRange.startPage+pageRange.nPages-1, master.pages]] DO
OnePage: PROC [context: Imager.Context] ~ {Interpress.DoPage[master, i, context, logProc]};
IO.PutF[stream: log, format: " [%g", v1: [integer[i]]];
ImagerInterpress.DoPage[out, OnePage];
IO.PutRope[self: log, r: "]"];
ENDLOOP;
ENDLOOP;
ImagerInterpress.Close[out];
IO.PutRope[log, " written.\n"];
};
InterpressExtractCommand: Commander.CommandProc ~ {
GetToken: PROC [stream: IO.STREAM] RETURNS [token: ROPENIL] = {
Break: PROC [char: CHAR] RETURNS [IO.CharClass] = {
IF char = '← THEN RETURN [break];
IF char = ' OR char = '  OR char = ', OR char = '; OR char = '\n THEN RETURN [sepr];
RETURN [other];
};
token ← stream.GetTokenRope[Break ! IO.EndOfStream => CONTINUE].token;
};
ParsePageSpec: PUBLIC PROC [pageSpecRope: ROPE] RETURNS [pageSpec: LIST OF PageRange, charsParsed: INT] ~ {
Reverse: PROC [pageSpec: LIST OF PageRange] RETURNS [reversed: LIST OF PageRange] ~ {
WHILE pageSpec # NIL DO
t: LIST OF PageRange ← pageSpec;
pageSpec ← t.rest;
t.rest ← reversed;
reversed ← t;
ENDLOOP;
};
text: REF TEXT ~ pageSpecRope.ToRefText;
i: NAT ← 0;
c: CHAR;
SkipSpaces: PROC ~ {c ← ' ; WHILE i < text.length AND ((c ← text[i]) = ', OR c = ' OR c = ' OR c = '\n) DO i ← i+1 ENDLOOP};
GetChar: PROC RETURNS [CHAR] ~ {IF i < text.length THEN {i ← i+1; RETURN [text[i-1]]} ELSE RETURN ['\000]};
Int: PROC RETURNS [value: INT𡤀] ~ {
SkipSpaces[];
IF NOT c IN ['0..'9] THEN ERROR SpecError[i];
WHILE i < text.length AND (c ← text[i]) IN ['0..'9] DO
value ← value * 10 + (c-'0);
i ← i+1;
ENDLOOP;
};
spec: LIST OF PageRange ← NIL;
SkipSpaces[];
WHILE i < text.length DO
SELECT text[i] FROM
IN ['0..'9] => spec ← CONS[[Int[], 1], spec];
'[, '( => {
open: CHAR ← GetChar[];
start: INT ← Int[];
end: INT;
SkipSpaces[];
IF i < text.length AND text[i] = '. THEN i ← i+1 ELSE ERROR SpecError[i];
IF i < text.length AND text[i] = '. THEN i ← i+1 ELSE ERROR SpecError[i];
end ← Int[];
SkipSpaces[];
IF (c ← GetChar[]) = '] OR c = ') THEN {
IF open = '( THEN start ← start + 1;
IF c = '] THEN end ← end + 1;
IF end > start THEN spec ← CONS[[start, end-start], spec]
}
ELSE ERROR SpecError[i];
};
ENDCASE => EXIT;
SkipSpaces[];
ENDLOOP;
RETURN [Reverse[spec], i]
};
stream: IO.STREAMIO.RIS[cmd.commandLine];
outputName: ROPE ← GetToken[stream];
inputName: ROPE;
pagesToken: ROPE;
pageSpec: LIST OF PageRange;
IF outputName.Length = 0 THEN {cmd.out.PutRope["Output file missing.\n"]; RETURN};
IF NOT GetToken[stream].Equal["←"] THEN {cmd.out.PutRope["Missing \"←\".\n"]; RETURN};
inputName ← GetToken[stream];
pagesToken ← GetToken[stream];
IF pagesToken.Equal["PAGE", FALSE] OR pagesToken.Equal["PAGES", FALSE] THEN {
skip: INT;
[pageSpec, skip] ← ParsePageSpec[cmd.commandLine.Substr[stream.GetIndex]];
stream.SetIndex[stream.GetIndex+skip];
}
ELSE pageSpec ← LIST[[1, 1000000]];
IF NOT DoExtract[inputName, outputName, pageSpec, cmd.out] THEN {
cmd.out.PutRope["Unable to extract any pages from "];
cmd.out.PutRope[inputName];
cmd.out.PutChar['\n];
}
ELSE {cmd.out.PutRope[outputName]; cmd.out.PutRope[" Written.\n"]};
IF NOT stream.EndOf THEN {cmd.out.PutRope["Ignored: "]; cmd.out.PutRope[cmd.commandLine.Substr[stream.GetIndex]]; cmd.out.PutChar['\n]};
};
Breaking into single pages
BreakupStyle: TYPE ~ {pageCount, numFiles};
DoBreakup: PROC [inputName: ROPE, log: IO.STREAM, style: BreakupStyle, div: INT] ~ {
logProc: PROC [class: INT, code: ATOM, explanation: ROPE] ~ {
IO.PutRope[log, "\n *** Interpress Error "];
IO.PutRope[log, explanation];
};
n: ExpandedName ~ ExpandName[FS.FileInfo[inputName].fullFName];
base: ROPE ~ Rope.Substr[n.fullFName, n.cp.base.start, n.cp.base.length];
ext: ROPE ~ Rope.Substr[n.fullFName, n.cp.ext.start, n.cp.ext.length];
logPages: BOOL ~ div#1 OR style#pageCount;
master: Interpress.Master;
nFiles, j: INT ← 0;
IO.PutRope[log, "Reading "];
IO.PutRope[log, n.fullFName];
IO.PutRope[log, " . . . "];
master ← Interpress.Open[n.fullFName, logProc];
IO.PutF[log, IF logPages THEN " %g pages\n" ELSE " %g pages; ", [integer[master.pages]]];
SELECT style FROM
pageCount => nFiles ← (master.pages+div-1)/div;
numFiles => nFiles ← div;
ENDCASE => ERROR;
FOR i: INT IN [1..nFiles] DO
lim: INT ~ SELECT style FROM
pageCount => MIN[j+div, master.pages],
numFiles => (i*master.pages + div/2)/div,
ENDCASE => ERROR;
name: ROPE ~ Rope.Cat[base, "-", Convert.RopeFromInt[i], ".", ext];
out: ImagerInterpress.Ref ~ ImagerInterpress.Create[name];
IO.PutRope[log, FS.ExpandName[name].fullFName];
WHILE j<lim DO
onePage: PROC [context: Imager.Context] ~ {
IF logPages THEN IO.PutF[log, " [%g", [integer[j]]];
Interpress.DoPage[master, j, context, logProc];
IF logPages THEN IO.PutRope[log, "]"];
};
j ← j+1;
ImagerInterpress.DoPage[out, onePage];
ENDLOOP;
IO.PutRope[log, IF logPages THEN "\n" ELSE " "];
ImagerInterpress.Close[out];
ENDLOOP;
IO.PutRope[log, "done.\n"];
};
InterpressBreakupCommand: Commander.CommandProc ~ {
stream: IO.STREAMIO.RIS[cmd.commandLine];
style: BreakupStyle ← pageCount;
div: INT ← 1;
inputName: ROPE ← GetCmdToken[stream];
IF inputName.Equal["-by"] THEN {
div ← stream.GetInt[!IO.Error => GOTO SyntaxError];
IF div<=0 THEN RETURN [result: $Failure, msg: IO.PutFR["Must divide by a positive number of pages, not %g", [integer[div]] ]];
inputName ← GetCmdToken[stream !IO.Error => GOTO SyntaxError];
}
ELSE IF inputName.Equal["-into"] THEN {
style ← numFiles;
div ← stream.GetInt[!IO.Error => GOTO SyntaxError];
IF div<=1 THEN RETURN [result: $Failure, msg: IO.PutFR["Number of output files must be greater than 1, not %g", [integer[div]] ]];
inputName ← GetCmdToken[stream !IO.Error => GOTO SyntaxError];
};
IF inputName = NIL THEN GOTO SyntaxError;
DoBreakup[inputName, cmd.out, style, div ! FS.Error => IF error.group = user THEN {result ← $Failure; msg ← error.explanation; CONTINUE}];
EXITS SyntaxError => RETURN[result: $Failure, msg: cmd.procData.doc]};
Concatenating masters together
DoConcatenate: PROC [outputName: ROPE, cmdLineStream: IO.STREAM, log: IO.STREAM] ~ {
pageNumber: INT ← 0;
logProc: PROC [class: INT, code: ATOM, explanation: ROPE] ~ {
IO.PutRope[log, "\n *** Interpress Error "];
IO.PutRope[log, explanation];
};
n: ExpandedName ~ ExpandName[outputName];
extendedName: ROPE ~ IF n.cp.ext.length = 0
THEN Rope.Substr[n.fullFName, 0, n.cp.base.start+n.cp.base.length].Concat[".interpress"]
ELSE n.fullFName;
tempName: ROPE ← Rope.Cat["/vux/tmp/", Rope.Substr[n.fullFName, 0, n.cp.base.start+n.cp.base.length], ".interpress"];
Write it with a temp name to avoid lock conflicts if it is also an input.
out: ImagerInterpress.Ref ~ ImagerInterpress.Create[tempName];
IO.PutRope[log, "Reading "];
FOR inputName: ROPE ← GetCmdToken[cmdLineStream], GetCmdToken[cmdLineStream] UNTIL inputName = NIL DO
fullInputName: ROPE ~ FS.FileInfo[inputName].fullFName;
master: Interpress.Master ~ Interpress.Open[fullInputName, logProc];
IO.PutRope[log, fullInputName];
IO.PutRope[log, " "];
FOR i: INT IN [1..master.pages] DO
onePage: PROC [context: Imager.Context] ~ {Interpress.DoPage[master, i, context, logProc]};
IO.PutRope[log, "["];
IO.PutRope[log, Convert.RopeFromInt[pageNumber ← pageNumber + 1]];
ImagerInterpress.DoPage[out, onePage];
IO.PutRope[log, "] "];
ENDLOOP;
ENDLOOP;
ImagerInterpress.Close[out];
[] ← FS.Copy[from: tempName, to: extendedName];
FS.Delete[tempName];
IO.PutRope[log, FS.FileInfo[extendedName].fullFName];
IO.PutRope[log, " written.\n"];
};
InterpressConcatenateCommand: Commander.CommandProc ~ {
stream: IO.STREAMIO.RIS[cmd.commandLine];
outputName: ROPE ← GetCmdToken[stream];
gets: ROPE ← GetCmdToken[stream];
IF outputName = NIL OR NOT gets.Equal["←"] THEN RETURN[result: $Failure, msg: cmd.procData.doc];
DoConcatenate[outputName, stream, cmd.out ! FS.Error => IF error.group = user THEN {result ← $Failure; msg ← error.explanation; CONTINUE}];
};
Page Counting
InterpressLS: Commander.CommandProc ~ {
ENABLE FS.Error => IF error.group = user THEN {result ← $Failure; msg ← error.explanation; CONTINUE};
log: IO.STREAM ~ cmd.out;
args: CommandTool.ArgumentVector ~ CommandTool.Parse[cmd: cmd, starExpand: TRUE];
logProc: PROC [class: INT, code: ATOM, explanation: ROPE] ~ {
IO.PutRope[log, "\n *** Interpress Error "];
IO.PutRope[log, explanation];
};
FOR i: NAT IN [1 .. args.argc) DO
name: ROPE ~ args[i];
fullInputName: ROPE ~ FS.FileInfo[name].fullFName;
master: Interpress.Master;
IO.PutF[log, "%g... ", [rope[fullInputName]]];
master ← Interpress.Open[fullInputName, logProc];
IO.PutF[log, " %g pages\n", [integer[master.pages]]];
Interpress.Close[master];
ENDLOOP;
};
Command Registration
Commander.Register[key: "InterpressExtract", proc: InterpressExtractCommand, doc: "Extract pages from interpress files, e.g. result.ip ← source.ip PAGES 1, 3, [10..15]"];
Commander.Register[key: "IPExtract", proc: InterpressExtractCommand, doc: "Extract pages from interpress files, e.g. result.ip ← source.ip PAGES 1, 3, [10..15] (alias for InterpressExtract)"];
Commander.Register[key: "InterpressBreakup", proc: InterpressBreakupCommand, doc: "Breaks an interpress master into a collection of masters: InterpressBreakup [-by <pages> | -into <files>] <input>"];
Commander.Register[key: "IPBreakup", proc: InterpressBreakupCommand, doc: "Breaks an interpress master into a collection of masters (alias for InterpressBreakup)"];
Commander.Register[key: "InterpressConcatenate", proc: InterpressConcatenateCommand, doc: "Concatenates a collection of Interpress masters into one. (output ← input1 input2 . . .)"];
Commander.Register[key: "IPConcatenate", proc: InterpressConcatenateCommand, doc: "Concatenates a collection of Interpress masters into one. (output ← input1 input2 . . .) (alias for InterpressConcatenate)"];
Commander.Register[key: "InterpressLS", proc: InterpressLS, doc: "List page count of files, e.g. InterpressLS foo.ip bar.ip"];
Commander.Register[key: "IPLS", proc: InterpressLS, doc: "List page count of files (alias for InterpressLS)"];
END.