<> <> <> 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]; <> 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] = { <> 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 [] = { <> 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] => { <> typename _ PGSupport.RetrieveTypeName[ LOOPHOLE[op-T1FileOps.terminalTextNodeFirst, T1FileOps.TypeIndex], pgf]; terminalNode _ TRUE }; IN [T1FileOps.startNodeFirst..T1FileOps.startNodeLast] => { <> typename _ PGSupport.RetrieveTypeName[ LOOPHOLE[op-T1FileOps.startNodeFirst, T1FileOps.TypeIndex], pgf] }; T1FileOps.endNode => { <> 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 => { <> typename _ GetName[]; [] _ PGSupport.EnterTypeName[typename,pgf] }; T1FileOps.terminalTextNode => { <> typename _ GetName[]; [] _ PGSupport.EnterTypeName[typename,pgf]; terminalNode _ TRUE }; T1FileOps.rope, T1FileOps.comment => { <> 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 <> 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 => { <> [] _ PGSupport.EnterProp[propname _ GetAtom[], pgf]; ReadProp; op _ NextOp[]; LOOP }; T1FileOps.propShort => { <> propname _ PGSupport.RetrieveProp[LOOPHOLE[ReadByte[], T1FileOps.PropIndex], pgf]; ReadProp; op _ NextOp[]; LOOP }; T1FileOps.endOfFile => { <> IF parentBranch=NIL THEN EXIT; -- have reached the root [] _ RopeReader.Backwards[control]; -- backup so read endOfFile again op _ T1FileOps.endNode; LOOP }; ENDCASE => ERROR FromFileError; <> 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.