GetFileImpl.Mesa;
Written by Paxton. January 1981
Last edited by Paxton - August 19, 1983 9:22 am
DIRECTORY
Atom USING [MakeAtom],
CIFS USING [Close, OpenFile],
CreateNode USING [CreateFromInfo, SpanInfo, SpanInfoRec],
File USING [Capability],
FileReader USING [FreeReply, Reply, FromRope, FromStream, Open, OpenC],
IO USING [Handle],
NameSymbolTable USING [MakeName, Name],
NodeProps USING [DoSpecs, PutProp],
PGSupport USING [CreatePGF, EnterLooks, EnterProp, EnterTypeName, FreePGF, PGF, RetrieveLooks, RetrieveProp, RetrieveTypeName],
PutGet USING [MaxLen],
Rope USING [Find, FromRefText, Lower, ROPE, Size, Substr, Translate],
RopeReader USING [Backwards, BumpIndex, Get, GetString, Position, ReadOffEnd, Ref, SetIndex],
RopeEdit USING [ROPE, Substr],
System USING [GreenwichMeanTime],
T1FileOps,
TiogaLooks USING [BaseRuns, Looks, Runs],
TiogaLooksOps USING [Concat, LooksBytes],
TiogaLooksSupport USING [NewBase, Short],
TiogaNode USING [defaultTextClassID, Offset, Node, Ref, RefBranchNode, RefTextNode],
TiogaNodeOps USING [DocFromNode, FromRope, NarrowToBranchNode, NewBranchNode, NewTextNode];
GetFileImpl:
CEDAR PROGRAM
IMPORTS Atom, CIFS, CreateNode, FileReader, NameSymbolTable, NodeProps, PGSupport, Rope, RopeEdit, RopeReader, TiogaLooksOps, TiogaLooksSupport, TiogaNodeOps
EXPORTS PutGet
SHARES TiogaNode = BEGIN
Byte: TYPE = [0..255];
FromFileError: PUBLIC ERROR = CODE;
FromFile:
PUBLIC
PROC [
fileName: Rope.ROPE, start: TiogaNode.Offset ← 0, len: TiogaNode.Offset ← PutGet.MaxLen]
RETURNS [node: TiogaNode.Ref] = {
ForceLower:
PROC [r: Rope.
ROPE]
RETURNS [Rope.
ROPE] = {
ForceCharLower:
SAFE
PROC [old:
CHAR]
RETURNS [new:
CHAR] =
TRUSTED {
RETURN [Rope.Lower[old]] };
RETURN [Rope.Translate[base: r, translator: ForceCharLower]] };
reply: FileReader.Reply;
fh: CIFS.OpenFile;
createDate: System.GreenwichMeanTime;
dot: INTEGER;
ext: Rope.ROPE;
[reply, fh, createDate] ← FileReader.Open[fileName, start, len];
node ← BuildTiogaStructure[reply];
CIFS.Close[fh];
Now put file wide attributes on root node
NodeProps.PutProp[node, $FileExtension,
IF (dot ← Rope.Find[fileName, "."]) > 0
AND
Rope.Size[ext ← Rope.Substr[fileName, dot+1]] > 0
THEN
Atom.MakeAtom[ForceLower[ext]] ELSE $null];
AddCreateDate[node, createDate] };
FromFileC:
PUBLIC
PROC [
file: File.Capability, start: TiogaNode.Offset ← 0, len: TiogaNode.Offset ← PutGet.MaxLen]
RETURNS [node: TiogaNode.Ref] = {
reply: FileReader.Reply;
createDate: System.GreenwichMeanTime;
[reply,createDate] ← FileReader.OpenC[file,start,len];
node ← BuildTiogaStructure[reply];
AddCreateDate[node, createDate];};
AddCreateDate:
PROC [node: TiogaNode.Ref, createDate: System.GreenwichMeanTime] = {
NodeProps.PutProp[node, $FileCreateDate, NEW[System.GreenwichMeanTime ← createDate]] };
FromRope:
PUBLIC
PROC [
rope: Rope.ROPE, start: TiogaNode.Offset ← 0, len: TiogaNode.Offset ← PutGet.MaxLen]
RETURNS [node: TiogaNode.Ref] = {
node ← BuildTiogaStructure[FileReader.FromRope[rope,start,len]] };
FromStream:
PUBLIC
PROC [stream:
IO.Handle, len: TiogaNode.Offset ← PutGet.MaxLen]
RETURNS [node: TiogaNode.Ref] = {
node ← BuildTiogaStructure[FileReader.FromStream[stream,len]] };
BuildTiogaStructure:
PROCEDURE [fileInfo: FileReader.Reply]
RETURNS [node: TiogaNode.Ref] = {
info: CreateNode.SpanInfo;
IF fileInfo.fileType#Tioga2
THEN
node ← BuildT2FromT1[fileInfo.control, fileInfo.comment, fileInfo.text, fileInfo.fileType=Tioga1]
ELSE
IF fileInfo.flags=0C
THEN
-- file contains a non-branch
node ← CreateNode.CreateFromInfo[
fileInfo.fileRope, fileInfo.textStart, fileInfo.textLen, fileInfo.controlStart, fileInfo.controlLen]
ELSE {
-- file contains a branch
info ← NEW[CreateNode.SpanInfoRec];
node ← TiogaNodeOps.NewBranchNode[];
node.internalRepCreated ← FALSE;
node.externalRepValid ← TRUE;
info.externalRepRope ← fileInfo.fileRope;
info.charsStart ← fileInfo.textStart;
info.charsLen ← fileInfo.textLen;
info.ctrlStart ← fileInfo.controlStart;
info.ctrlLen ← fileInfo.controlLen;
NodeProps.PutProp[node, $BranchInfo, info] };
FileReader.FreeReply[fileInfo] };
BuildT2FromT1:
PROC [control, cmmnt, stext: RopeReader.Ref, tiogaFile:
BOOL]
RETURNS [rootBranch: TiogaNode.RefBranchNode] = {
op: T1FileOps.Op;
parentBranch, prevBranch, newBranch: TiogaNode.RefBranchNode;
newText: TiogaNode.RefTextNode;
terminalNode: BOOLEAN;
textLength, runsLength: TiogaNode.Offset ← 0;
typename: NameSymbolTable.Name;
propname: ATOM;
pgf: PGSupport.PGF ← PGSupport.CreatePGF[];
text: REF TEXT ← NEW[TEXT[32]];
GetAtom:
PROC
RETURNS [
ATOM] = {
GetText[]; -- get the print name
RETURN [Atom.MakeAtom[Rope.FromRefText[text]]] };
GetControlRope:
PROC
RETURNS [Rope.
ROPE] = {
Read a rope from the control stream
RETURN [GetRope[ReadLength[], control]] };
GetName:
PROC
RETURNS [NameSymbolTable.Name] = {
GetText[];
RETURN [NameSymbolTable.MakeName[text]] };
GetRope:
PROC [len: TiogaNode.Offset, rdr: RopeReader.Ref]
RETURNS [Rope.
ROPE] = {
rope: Rope.ROPE;
pos: TiogaNode.Offset;
[rope,pos] ← RopeReader.Position[rdr];
RopeReader.SetIndex[rdr,pos+len];
RETURN [RopeEdit.Substr[rope,pos,len]] };
GetText:
PROC = {
len: NAT;
IF (len ← ReadByte[]) > text.maxLength THEN text ← NEW[TEXT[len]];
text.length ← 0;
text.length ← RopeReader.GetString[control, text, len] };
NextOp:
PROC
RETURNS [op: T1FileOps.Op] = {
op ← RopeReader.Get[control
! RopeReader.ReadOffEnd => { op ← T1FileOps.endOfFile; CONTINUE } ]; };
ReadByte:
PROC
RETURNS [Byte] =
INLINE {
RETURN [
LOOPHOLE[ReadChar[]]] };
ReadChar:
PROC
RETURNS [
CHARACTER] = {
RETURN [RopeReader.Get[control]] };
ReadLength:
PROC
RETURNS [TiogaNode.Offset] = {
first, second, fourth: T1FileOps.LengthByte;
third: T1FileOps.ThirdByte;
card: T1FileOps.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]] };
ReadProp:
PROC = {
specs: Rope.ROPE ← GetControlRope[];
value: REF ← NodeProps.DoSpecs[propname, specs, 0, LAST[INT], newBranch];
IF value # NIL THEN NodeProps.PutProp[newBranch, propname, value] };
GraftOnTextNode:
PROCEDURE []
RETURNS [] = {
Attaches a text node to the current branch contents field
newBranch.contents ← newText ←
TiogaNodeOps.NewTextNode[TiogaNode.defaultTextClassID]; -- hang stmt off brnch
newText.next ← newBranch };
-- BODY OF BuildTiogaStructure
terminalNode ← FALSE;
parentBranch ← NIL; -- means need to establish a root node
prevBranch ← NIL; -- means no oldest child yet
op ← NextOp[];
DO
SELECT op
FROM
IN [T1FileOps.terminalTextNodeFirst..T1FileOps.terminalTextNodeLast] => {
leaf node - using earlier format
typename ← PGSupport.RetrieveTypeName[
LOOPHOLE[op-T1FileOps.terminalTextNodeFirst, T1FileOps.TypeIndex], pgf];
terminalNode ← TRUE };
IN [T1FileOps.startNodeFirst..T1FileOps.startNodeLast] => {
branch node - using earlier format
typename ← PGSupport.RetrieveTypeName[
LOOPHOLE[op-T1FileOps.startNodeFirst, T1FileOps.TypeIndex], pgf] };
T1FileOps.endNode => {
end node - pop up a level
IF prevBranch#
NIL
THEN {
prevBranch.last ← TRUE;
prevBranch.next ← parentBranch };
prevBranch ← parentBranch;
IF (parentBranch ← TiogaNodeOps.NarrowToBranchNode[parentBranch.next])=NIL THEN EXIT;
op ← NextOp[];
LOOP };
T1FileOps.startNode => {
branch node - new type of format
typename ← GetName[];
[] ← PGSupport.EnterTypeName[typename,pgf] };
T1FileOps.terminalTextNode => {
leaf node - new type of format
typename ← GetName[];
[] ← PGSupport.EnterTypeName[typename,pgf];
terminalNode ← TRUE };
T1FileOps.rope, T1FileOps.comment => {
text or comments
reader: RopeReader.Ref;
IF newText=NIL THEN GraftOnTextNode[];
IF op=T1FileOps.rope THEN reader ← stext ELSE { reader ← cmmnt; newText.comment ← TRUE };
IF (textLength←ReadLength[]) > 0
THEN
-- get the rope
newText.rope ← GetRope[textLength,reader];
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 };
T1FileOps.runs => {
-- runs, if any, come before corresponding rope
runs of looks
lookRuns: TiogaLooks.Runs;
numRuns: TiogaNode.Offset;
pos: TiogaNode.Offset ← 0;
IF newText=NIL THEN GraftOnTextNode[];
numRuns ← ReadLength[]; -- read number of runs
WHILE numRuns > 0
DO
num: NAT ← TiogaLooksSupport.Short[MIN[numRuns,LAST[NAT]]];
baseRuns: TiogaLooks.BaseRuns ← TiogaLooksSupport.NewBase[num];
len: TiogaNode.Offset ← pos;
numRuns ← numRuns-num;
FOR i:
NAT
IN [0..num)
DO
-- read runs for this baseRuns
looks: TiogaLooks.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 [T1FileOps.looksFirst .. T1FileOps.looksLast] =>
looks ← PGSupport.RetrieveLooks[
LOOPHOLE[op-T1FileOps.looksFirst, T1FileOps.LooksIndex],pgf];
T1FileOps.look1 => ReadLookChars[1];
T1FileOps.look2 => ReadLookChars[2];
T1FileOps.look3 => ReadLookChars[3];
T1FileOps.looks => {
-- read 4 bytes of looks from control stream
lb: TiogaLooksOps.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
TiogaLooksOps.Concat[lookRuns,baseRuns,len,pos-len];
ENDLOOP;
runsLength ← pos; -- for use in checking rope length
newText.runs ← lookRuns;
op ← NextOp[];
LOOP };
T1FileOps.prop => {
node properties - new sort
[] ← PGSupport.EnterProp[propname ← GetAtom[], pgf];
ReadProp;
op ← NextOp[];
LOOP };
T1FileOps.propShort => {
node properties - old sort
propname ← PGSupport.RetrieveProp[LOOPHOLE[ReadByte[], T1FileOps.PropIndex], pgf];
ReadProp;
op ← NextOp[];
LOOP };
T1FileOps.endOfFile => {
end of file
IF parentBranch=NIL THEN EXIT; -- have reached the root
[] ← RopeReader.Backwards[control]; -- backup so read endOfFile again
op ← T1FileOps.endNode;
LOOP };
ENDCASE => ERROR FromFileError;
If reach here, then want to start a new branch node.
newBranch ← TiogaNodeOps.NewBranchNode[];
IF prevBranch#
NIL
THEN {
prevBranch.next ← newBranch;
prevBranch.last ← FALSE;
newBranch.next ← parentBranch;
prevBranch ← newBranch }
ELSE
IF parentBranch#
NIL
THEN {
parentBranch.child ← newBranch;
newBranch.next ← parentBranch;
prevBranch ← newBranch }
ELSE {
parentBranch ← newBranch;
parentBranch.next ← NIL;
prevBranch ← NIL };
newText ← NIL;
newBranch.format ← typename;
IF terminalNode THEN terminalNode ← FALSE -- reset to default case
ELSE { parentBranch ← newBranch; prevBranch ← NIL };
op ← NextOp[];
ENDLOOP;
IF (rootBranch ← prevBranch)=
NIL
THEN
-- don't have a normal tree
IF newBranch=
NIL
THEN
-- null file
rootBranch ← TiogaNodeOps.DocFromNode[TiogaNodeOps.FromRope[NIL]]
ELSE rootBranch ← newBranch;
rootBranch.last ← TRUE;
NodeProps.PutProp[rootBranch, $FromTiogaFile, IF tiogaFile THEN $Yes ELSE $No];
PGSupport.FreePGF[pgf] };
END.