GetFileImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
written by Paxton. January 1981
Paxton. December 1, 1982 9:44 am
Russ Atkinson, June 18, 1984 8:29:12 pm PDT
Paul Rovner, August 10, 1983 4:28 pm
Doug Wyatt, March 3, 1985 5:09:00 pm PST
Michael Plass, March 29, 1985 2:10:32 pm PST
Rick Beach, March 28, 1985 9:46:42 am PST
DIRECTORY
Ascii USING [Lower],
Atom USING [MakeAtom, MakeAtomFromRefText],
BasicTime USING [GMT],
FileOps USING [comment, endNode, endOfFile, IntBytes, LengthByte, look1, look2, look3, looks, looksFirst, LooksIndex, looksLast, Op, otherNode, otherNodeShort, otherNodeSpecs, otherNodeSpecsShort, prop, PropIndex, propShort, rope, runs, startNode, startNodeFirst, startNodeLast, terminalTextNode, terminalTextNodeFirst, terminalTextNodeLast, ThirdByte, FormatIndex],
FileReader USING [FromRope, FromStream, InterDoc, Open, OpenC],
FS USING [ComponentPositions, ExpandName, GetName, OpenFile, Position],
InterFile USING [FromRope],
IO USING [STREAM],
NodeProps USING [DoSpecs, PutProp],
PGSupport USING [BadIndex, CreatePGF, EnterLooks, EnterProp, EnterFormatName, FreePGF, PGF, RetrieveLooks, RetrieveProp, RetrieveFormatName],
PutGet USING [],
Rope USING [MaxLen, ROPE, Substr, Translate],
RopeEdit USING [MaxLen, Substr],
RopeReader USING [Backwards, BumpIndex, Get, GetRope, Position, ReadOffEnd, Ref, SetIndex],
RuntimeError USING [UNCAUGHT],
SafeStorage USING [SetCollectionInterval],
TextEdit USING [],
TextLooks USING [BaseRuns, Concat, Looks, LooksBytes, Runs],
TextLooksSupport USING [NewBase, Short],
TextNode USING [Body, MaxLen, Ref, RefTextNode];
GetFileImpl: CEDAR MONITOR
IMPORTS Ascii, Atom, FileReader, FS, InterFile, NodeProps, PGSupport, Rope, RopeEdit, RopeReader, RuntimeError, SafeStorage, TextLooks, TextLooksSupport
EXPORTS PutGet
SHARES TextLooks
= BEGIN OPEN FileOps;
ROPE: TYPE = Rope.ROPE;
Byte: TYPE = [0..255];
FromFileError: PUBLIC ERROR = CODE;
FromFile: PUBLIC PROC [fileName: ROPE, start: INT ← 0, len: INT ← TextNode.MaxLen] RETURNS [root: TextNode.Ref] = {
control, comment, stext: RopeReader.Ref;
interdoc: ROPE;
tiogaFile: BOOL;
fh: FS.OpenFile;
createDate: BasicTime.GMT;
StartRead[];
{ ENABLE UNWIND => EndRead[];
[control, comment, stext, tiogaFile, fh, createDate] ←
FileReader.Open[fileName, start, len !
FileReader.InterDoc => { interdoc ← doc; RESUME }];
root ← IF interdoc#NIL THEN InterFile.FromRope[interdoc]
ELSE Finish[control, comment, stext, tiogaFile];
};
AddFileExtension[root, fh];
AddCreateDate[root, createDate];
EndRead[];
};
FromFileC: PUBLIC PROC [file: FS.OpenFile, start: INT ← 0, len: INT ← TextNode.MaxLen]
RETURNS [root: TextNode.Ref] = {
control, comment, stext: RopeReader.Ref;
interdoc: ROPE;
tiogaFile: BOOL;
createDate: BasicTime.GMT;
StartRead[];
{ ENABLE UNWIND => EndRead[];
[control, comment, stext, tiogaFile, createDate] ← FileReader.OpenC[file, start, len !
FileReader.InterDoc => { interdoc ← doc; RESUME }];
root ← IF interdoc#NIL THEN InterFile.FromRope[interdoc]
ELSE Finish[control, comment, stext, tiogaFile];
};
AddFileExtension[root, file];
AddCreateDate[root, createDate];
EndRead[];
};
AddFileExtension: PROC [root: TextNode.Ref, file: FS.OpenFile] = {
ForceLower: PROC [r: ROPE] RETURNS [ROPE] = {
ForceCharLower: PROC [old: CHAR] RETURNS [new: CHAR] = {
RETURN [Ascii.Lower[old]];
};
RETURN [Rope.Translate[base: r, translator: ForceCharLower]];
};
name: ROPE; cp: FS.ComponentPositions;
[fullFName: name, cp: cp] ← FS.ExpandName[name: FS.GetName[file].fullFName];
NodeProps.PutProp[root, $FileExtension, IF cp.ext.length=0 THEN $null
ELSE Atom.MakeAtom[ForceLower[name.Substr[cp.ext.start, cp.ext.length]]]];
};
AddCreateDate: PROC [root: TextNode.Ref, createDate: BasicTime.GMT] = {
NodeProps.PutProp[root, $FileCreateDate,
NEW[BasicTime.GMT ← createDate]];
};
FromRope: PUBLIC PROC
[rope: ROPE, start: INT ← 0, len: INT ← TextNode.MaxLen] RETURNS [root: TextNode.Ref] = {
control, comment, stext: RopeReader.Ref;
interdoc: ROPE;
tiogaFile: BOOL;
StartRead[];
{ ENABLE UNWIND => EndRead[];
[control, comment, stext, tiogaFile] ← FileReader.FromRope[rope, start, len !
FileReader.InterDoc => { interdoc ← doc; RESUME }];
root ← IF interdoc#NIL THEN InterFile.FromRope[interdoc]
ELSE Finish[control, comment, stext, tiogaFile];
};
EndRead[];
};
FromStream: PUBLIC PROC
[stream: IO.STREAM, len: INT ← TextNode.MaxLen] RETURNS [root: TextNode.Ref] = {
control, comment, stext: RopeReader.Ref;
interdoc: ROPE;
tiogaFile: BOOL;
StartRead[];
{ ENABLE UNWIND => EndRead[];
[control, comment, stext, tiogaFile] ← FileReader.FromStream[stream, len !
FileReader.InterDoc => { interdoc ← doc; RESUME }];
root ← IF interdoc#NIL THEN InterFile.FromRope[interdoc]
ELSE Finish[control, comment, stext, tiogaFile];
};
EndRead[];
};
collectionInterval: LONG CARDINAL;
bigCollectionInterval: LONG CARDINAL = 1000000;
startCount: INTEGER ← 0;
StartRead: ENTRY PROC = {
ENABLE UNWIND => NULL;
IF startCount = 0 THEN {
collectionInterval ← SafeStorage.SetCollectionInterval[bigCollectionInterval];
};
startCount ← startCount+1;
};
EndRead: ENTRY PROC = { -- restore initial collectionInterval when all Get's done
ENABLE UNWIND => NULL;
IF (startCount ← startCount-1) = 0 THEN
[] ← SafeStorage.SetCollectionInterval[collectionInterval];
};
Finish: PROC [control, cmmnt, stext: RopeReader.Ref, tiogaFile: BOOL] RETURNS [root: TextNode.Ref] = {
op: Op;
parent, node, prev: TextNode.Ref;
textNode: TextNode.RefTextNode;
terminalNode: BOOL;
textLength, runsLength: INT ← 0;
formatName: ATOM;
propname: ATOM;
charProps: REFNIL;
charSets: REFNIL;
InsertNode: PROC [node: TextNode.Ref] = {
IF prev#NIL THEN prev.next ← node
ELSE IF parent#NIL THEN parent.child ← node;
prev ← node;
};
ReadByte: PROC RETURNS [Byte] = INLINE {
RETURN [LOOPHOLE[ReadChar[]]];
};
ReadChar: PROC RETURNS [CHAR] = {
RETURN [RopeReader.Get[control]];
};
ReadProp: PROC = {
specs: ROPE ← GetControlRope[];
disaster: BOOLFALSE;
value: REF;
value ← NodeProps.DoSpecs[propname, specs !
RuntimeError.UNCAUGHT => {disaster←TRUE}
];
IF disaster THEN ERROR FromFileError;
IF value # NIL THEN {
The character properties and character sets are not stored in the node until after the rope is stored, so that NodePropsImpl can do its consistency check.
IF propname = $CharProps THEN charProps ← value
ELSE IF propname = $CharSets THEN charSets ← value
ELSE NodeProps.PutProp[node, propname, value];
};
};
GetRope: PROC [len: INT, rdr: RopeReader.Ref] RETURNS [ROPE] = {
rope: ROPE;
pos: INT;
[rope, pos] ← RopeReader.Position[rdr];
RopeReader.SetIndex[rdr, pos+len];
RETURN [RopeEdit.Substr[rope, pos, len]];
};
GetControlRope: PROC RETURNS [ROPE] = {
RETURN [GetRope[ReadLength[], control]];
};
GetText: PROC = {
len: NAT;
IF (len ← ReadByte[]) > text.maxLength THEN text ← NEW[TEXT[len]];
FOR i: NAT IN [0..len) DO
text[i] ← RopeReader.Get[control];
ENDLOOP;
text.length ← len;
};
GetAtom: PROC RETURNS [ATOM] = {
GetText[]; -- get the print name
RETURN [IF text.length = 0 THEN NIL ELSE Atom.MakeAtomFromRefText[text]];
};
ReadLength: PROC RETURNS [INT] = {
first, second, fourth: LengthByte;
third: ThirdByte;
card: IntBytes;
first ← LOOPHOLE[ReadByte[]];
card.first ← first.data;
IF ~first.others THEN RETURN [LOOPHOLE[card]];
second ← LOOPHOLE[ReadByte[]];
card.second ← second.data;
IF ~second.others THEN RETURN [LOOPHOLE[card]];
third ← LOOPHOLE[ReadByte[]];
card.thirdBottom ← third.dataBottom;
card.thirdTop ← third.dataTop;
IF ~third.others THEN RETURN [LOOPHOLE[card]];
fourth ← LOOPHOLE[ReadByte[]];
card.fourth ← fourth.data;
RETURN [LOOPHOLE[card]];
};
NextOp: PROC RETURNS [op: Op] = {
op ← RopeReader.Get[control ! RopeReader.ReadOffEnd => { op ← endOfFile; CONTINUE }];
};
MAIN PROGRAM
pgf: PGSupport.PGF ← PGSupport.CreatePGF[];
text: REF TEXTNEW[TEXT[32]];
terminalNode ← FALSE;
op ← NextOp[];
DO
SELECT op FROM
IN [terminalTextNodeFirst..terminalTextNodeLast] => {
formatName ← PGSupport.RetrieveFormatName[
LOOPHOLE[op-terminalTextNodeFirst, FormatIndex], pgf
! PGSupport.BadIndex => ERROR FromFileError
];
terminalNode ← TRUE;
};
IN [startNodeFirst..startNodeLast] =>
formatName ← PGSupport.RetrieveFormatName[
LOOPHOLE[op-startNodeFirst, FormatIndex], pgf
! PGSupport.BadIndex => ERROR FromFileError
];
endNode => {
IF prev#NIL THEN { prev.last ← TRUE; prev.next ← parent;};
prev ← parent;
IF (parent ← parent.next)=NIL THEN EXIT;
op ← NextOp[]; LOOP;
};
startNode => {
formatName ← GetAtom[];
[] ← PGSupport.EnterFormatName[formatName, pgf];
};
terminalTextNode => {
formatName ← GetAtom[];
[] ← PGSupport.EnterFormatName[formatName, pgf];
terminalNode ← TRUE;
};
rope, comment => {
reader: RopeReader.Ref;
IF op=rope THEN reader ← stext ELSE { reader ← cmmnt; textNode.comment ← TRUE;};
IF textNode=NIL OR textNode#node THEN ERROR FromFileError;
IF (textLength←ReadLength[]) > 0 THEN -- get the rope
textNode.rope ← GetRope[textLength, reader];
IF charProps#NIL THEN {NodeProps.PutProp[node,$CharProps,charProps]; charProps←NIL};
IF charSets#NIL THEN {NodeProps.PutProp[node, $CharSets, charSets]; charSets ← NIL};
SELECT runsLength FROM
0 => NULL; -- no runs for this rope
textLength => runsLength ← 0; -- correct length
ENDCASE => ERROR FromFileError; -- mismatch
RopeReader.BumpIndex[reader, 1]; -- skip CR at end of rope
op ← NextOp[]; LOOP;
};
runs => { -- runs, if any, come before corresponding rope
lookRuns: TextLooks.Runs;
numRuns: INT;
pos: INT ← 0;
IF textNode=NIL OR textNode#node THEN ERROR FromFileError;
numRuns ← ReadLength[]; -- read number of runs
WHILE numRuns > 0 DO
num: NAT ← TextLooksSupport.Short[MIN[numRuns, LAST[NAT]]];
baseRuns: TextLooks.BaseRuns ← TextLooksSupport.NewBase[num];
len: INT ← pos;
numRuns ← numRuns-num;
FOR i:NAT IN [0..num) DO -- read runs for this baseRuns
looks: TextLooks.Looks;
ReadLookChars: PROC [num: NAT] = {
FOR i:NAT IN [0..num) DO looks[ReadChar[]] ← TRUE; ENDLOOP;
[] ← PGSupport.EnterLooks[looks, pgf];
};
SELECT op ← NextOp[] FROM
IN [looksFirst .. looksLast] => {
looks ← PGSupport.RetrieveLooks[
LOOPHOLE[op-looksFirst, LooksIndex], pgf
! PGSupport.BadIndex => ERROR FromFileError
]
};
look1 => ReadLookChars[1];
look2 => ReadLookChars[2];
look3 => ReadLookChars[3];
FileOps.looks => {
read 4 bytes of looks from control stream
lb: TextLooks.LooksBytes;
lb.byte0 ← ReadByte[]; lb.byte1 ← ReadByte[];
lb.byte2 ← ReadByte[]; lb.byte3 ← ReadByte[];
[] ← PGSupport.EnterLooks[looks ← LOOPHOLE[lb], pgf];
};
ENDCASE => ERROR FromFileError;
baseRuns[i] ← [pos←pos+ReadLength[], looks];
ENDLOOP;
lookRuns ← IF lookRuns=NIL THEN baseRuns ELSE
TextLooks.Concat[lookRuns, baseRuns, len, pos-len];
ENDLOOP;
runsLength ← pos; -- for use in checking rope length
textNode.runs ← lookRuns;
op ← NextOp[]; LOOP;
};
prop => {
[] ← PGSupport.EnterProp[propname ← GetAtom[], pgf];
ReadProp;
op ← NextOp[]; LOOP;
};
propShort => {
propname ← PGSupport.RetrieveProp[
LOOPHOLE[ReadByte[], PropIndex], pgf
! PGSupport.BadIndex => ERROR FromFileError
];
ReadProp;
op ← NextOp[]; LOOP;
};
otherNode, otherNodeShort, otherNodeSpecs, otherNodeSpecsShort => {
ERROR FromFileError;
};
endOfFile => {
IF parent=NIL THEN EXIT; -- have reached the root
[] ← RopeReader.Backwards[control]; -- backup so read endOfFile again
op ← endNode; LOOP;
};
ENDCASE => ERROR FromFileError;
if reach here, then want to start a new text node
IF charProps # NIL OR charSets # NIL THEN ERROR FromFileError;
InsertNode[node ← textNode ← NEW[TextNode.Body]];
node.formatName ← formatName;
IF terminalNode THEN terminalNode ← FALSE
ELSE { node.next ← parent; parent ← node; prev ← NIL };
op ← NextOp[];
ENDLOOP;
IF (root ← prev)=NIL THEN -- don't have a normal tree
IF node=NIL THEN root ← NEW[TextNode.Body] -- null file
ELSE root ← node; -- single node in file
root.last ← TRUE;
NodeProps.PutProp[root, $FromTiogaFile, IF tiogaFile THEN $Yes ELSE $No];
PGSupport.FreePGF[pgf];
};
END.