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.STREAMFS.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.PENEW[ARRAY [0..960) OF PressFormat.PE];
buf: REF TEXTNEW[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: INTSIZE[PressFormat.DDV];
ReadDirectory: PROC [input: IO.STREAM] RETURNS [REF PressFormat.DDV] ~ TRUSTED {
ddv: REF PressFormat.DDVNEW[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: INTLAST[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.STREAMNIL] ~ {
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];
EXITS
Quit => RETURN;
};
SendSinglePages: PUBLIC PROC [server: Rope.ROPE, inputFile: Rope.ROPE, pageSpec: LIST OF PageRange, message: IO.STREAMNIL] ~ 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.STREAMIO.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.STREAMIO.RIS[cmd.commandLine];
inputName: ROPE ← stream.GetTokenRope[Break].token;
BreakIntoSinglePages[inputName, cmd.out];
};
SendSinglePagesCommand: Commander.CommandProc ~ {
stream: IO.STREAMIO.RIS[cmd.commandLine];
pageSpec: LIST OF PageRange ← LIST[[1, 1000000]];
inputName: ROPE ← stream.GetTokenRope[Break].token;
token: ROPE ← stream.GetTokenRope[Break].token;
server: ROPENIL;
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.