PressFileUtilitiesImpl.mesa
Michael Plass, September 8, 1983 3:12 pm
Last Edited by: Beach, April 6, 1984 2:22:11 pm PST
DIRECTORY Commander, Basics, Rope, FS, IO, PressFormat, PressFileUtilities, UserProfile, TSViewer, PressPrinter;
PressFileUtilitiesImpl:
CEDAR
PROGRAM
IMPORTS Commander, FS, IO, Rope, UserProfile, TSViewer, PressPrinter
EXPORTS PressFileUtilities
~ BEGIN
Bad: ERROR ~ CODE;
bytesPerPressPage: INT ~ 512;
bytesPerWord: INT ~ Basics.bytesPerWord;
PageRange: TYPE ~ PressFileUtilities.PageRange;
ROPE: TYPE ~ Rope.ROPE;
SpecError: PUBLIC ERROR [offset: INT] ~ CODE;
RopeFromInt:
PROC [int:
INT]
RETURNS [
ROPE] ~ {
RETURN [
SELECT
ABS[int]
FROM
0 => "0",
1 => "1",
2 => "2",
3 => "3",
4 => "4",
5 => "5",
6 => "6",
7 => "7",
8 => "8",
9 => "9",
ENDCASE => RopeFromInt[ABS[int]/10].Concat[RopeFromInt[ABS[int] MOD 10]]
];
};
ParsePageSpec:
PUBLIC
PROC [pageSpecRope:
ROPE]
RETURNS [pageSpec:
LIST
OF PageRange, charsParsed:
INT] ~ {
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]
};
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;
};
ExtractPages:
PUBLIC
PROC [inputFile, outputFile:
ROPE, pageSpec:
LIST
OF PageRange]
RETURNS [success:
BOOLEAN] ~
TRUSTED {
input: IO.STREAM ← FS.StreamOpen[fileName: inputFile ! FS.Error => GOTO Quit];
output: IO.STREAM ← FS.StreamOpen[fileName: outputFile, accessOptions: create, keep: 2];
documentDirectory: REF PressFormat.DDV ← ReadDirectory[input ! Bad => GOTO Quit];
pdStartByte: INT ← documentDirectory.pdStart*bytesPerPressPage;
pdLength: INT ← documentDirectory.pdRecs*bytesPerPressPage;
numberOfParts: INT ← documentDirectory.nParts;
ReadPart:
PROC [n:
INT]
RETURNS [partEntry: PressFormat.
PE] ~
TRUSTED {
dest: LONG POINTER ← @partEntry;
input.SetIndex[pdStartByte + n*bytesPerWord*SIZE[PressFormat.PE]];
[] ← input.UnsafeGetBlock[block: [base: dest, startIndex: 0, count: bytesPerWord*SIZE[PressFormat.PE]]];
};
page: INT ← 0;
outputPart: INT ← 0;
outputParts: REF ARRAY [0..960) OF PressFormat.PE ← NEW[ARRAY [0..960) OF PressFormat.PE];
buf: REF TEXT ← NEW[TEXT[bytesPerPressPage]];
CopyBlocks:
PROC [nBlocks:
INT] ~
CHECKED {
buf.length ← bytesPerPressPage;
THROUGH [0..nBlocks)
DO
IF input.GetBlock[buf] # bytesPerPressPage THEN ERROR;
output.PutBlock[buf];
ENDLOOP;
};
success ← FALSE;
WHILE pageSpec #
NIL
DO
FOR i:
INT
IN [0..numberOfParts)
DO
partEntry: PressFormat.PE ← ReadPart[i];
IF partEntry.Type = PressFormat.PETypeFont
AND pageSpec.rest =
NIL
THEN {
outputParts[outputPart] ← [partEntry.Type, output.GetIndex/bytesPerPressPage, partEntry.pRecs, partEntry.Padding];
outputPart ← outputPart + 1;
input.SetIndex[partEntry.pStart*bytesPerPressPage];
CopyBlocks[partEntry.pRecs];
}
ELSE
IF partEntry.Type = PressFormat.PETypePage
THEN {
page ← page + 1;
IF page > pageSpec.first.startPage + pageSpec.first.nPages AND pageSpec.rest # NIL AND page <= pageSpec.rest.first.startPage THEN pageSpec ← pageSpec.rest;
IF page
IN [pageSpec.first.startPage..pageSpec.first.startPage + pageSpec.first.nPages)
THEN {
success ← TRUE;
outputParts[outputPart] ← [partEntry.Type, output.GetIndex/bytesPerPressPage, partEntry.pRecs, partEntry.Padding];
outputPart ← outputPart + 1;
input.SetIndex[partEntry.pStart*bytesPerPressPage];
CopyBlocks[partEntry.pRecs];
}
};
ENDLOOP;
pageSpec ← pageSpec.rest;
ENDLOOP;
documentDirectory.pdStart ← output.GetIndex/bytesPerPressPage;
documentDirectory.pdRecs ← (outputPart*bytesPerWord*SIZE[PressFormat.PE] + bytesPerPressPage - 1)/bytesPerPressPage;
documentDirectory.nRecs ← documentDirectory.pdStart+documentDirectory.pdRecs+1;
documentDirectory.nParts ← outputPart;
documentDirectory.fPage ← documentDirectory.lPage ← LAST[CARDINAL];
documentDirectory.Backp ← 0;
output.UnsafePutBlock[block: [base: LOOPHOLE[@(outputParts[0])], startIndex: 0, count: documentDirectory.pdRecs*bytesPerPressPage]];
output.UnsafePutBlock[block: [base: LOOPHOLE[documentDirectory], startIndex: 0, count: bytesPerWord*SIZE[PressFormat.DDV]]];
UNTIL output.GetLength MOD bytesPerPressPage = 0 DO output.PutChar['\000] ENDLOOP;
input.Close;
output.Close;
EXITS Quit => success ← FALSE;
};
ddvSize: INT ← SIZE[PressFormat.DDV];
ReadDirectory:
PROC [input:
IO.
STREAM]
RETURNS [
REF PressFormat.
DDV] ~
TRUSTED {
ddv: REF PressFormat.DDV ← NEW[PressFormat.DDV];
fileLength: INT ← input.GetLength;
IF fileLength MOD bytesPerPressPage # 0 THEN ERROR Bad;
IF fileLength < bytesPerPressPage THEN ERROR Bad;
input.SetIndex[fileLength-bytesPerPressPage];
[] ← input.UnsafeGetBlock[block: [base: LOOPHOLE[ddv], startIndex: 0, count: bytesPerWord*SIZE[PressFormat.DDV]]];
IF ddv.Passwd # PressFormat.PressPasswd THEN ERROR Bad;
IF ddv.nRecs*bytesPerPressPage # fileLength THEN ERROR Bad;
RETURN [ddv];
};
InsertPageNumberIntoName:
PROC [inputFile:
ROPE, number:
INT, lastNumber:
INT ←
LAST[
INT]]
RETURNS [result:
ROPE] ~ {
start: INT ← Rope.Find[s1: inputFile, s2: ".press", case: FALSE];
IF start < 0 THEN start ← inputFile.Length;
IF lastNumber =
LAST[
INT]
THEN
result ← inputFile.Replace[start: start, len: 0, with: Rope.Concat["-Page", RopeFromInt[number]]]
ELSE
result ← inputFile.Replace[start: start, len: 0, with: Rope.Cat["-Page", RopeFromInt[number], "of", RopeFromInt[lastNumber]]]
};
BreakIntoSinglePages:
PUBLIC
PROC [inputFile:
ROPE, message:
IO.
STREAM ←
NIL] ~ {
page: INT ← 1;
input: IO.STREAM ← FS.StreamOpen[fileName: inputFile ! FS.Error => GOTO Quit];
documentDirectory: REF PressFormat.DDV ← ReadDirectory[input ! Bad => GOTO Quit];
numberOfPages: INT ← documentDirectory.nParts - 1; -- don't include font part
outputName: ROPE ← InsertPageNumberIntoName[inputFile, page, numberOfPages];
IF message # NIL THEN message.PutRope["Writing"];
WHILE page <= numberOfPages
AND ExtractPages[inputFile, outputName,
LIST[[page,1]]]
DO
IF message # NIL THEN {message.PutChar[' ]; message.PutRope[outputName]};
page ← page + 1;
outputName ← InsertPageNumberIntoName[inputFile, page, numberOfPages];
ENDLOOP;
message.PutChar['\n];
};
SendSinglePages:
PUBLIC
PROC [server: Rope.
ROPE, inputFile: Rope.
ROPE, pageSpec:
LIST
OF PageRange, message:
IO.
STREAM ←
NIL] ~
TRUSTED {
tool: TSViewer.Tool ← TSViewer.NewTool[server];
IF
NOT PressPrinter.IsAPressFile[inputFile]
THEN {
message.PutRope[inputFile];
message.PutRope[" is not a press file\n"];
RETURN;
};
FOR pageRangeList:
LIST
OF PageRange ← pageSpec, pageRangeList.rest
UNTIL pageRangeList =
NIL
DO
pageRange: PageRange ← pageRangeList.first;
FOR page:
INT
IN [pageRange.startPage..pageRange.startPage+pageRange.nPages)
DO
tempName: ROPE ← InsertPageNumberIntoName[inputFile, page].Concat["$"];
IF NOT ExtractPages[inputFile, tempName, LIST[[page,1]]] THEN EXIT;
IF message #
NIL
THEN {
message.PutRope[tempName];
message.PutRope[" written\n"];
};
TSViewer.WaitForIdle[tool];
TSViewer.QueueRequest[tool, tempName];
TSViewer.Print[tool];
ENDLOOP;
ENDLOOP;
};
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];
};
PressExtractCommand: Commander.CommandProc ~ {
stream: IO.STREAM ← IO.RIS[cmd.commandLine];
outputName: ROPE ← stream.GetTokenRope[Break].token;
inputName: ROPE;
pagesToken: ROPE;
pageSpec: LIST OF PageRange;
IF outputName.Length = 0 THEN {cmd.out.PutRope["Output file missing.\n"]; RETURN};
IF NOT stream.GetTokenRope[Break].token.Equal["←"] THEN {cmd.out.PutRope["Missing \"←\".\n"]; RETURN};
inputName ← stream.GetTokenRope[Break].token;
pagesToken ← stream.GetTokenRope[Break].token;
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 ExtractPages[inputName, outputName, pageSpec]
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]};
};
PressPagesCommand: Commander.CommandProc ~ {
stream: IO.STREAM ← IO.RIS[cmd.commandLine];
inputName: ROPE ← stream.GetTokenRope[Break].token;
BreakIntoSinglePages[inputName, cmd.out];
};
SendSinglePagesCommand: Commander.CommandProc ~ {
stream: IO.STREAM ← IO.RIS[cmd.commandLine];
pageSpec: LIST OF PageRange ← LIST[[1, 1000000]];
inputName: ROPE ← stream.GetTokenRope[Break].token;
token: ROPE ← stream.GetTokenRope[Break].token;
server: ROPE ← NIL;
WHILE
NOT stream.EndOf
DO
IF token.Equal["PAGE",
FALSE]
OR token.Equal["PAGES",
FALSE]
THEN {
skip: INT;
[pageSpec, skip] ← ParsePageSpec[cmd.commandLine.Substr[stream.GetIndex]];
stream.SetIndex[stream.GetIndex+skip];
}
ELSE IF token.Equal["TO", FALSE] THEN server ← stream.GetTokenRope[Break].token
ELSE {cmd.out.PutRope["Unknown keyword: "]; cmd.out.PutRope[token]; RETURN};
token ← stream.GetTokenRope[Break].token;
ENDLOOP;
IF server = NIL THEN server ← UserProfile.Token["Hardcopy.PressPrinter", "Clover"];
SendSinglePages[server, inputName, pageSpec, cmd.out];
};
Commander.Register["PressExtract", PressExtractCommand, "Extract pages from press files, e.g. result.press ← myPress.press PAGES 1, 3, [10..15]"];
Someday Merge/extract pages from press files, e.g. result.press ← myPress1.press PAGES 1, 3, [10..15], myPress2.press PAGE 1, myPress3.press
Commander.Register["PressPages", PressPagesCommand, "Break a press file into single-page press files"];
Commander.Register["SendSinglePages", SendSinglePagesCommand, "Send single pages to a server, e.g., SendSinglePages myPress.press PAGES 1, 3, [10..15] TO RockNRoll"];
END.