DIRECTORY Atom USING [GetPName], Directory USING [GetProperty], File USING [Capability], FileIO USING [CapabilityFromStream, Open, StreamFromCapability], FileWriter USING [blockSize, BumpWriter, Close, OpenC, Ref, ToRope, ToStream, WriteChar, WriteRope], IO USING [Close, CreateOutputStreamToRope, Error, GetIndex, GetOutputStreamRope, Handle, PutChar, PutRope, SetLength], NameSymbolTable USING [RopeFromName], NodeProps USING [GetProp, PutProp, MapProps, GetSpecs], Process USING [GetPriority, Priority, priorityBackground, SetPriority], PropertyTypes USING [tCreateDate], PutGet, Rope USING [Fetch, Size, Substr], RopeEdit USING [BlankChar, ROPE], RopeIO USING [PutRope, ToFileC], RopeReader USING [FreeRopeReader, GetRopeReader, GetString, Ref, SetPosition], RunReader USING [FreeRunReader, GetRunReader, MergedGet, Ref, SetPosition], System USING [GreenwichMeanTime], T2FileOps USING [addLooksFirst, cmt, comment, default, fmt, format, heavyDuty, IntBytes, lastItem, lastLooksFirst, LengthByte, noClassData, noLooks, Op, prop, runs, startBI, startBranch, startBX, startClassData, startLI, startTI, ThirdByte], TiogaLooks USING [Look, Looks, noLooks, Runs], TiogaLooksOps USING [CountRuns], TiogaBasicClass USING [BasicClass], TiogaItemClass USING [ItemClass], TiogaNode USING [Name, NodeProps, nullName, Offset, Ref, RefBasicNode, RefBoxNode, RefBranchNode, RefItemNode, RefListNode, RefTextNode], TiogaNodeOps USING [BranchContents, FetchBasicClass, FetchItemClass, Forward, NarrowToTextNode, StepForwardNode]; PutFileImpl: CEDAR PROGRAM IMPORTS Directory, Atom, FileIO, FileWriter, IO, NameSymbolTable, NodeProps, Process, Rope, RopeEdit, RopeIO, RopeReader, RunReader, TiogaLooksOps, TiogaNodeOps EXPORTS PutGet SHARES RopeReader, FileWriter = BEGIN OPEN PutGet; ToRope: PUBLIC PROC [node: TiogaNode.RefBranchNode, flatten, textOnly: BOOLEAN _ FALSE] RETURNS [dataLen, count: TiogaNode.Offset, output: RopeEdit.ROPE] = { ctrl, data: FileWriter.Ref; simple: BOOLEAN; [output, simple] _ SimpleFile[node]; IF simple THEN { dataLen _ count _ Rope.Size[output]; RETURN }; [ctrl, data] _ FileWriter.ToRope[]; OutputStructure[ctrl, data, node, flatten]; [dataLen, count, output] _ FileWriter.Close[ctrl, data, textOnly] }; ToStream: PUBLIC PROC [stream: IO.Handle, node: TiogaNode.RefBranchNode, flatten, textOnly: BOOLEAN _ FALSE] RETURNS [dataLen, count: TiogaNode.Offset] = { ctrl, data: FileWriter.Ref; rope: RopeEdit.ROPE; simple: BOOLEAN; [rope, simple] _ SimpleFile[node]; IF simple THEN { RopeIO.PutRope[stream, rope]; dataLen _ count _ Rope.Size[rope]; RETURN }; [ctrl, data] _ FileWriter.ToStream[stream]; OutputStructure[ctrl, data, node, flatten]; [dataLen, count, ] _ FileWriter.Close[ctrl, data, textOnly] }; ToFile: PUBLIC PROC [fileName: RopeEdit.ROPE, node: TiogaNode.RefBranchNode, start: TiogaNode.Offset _ 0, flatten, textOnly: BOOLEAN _ FALSE] RETURNS [dataLen, count: TiogaNode.Offset] = { stream: IO.Handle _ FileIO.Open[fileName, overwrite]; capability: File.Capability _ FileIO.CapabilityFromStream[stream]; IO.Close[stream]; -- need to close first or get 0 length file! [dataLen, count] _ ToFileC[capability, node, start, flatten, textOnly] }; ToFileC: PUBLIC PROC [file: File.Capability, node: TiogaNode.RefBranchNode, start: TiogaNode.Offset _ 0, flatten, textOnly: BOOLEAN _ FALSE] RETURNS [dataLen, count: TiogaNode.Offset] = { ropeToFile: PROC [rope: RopeEdit.ROPE] = { RopeIO.ToFileC[file, rope, start] }; opener: PROC RETURNS [ctrl, data: FileWriter.Ref] = { [ctrl, data] _ FileWriter.OpenC[file, start] }; [dataLen, count] _ FileIt[ropeToFile, opener, node, flatten, textOnly] ; UpdateCreateDate[node, file] }; UpdateCreateDate: PROC [root: TiogaNode.RefBranchNode, file: File.Capability] = TRUSTED { createDate: System.GreenwichMeanTime; prop: REF System.GreenwichMeanTime; Directory.GetProperty[file: file, property: PropertyTypes.tCreateDate, propertyValue: DESCRIPTOR[@createDate, SIZE[System.GreenwichMeanTime]]]; IF (prop _ NARROW[NodeProps.GetProp[root, $FileCreateDate]]) # NIL THEN prop^ _ createDate ELSE NodeProps.PutProp[root, $FileCreateDate, NEW[System.GreenwichMeanTime _ createDate]] }; FileIt: PROC [ropeToFile: PROC [RopeEdit.ROPE], opener: PROC RETURNS [ctrl, data: FileWriter.Ref], node: TiogaNode.RefBranchNode, flatten, textOnly: BOOLEAN] RETURNS [dataLen, count: TiogaNode.Offset] = TRUSTED { ctrl, data: FileWriter.Ref; rope: RopeEdit.ROPE; simple: BOOLEAN; priority: Process.Priority = Process.GetPriority[]; Process.SetPriority[Process.priorityBackground]; [rope, simple] _ SimpleFile[node]; IF simple THEN { ropeToFile[rope]; dataLen _ count _ Rope.Size[rope]; RETURN }; [ctrl, data] _ opener[]; OutputStructure[ctrl, data, node, flatten]; [dataLen, count, ] _ FileWriter.Close[ctrl, data, textOnly]; Process.SetPriority[priority] }; SimpleFile: PROC [root: TiogaNode.RefBranchNode] RETURNS [rope: RopeEdit.ROPE, simple: BOOLEAN] = { SimpleNode: PROC [node: TiogaNode.RefBranchNode] RETURNS [RopeEdit.ROPE, BOOLEAN] = TRUSTED { HasInterestingProp: PROC RETURNS [BOOL] = TRUSTED { Check: PROC [name: ATOM, value: REF] RETURNS [BOOLEAN] = TRUSTED { RETURN [SELECT name FROM $Viewer, $FromTiogaFile, $DocumentLock, $FileCreateDate, $FileExtension => FALSE, ENDCASE => TRUE] }; RETURN [NodeProps.MapProps[node, Check, FALSE, FALSE]] }; IF node.format=TiogaNode.nullName AND ~node.comment AND ~HasInterestingProp[] THEN IF node.contents = NIL THEN RETURN [NIL, TRUE] ELSE WITH node.contents SELECT FROM t:TiogaNode.RefTextNode => IF t.runs=NIL THEN RETURN [t.rope, TRUE] ELSE WITH r:t.runs SELECT FROM base => IF r.length=1 AND r[0].looks=TiogaLooks.noLooks THEN RETURN [t.rope, TRUE]; ENDCASE; ENDCASE; RETURN [NIL, FALSE] }; IF root=NIL THEN RETURN [NIL, TRUE]; [rope, simple] _ SimpleNode[root]; IF ~simple OR root.contents#NIL THEN RETURN[NIL, FALSE]; -- root must not have contents IF root.child=NIL THEN RETURN; -- simple root and no child IF ~root.child.last OR root.child.child # NIL THEN RETURN [NIL, FALSE]; -- more than one child, so not simple [rope, simple] _ SimpleNode[root.child] }; OutputStructure: PROC [ctrl, data: FileWriter.Ref, root: TiogaNode.RefBranchNode, flatten: BOOLEAN] = { CountLookBits: PROC [lks: TiogaLooks.Looks] RETURNS [cnt: NAT] = INLINE { cnt _ 0; FOR c: CHARACTER IN TiogaLooks.Look DO IF lks[c] THEN cnt _ cnt+1; ENDLOOP }; NoOfRuns: PROCEDURE [runs:TiogaLooks.Runs, size:INT] RETURNS [n:INT] = INLINE { IF runs=NIL THEN RETURN [0]; [n, ,] _ TiogaLooksOps.CountRuns[runs, 0, size] }; Output32: PROC [len: TiogaNode.Offset, forwards:BOOLEAN] = { first, second, fourth: T2FileOps.LengthByte; third: T2FileOps.ThirdByte; lenBytes: T2FileOps.IntBytes _ LOOPHOLE[len]; IF lenBytes.fourth # 0 THEN { fourth.data _ lenBytes.fourth; first.others _ second.others _ third.others _ TRUE }; IF lenBytes.thirdTop # 0 OR lenBytes.thirdBottom # 0 THEN { third.dataTop _ lenBytes.thirdTop; third.dataBottom _ lenBytes.thirdBottom; first.others _ second.others _ TRUE }; IF lenBytes.second # 0 THEN { second.data _ lenBytes.second; first.others _ TRUE }; first.data _ lenBytes.first; IF forwards THEN { WriteChar[LOOPHOLE[first], ctrl]; IF first.others THEN { WriteChar[LOOPHOLE[second], ctrl]; IF second.others THEN { WriteChar[LOOPHOLE[third], ctrl]; IF third.others THEN { WriteChar[LOOPHOLE[fourth], ctrl] }}} } ELSE { IF first.others THEN { IF second.others THEN { IF third.others THEN WriteChar[LOOPHOLE[fourth], ctrl]; WriteChar[LOOPHOLE[third], ctrl]; }; WriteChar[LOOPHOLE[second], ctrl]; }; WriteChar[LOOPHOLE[first], ctrl]; }; }; OutputAtom: PROC [atom: ATOM] = INLINE { OutputSymbolTableIndex[Atom.GetPName[atom]] }; OutputB32: PROCEDURE [len: TiogaNode.Offset] = INLINE { Output32[len, FALSE]; }; OutputBranch: PROCEDURE [branch:TiogaNode.RefBranchNode] = { IsSimple: PROCEDURE [branch:TiogaNode.RefBranchNode] RETURNS [BOOLEAN] = { IF branch.comment THEN RETURN [FALSE]; IF branch.props#NIL THEN RETURN [FALSE]; IF branch.format#TiogaNode.nullName THEN RETURN [FALSE]; IF branch.child#NIL THEN RETURN [FALSE]; WITH branch.contents SELECT FROM t:TiogaNode.RefTextNode => { classInfo:TiogaItemClass.ItemClass _ TiogaNodeOps.FetchItemClass[t.class]; IF classInfo=NIL THEN ERROR; IF ~t.last THEN RETURN [FALSE]; IF t.comment THEN RETURN [FALSE]; IF t.props#NIL THEN RETURN [FALSE]; IF t.format#TiogaNode.nullName THEN RETURN [FALSE]; IF classInfo.flavor#$Text THEN RETURN [FALSE]; IF t.runs#NIL THEN RETURN[FALSE]; RETURN[TRUE]; }; ENDCASE => RETURN [FALSE]; }; IF branch.externalRepValid THEN { WriteRope: PROC [r: RopeEdit.ROPE, size: LONG INTEGER, writer: FileWriter.Ref, reader: RopeReader.Ref, start: LONG INTEGER_0] = { WHILE size>0 DO FileWriter.WriteRope[r, MIN[size, LAST[NAT]], writer, reader, start]; size _ size - LAST[NAT]; start _ start + LAST[NAT]; ENDLOOP}; brInfo:PutGet.SpanInfo _ NARROW[NARROW[NodeProps.GetProp[branch, $BranchInfo]]]; WriteRope[brInfo.externalRepRope, brInfo.charsLen, data, externRopeReader, brInfo.charsStart]; WriteRope[brInfo.externalRepRope, brInfo.ctrlLen, ctrl, externRopeReader, brInfo.ctrlStart]; } ELSE { IF branch.deleted THEN ERROR; IF IsSimple[branch] THEN { WITH branch.contents SELECT FROM t:TiogaNode.RefTextNode => IF Rope.Size[t.rope]#0 THEN OutputRope[t.rope, data]; ENDCASE => ERROR; -- shouldn't happen for simple nodes } ELSE { myCurrentBlock:OffsetBlockRef; myNextFreeCell:INTEGER; IF branch.comment THEN FileWriter.WriteChar[T2FileOps.comment, ctrl]; -- COMMENT IF branch.props # NIL THEN { node _ branch; -- make node available for property specs proc [] _ NodeProps.MapProps[branch, OutputProperty, FALSE, FALSE] }; -- PROPERTIES IF branch.format#TiogaNode.nullName THEN OutPutFormat[branch.format]; -- FORMAT IF branch.contents # NIL THEN OutputItemList[branch.contents]; -- CONTENTS FileWriter.WriteChar[T2FileOps.lastItem, ctrl]; -- LAST ITEM myCurrentBlock _ currentBlock; -- mark offset stream myNextFreeCell _ nextFreeCell; IF branch.child # NIL THEN -- CHILDREN FOR b:TiogaNode.RefBranchNode _ branch.child, NARROW[b.next] DO SaveStreamOffsets[]; OutputBranch[b]; IF b.last THEN EXIT; ENDLOOP; OutputHK[myCurrentBlock, myNextFreeCell]; -- CHILD H.K. }; }; }; OutputContents: PROCEDURE [c:TiogaNode.Ref] = { OutputItemList[ref:c, contentMode:TRUE]; }; OutputControlRope: PROC [r: RopeEdit.ROPE] = INLINE { OutputF32[Rope.Size[r]]; FileWriter.WriteRope[r, Rope.Size[r], ctrl, externRopeReader]; }; OutputCountRopeCR: PROC [r: RopeEdit.ROPE, writer: FileWriter.Ref] = INLINE { OutputF32[Rope.Size[r]]; OutputRope[r, writer]; }; OutputF32: PROCEDURE [len: TiogaNode.Offset] = INLINE { Output32[len, TRUE]; }; OutPutFormat: PROCEDURE [format:TiogaNode.Name] = { WriteChar[T2FileOps.format, ctrl]; OutputSymbolTableIndex[NameSymbolTable.RopeFromName[format]]; }; OutputYUKBranch: PROCEDURE [br:TiogaNode.RefBranchNode] = { startData:LONG CARDINAL; saveCtrl:FileWriter.Ref _ ctrl; -- remember the control stream dummyData:FileWriter.Ref; controlRope:RopeEdit.ROPE; controlRopeSize:LONG CARDINAL; startData _ data.blockCount*LONG[FileWriter.blockSize]+data.block.length; [ctrl, dummyData] _ FileWriter.ToRope[]; -- puts two NULLS at ctrl stream head - damn it OutputBranch[br]; -- BRANCH INFO controlRopeSize _ (ctrl.blockCount*LONG[FileWriter.blockSize]+ctrl.block.length); [ , , controlRope] _ FileWriter.Close[ctrl, dummyData, FALSE]; -- NB writes unreqd. junk ctrl _ saveCtrl; -- reset control stream WriteChar[T2FileOps.startBranch, ctrl]; -- LABELLED BRANCH TOKEN OutputF32[(data.blockCount*LONG[FileWriter.blockSize]+data.block.length) - startData]; OutputControlRope[Rope.Substr[controlRope, 2, controlRopeSize-2]]; }; OutputItemList: PROCEDURE [ref:TiogaNode.Ref, contentMode:BOOLEAN_FALSE] = { IF ref#NIL THEN DO WITH ref SELECT FROM t:TiogaNode.RefTextNode => OutputTI[t]; br:TiogaNode.RefBranchNode => OutputYUKBranch[br]; ENDCASE => OutputOther[ref, contentMode]; IF ref.last THEN RETURN ELSE ref _ NARROW[ref.next]; ENDLOOP }; OutputProperty: PROC [name: ATOM, value: REF] RETURNS [BOOLEAN] = { specs: RopeEdit.ROPE _ NodeProps.GetSpecs[name, value, node]; -- node set by caller IF specs=NIL THEN RETURN [FALSE]; WriteChar[T2FileOps.prop, ctrl]; OutputAtom[name]; OutputControlRope[specs]; RETURN [FALSE] }; OutputRope: PROC [r: RopeEdit.ROPE, writer: FileWriter.Ref] = INLINE { FileWriter.WriteRope[r, Rope.Size[r], writer, externRopeReader]; WriteChar[15C, writer] }; OutputRuns: PROCEDURE [runs:TiogaLooks.Runs, noOfRuns: TiogaNode.Offset] RETURNS [loc:TiogaNode.Offset _ 0] = { cnt: TiogaNode.Offset _ 0; RunReader.SetPosition[runReader, runs, 0]; OutputF32[noOfRuns]; WHILE (cnt_cnt+1) <= noOfRuns DO -- Process each run descriptor looks: TiogaLooks.Looks; len: TiogaNode.Offset; noOfLookBitsToProcess:INT; lookIx:CARDINAL _ 0; [len, looks] _ RunReader.MergedGet[runReader]; noOfLookBitsToProcess _ CountLookBits[looks]; IF noOfLookBitsToProcess>0 THEN { -- ** lookSeq WHILE noOfLookBitsToProcess>1 DO UNTIL looks[lookIx+FIRST[TiogaLooks.Look]] DO -- skip unset bits lookIx _ lookIx + 1; ENDLOOP; WriteChar[T2FileOps.addLooksFirst+lookIx, ctrl]; noOfLookBitsToProcess _ noOfLookBitsToProcess - 1; lookIx _ lookIx + 1; -- continue search with following bit ENDLOOP; UNTIL looks[lookIx+FIRST[TiogaLooks.Look]] DO -- skip unset bits lookIx _ lookIx + 1; ENDLOOP; WriteChar[T2FileOps.lastLooksFirst+lookIx, ctrl]; } ELSE WriteChar[T2FileOps.noLooks, ctrl]; OutputF32[len]; loc _ loc+len; ENDLOOP; }; OutputSymbolTableIndex: PROCEDURE [rope:RopeEdit.ROPE] = INLINE { OutputControlRope[rope]; -- dummy writes rope rather tan ST index }; OutputTI: PROCEDURE [t:TiogaNode.RefTextNode] = { HeavyDuty: PROCEDURE [] RETURNS [BOOLEAN] = INLINE { RETURN [t.props#NIL] }; ropeSize: TiogaNode.Offset _ Rope.Size[t.rope]; noOfRuns: TiogaNode.Offset _ NoOfRuns[t.runs, ropeSize]; token:T2FileOps.Op _ T2FileOps.startTI; -- build item type token here doFormat, doHeavyDuty, doRuns:BOOLEAN _ FALSE; doClass:BOOLEAN _ TRUE; classInfo: TiogaItemClass.ItemClass _ TiogaNodeOps.FetchItemClass[t.class]; IF HeavyDuty[] THEN { token _ token + T2FileOps.heavyDuty; doHeavyDuty _ TRUE; }; IF classInfo.flavor=$Text THEN { doClass_FALSE; token _ token + T2FileOps.default; }; IF t.format#TiogaNode.nullName THEN { token _ token + T2FileOps.fmt; doFormat _ TRUE; }; IF noOfRuns #0 THEN { doRuns _ TRUE; token _ token + T2FileOps.runs; }; IF t.comment THEN token _ token + T2FileOps.cmt; WriteChar[token, ctrl]; -- ITEM TOKEN IF doHeavyDuty THEN { -- PROPERTIES and CONTENTS node _ t; -- make node available for property specs proc [] _ NodeProps.MapProps[t, OutputProperty, FALSE, FALSE]; WriteChar[T2FileOps.noClassData, ctrl]; -- property terminator }; IF doClass THEN OutputAtom[classInfo.flavor]; -- CLASS ATOM IF doRuns THEN -- RUNS IF OutputRuns[t.runs, noOfRuns] # ropeSize THEN ERROR; -- sum of looks < rope length IF doFormat THEN -- FORMAT OutputSymbolTableIndex[NameSymbolTable.RopeFromName[t.format]]; OutputCountRopeCR[t.rope, IF t.comment THEN ctrl ELSE data]; -- TEXT }; OutputOther: PROCEDURE [t:TiogaNode.Ref, contentMode:BOOLEAN] = { doFormat, doHeavyDuty, doRuns:BOOLEAN _ FALSE; doClass, doContents:BOOLEAN _ TRUE; token:T2FileOps.Op; -- build item type token here flavor: ATOM; -- save item flavor here to avoid second type discrimination classData: RopeEdit.ROPE; contents:TiogaNode.Ref; WITH t SELECT FROM bs:TiogaNode.RefBasicNode => { classInfo: TiogaBasicClass.BasicClass _ TiogaNodeOps.FetchBasicClass[bs.class]; IF ~contentMode THEN ERROR; -- basic node only allowed as contents token _ T2FileOps.startBI; IF bs.props#NIL THEN { token _ token + T2FileOps.heavyDuty; doHeavyDuty _ TRUE; }; IF (flavor _ classInfo.flavor) = $Basic THEN { doClass_FALSE; token _ token + T2FileOps.default; }; IF classInfo.get#NIL AND bs.data#NIL THEN classData _ NARROW[classInfo.get[bs, $Save]]; -- ELSE classData_NIL doContents _ FALSE; }; bx:TiogaNode.RefBoxNode => { classInfo: TiogaItemClass.ItemClass _ TiogaNodeOps.FetchItemClass[bx.class]; token _ T2FileOps.startBX; IF bx.contents#NIL OR bx.props#NIL THEN { token _ token + T2FileOps.heavyDuty; doHeavyDuty _ TRUE; }; IF (flavor _ classInfo.flavor) = $Box THEN { doClass_FALSE; token _ token + T2FileOps.default; }; IF classInfo.get#NIL AND bx.data#NIL THEN classData _ NARROW[classInfo.get[bx, $Save]]; -- ELSE classData_NIL contents _ bx.contents; }; li:TiogaNode.RefListNode => { classInfo: TiogaItemClass.ItemClass _ TiogaNodeOps.FetchItemClass[li.class]; token _ T2FileOps.startLI; IF li.contents#NIL OR li.props#NIL THEN { token _ token + T2FileOps.heavyDuty; doHeavyDuty _ TRUE; }; IF (flavor _ classInfo.flavor) = $List THEN { doClass_FALSE; token _ token + T2FileOps.default; }; IF classInfo.get#NIL AND li.data#NIL THEN classData _ NARROW[classInfo.get[li, $Save]]; -- ELSE classData_NIL contents _ li.contents; }; ENDCASE => ERROR; IF t.format#TiogaNode.nullName THEN { token _ token + T2FileOps.fmt; doFormat _ TRUE; }; IF t.comment THEN token _ token + T2FileOps.cmt; WriteChar[token, ctrl]; -- ITEM TOKEN IF doHeavyDuty THEN { -- PROPERTIES and CONTENTS [] _ NodeProps.MapProps[t, OutputProperty, FALSE, FALSE]; IF doContents THEN OutputContents[contents]; -- basics have no conts. }; IF classData=NIL THEN WriteChar[T2FileOps.noClassData, ctrl] -- TERMINATOR FOR CONTS ELSE { WriteChar[T2FileOps.startClassData, ctrl]; OutputControlRope[classData]; }; IF doClass THEN OutputAtom[flavor]; -- CLASS ATOM IF doFormat THEN -- FORMAT OutputSymbolTableIndex[NameSymbolTable.RopeFromName[t.format]]; }; WriteChar: PROC [c: CHARACTER, writer: FileWriter.Ref] = INLINE { FileWriter.WriteChar[c, writer] }; NoOfPairs: CARDINAL = 63; OffsetPair: TYPE = RECORD [c, d:LONG CARDINAL]; -- 8 bytes OffsetBlockRef: TYPE = REF OffsetBlock; OffsetBlock: TYPE = RECORD [ next, prev: OffsetBlockRef _ NIL, -- 8 bytes pair: ARRAY [0..NoOfPairs) OF OffsetPair ]; offsetStreamHead: OffsetBlockRef _ NEW[OffsetBlock]; currentBlock: OffsetBlockRef _ offsetStreamHead; nextFreeCell: INTEGER _ 0; node: TiogaNode.Ref; SaveStreamOffsets: PROCEDURE [] = { currentBlock.pair[nextFreeCell].c _ ctrl.blockCount*LONG[FileWriter.blockSize]+ctrl.block.length; currentBlock.pair[nextFreeCell].d _ data.blockCount*LONG[FileWriter.blockSize]+data.block.length; nextFreeCell _ nextFreeCell + 1; IF nextFreeCell >= NoOfPairs THEN { IF currentBlock.next=NIL THEN { currentBlock.next _ NEW[OffsetBlock]; currentBlock.next.prev _ currentBlock; }; currentBlock _ currentBlock.next; nextFreeCell _ 0; }; }; OutputHK: PROCEDURE [b: OffsetBlockRef, n:INTEGER] = { nSibs: CARDINAL _ 0; this: OffsetPair; c: LONG CARDINAL _ ctrl.blockCount*LONG[FileWriter.blockSize]+ctrl.block.length; d: LONG CARDINAL _ data.blockCount*LONG[FileWriter.blockSize]+data.block.length; UNTIL currentBlock=b AND nextFreeCell=n DO IF nextFreeCell = 0 THEN { -- decrement pair pointer currentBlock _ currentBlock.prev; nextFreeCell _ NoOfPairs-1; IF currentBlock=NIL THEN ERROR; -- unexpected end of chain } ELSE nextFreeCell _ nextFreeCell - 1; this _ currentBlock.pair[nextFreeCell]; OutputB32[c-this.c]; OutputB32[d-this.d]; c _ this.c; d _ this.d; nSibs _ nSibs + 1; ENDLOOP; OutputB32[nSibs]; }; externRopeReader: RopeReader.Ref _ RopeReader.GetRopeReader[]; runReader: RunReader.Ref _ RunReader.GetRunReader[]; OutputBranch[root]; RunReader.FreeRunReader[runReader]; RopeReader.FreeRopeReader[externRopeReader]; }; WriteMesaFilePlain: PUBLIC PROC [fileName: RopeEdit.ROPE, root: TiogaNode.RefBranchNode]={ h: IO.Handle _ FileIO.Open[fileName: fileName, accessOptions: overwrite]; WritePlain[h, root, TRUE]; IO.Close[h] }; WriteFilePlain: PUBLIC PROC [fileName: RopeEdit.ROPE, root: TiogaNode.RefBranchNode] = { h: IO.Handle _ FileIO.Open[fileName: fileName, accessOptions: overwrite]; WritePlain[h, root]; IO.Close[h] }; WriteFileCPlain: PUBLIC PROC [file: File.Capability, root: TiogaNode.RefBranchNode] = { h: IO.Handle _ FileIO.StreamFromCapability[file]; WritePlain[h, root]; IO.Close[h] }; WriteRopePlain: PUBLIC PROC [root: TiogaNode.RefBranchNode] RETURNS [output: RopeEdit.ROPE] = { h: IO.Handle _ IO.CreateOutputStreamToRope[]; WritePlain[h, root]; RETURN [IO.GetOutputStreamRope[h]] }; WritePlain: PROC [h: IO.Handle, root: TiogaNode.RefBranchNode, restoreDashes: BOOL _ FALSE] = { HasInitialDashes: PROC [r: RopeEdit.ROPE] RETURNS [BOOL] = { loc: INT _ 0; size: INT = Rope.Size[r]; c: CHAR; WHILE loc < size AND RopeEdit.BlankChar[c _ Rope.Fetch[r, loc]] DO loc _ loc+1; ENDLOOP; IF loc > size OR c # '- OR Rope.Fetch[r, loc+1] # '- THEN RETURN [FALSE]; RETURN [TRUE] }; br: TiogaNode.RefBranchNode _ root; node: TiogaNode.Ref; level: INTEGER _ 0; levelDelta: INTEGER; first: BOOL _ TRUE; DO firstItem: BOOL _ TRUE; [br, levelDelta] _ TiogaNodeOps.Forward[br]; IF br=NIL THEN EXIT; IF first THEN first _ FALSE ELSE IO.PutChar[h, '\n]; -- carriage returns between nodes level _ level+levelDelta; IF (node _ TiogaNodeOps.BranchContents[br])=NIL THEN LOOP; THROUGH [1..level) DO IO.PutChar[h, '\t]; ENDLOOP; -- output level-1 tabs DO -- output the contents of the node text: TiogaNode.RefTextNode; IF (text _ TiogaNodeOps.NarrowToTextNode[node]) # NIL THEN { IF firstItem AND restoreDashes AND text.comment AND ~HasInitialDashes[text.rope] THEN IO.PutRope[h, "-- "]; -- restore the leading dashes for Mesa comments IO.PutRope[h, text.rope] }; firstItem _ FALSE; IF node.last AND node.next=br THEN EXIT; -- finished with the branch contents node _ TiogaNodeOps.StepForwardNode[node]; ENDLOOP; ENDLOOP; { ENABLE IO.Error => IF ec = NotImplementedForThisStream THEN GOTO Exit; IO.SetLength[h, IO.GetIndex[h]] } EXITS Exit => RETURN }; END. PutFileImpl.Mesa written by Paxton. May 1981 Last Edited by: Lamming, June 9, 1983 3:41 pm When add a new "system" property that should not go on files, add registration at end of TEditDocuments2Impl so will not copy/write the property. root simple, but what about its child? NB rope must be NIL here Count how many looks are set Counts the number of runs in a run sequence The bytes are in the right place now write them out in the appropriate order Output an ATOM BACKWARD32 ::= 1..4 RECORD [more:BOOL, data:[0..127]] control ::= [branch | cheapBranch] ; branch ::= optCmt {prop} optFormat {item} {child} {childHK} nchild ; cheapBranch ::= NULL ; A cheapBranch has no properties, format, children, comment or runs at either branch or item level in the structure. It is distinguished by the fact that it has no control information. If the data length is also zero then the branch is completely empty. If the data field is not empty, then the data forms the text of a default text item. Only bog-standard terminal text nodes are simple If the external rep is still valid then just blatt out the orginal data and ctrl ropes unchanged terminal text branches simply just produce a the rope and no more currentBlock _ myCurrentBlock; -- reset offset stream to mark nextFreeCell _ myNextFreeCell; contents ::= [ labelledBranch | item | basic ] ; labelledBranch ::= startBranch dataL controlL branch dataL ::= CONTROLINT ; controlLength ::= CONTROLINT ; basic ::= [basic | basicCmt ] ; basic ::= startBI classAtom {prop} optFormat {contents} classData ; basicCmt ::= startBICmt classAtom {prop} optFormat {contents} classData ; Write a rope onto the control stream preceeded by its length Output a rope onto a given stream, preceeded its length and followed by a CR FORWARD32 ::= 1..4 RECORD [more:BOOL, data:[0..127]] optFormat ::= [(format symbolTableIndex) | NULL ] ; Output one of these bloody labelled branches labelledBranch ::= startBranch dataL controlL branch dataL ::= FORWARD32 ; controlLength ::= FORWARD32 ; Change the control output to write to a rope. When done, discard any HK info., measure its length. Reconnect the control stream and write out the information. item ::= [defaultTextItem | textItem | textItemHeavyDuty | listItem | boxItem] ; prop ::= prop propName specs -- as per Tioga 1 propName ::= symbolTableIndex ; symbolTableIndex ::= FORWARD32 ; specs ::= specsLength specsRope ; specsLength ::= FORWARD32 ; specsRope ::= {CHAR}; Output a rope to the given stream followed by a CR and preceeded by the rope's length runs ::= noOfRuns {run} ; noOfRuns ::= FORWARD32 ; run ::= [lookSeq| noLooks] runLength ; lookSeq ::= {addlooks} lastLooks addlooks ::= BYTE [addLooksFirst..addLooksLast] ; lastLooks ::= BYTE [lastLooksFirst..lastLooksLast] ; runLength ::= FORWARD32 ; ** noOfRuns ** {run} ** {addlooks} ** lastLooks ** noLooks ** runLength Enter a rope into the symbol table and output its index defaultTextItem ::= [dtiFormat | dtiNoFormat]; These are text items without properties or contents but with class information = $TEXT? dtiFormat ::= [dtiF | dtiCmtF | dtiRunsF | dtiCmtRunsF] ; dtiNoFormat ::= [dti | dtiCmt | dtiRuns | dtiCmtRuns] ; textItem ::= [tiFormat | tiNoFormat] ; These are text items without propertiesor contents but with class information tiFormat ::= [tiF | tiCmtF | tiRunsF | tiCmtRunsF] tiNoFormat ::= [ti | tiCmt | tiRuns | tiCmtRuns] textItemHeavyDuty ::= [tiHDFormat | tiHDNoFormat] ; tiHDFormat ::= [tiHDF | tiCmtHDF | tiRunsHDF | tiCmtRunsHDF] These are text items with class information and properties and/or contents tiHDNoFormat ::= [tiHD | tiCmtHD | tiRunsHD | tiCmtRunsHD] These are text items with class information and properties and/or contents tiCmtRunsHD ::= startTICmtRunsHD {prop} noClassData classAtom runs textLength {CHAR} ; tests for properties or contents on an item Method: SCANNER inspects the node to determine what item token to emit and sets flags to steer the WRITER section ** SCANNER ** Base token is DefaultTextItem, TextItem or TextItemHeavyDuty ** WRITER ** Method: SCANNER inspects the node to determine what item token to emit and sets flags to steer the WRITER section ** SCANNER ** ** WRITER ** output class data - mandatory ----------------------------------------------------- OFFSET STREAM MANAGEMENT This section contains procedures for creating, writing and reading the offset stream. An offset stream is a linked list of blocks containing offsets - pairs of values of the form (ctrl stream offset, data stream offset). These values are used to compute the child node lengths which are output at the end of a branch. Get the current writer positions for the ctrl and data writers and save them on the offset stream. If last block of the stream is full, chain on another. Output the information which describes where each child's text and control ropes start ΚΉ– "cedar" style˜Jšœ™Jšœ™J™-J™šΟk ˜ Jšœ œ ˜Jšœ œ˜#Jšœ œ˜Jšœ œ5˜FJšœ œU˜hJšœœo˜}Jšœœ˜'Jšœ œ)˜;Jšœ œ;˜LJšœœ˜%Jšœ˜Jšœœ˜'Jšœ œ œ˜&Jšœ œ˜%Jšœ œ?˜RJšœ œ=˜OJšœ œ˜'Jšœ œβ˜τJšœ œ˜2Jšœœ˜#Jšœœ˜&Jšœœ˜$Jšœ œz˜ŒJšœ œ_˜s—J˜šœ  ˜Jšœ&œq˜ Jšœ˜Jšœ˜J˜—Jšœœ˜J˜š Οnœœœ4œœ˜WJšœ5œ˜EJ˜Jšœœ˜Jšœ$˜$Jšœœ(œ˜?J˜#J˜+J˜AJ˜J˜—š žœœœ œ;œœ˜lJšœ'˜.J˜Jšœœ˜Jšœœ˜J˜"šœœ˜J˜J˜"Jšœ˜ —J˜+J˜+J˜;J˜J˜—š žœœœœQœœ˜Jšœ'˜.J˜Jšœœ+˜5JšœB˜BJšœΟc,˜>JšœF˜FJšœ˜J˜—š žœœœhœœœ'˜»J˜šœ œœ˜*Jšœ!˜!Jšœ˜—šœœœ!˜5J˜,J˜—J˜J˜HJ˜J˜J˜—šžœœ:œ˜YJšœ%˜%Jšœœ˜#šœG˜GJšœ œœ˜H—Jšœ œ.œœ˜ZJšœ*œ(˜YJšœ˜J˜—šžœœœ œ œœQœœ&œ˜ΤJ˜J˜Jšœœ˜Jšœœ˜J˜3J˜0J˜"šœœ˜J˜J˜"Jšœ˜ —J˜J˜+J˜˜>J˜—J˜šžœœœœ˜MJ™LJšœ˜J˜J˜—J˜šž œ œœ˜7Jšœ5™5Jšœœ˜J˜—J˜šž œ œ˜3JšœžΟbœœ™3Jšœ#˜#Jšœ=˜=J˜—J˜šžœ œ!˜;J™,šœž œ™4Jšœ  œ™Jšœ œ™—J™Jšœ œœ˜Jšœ Ÿ˜>Jšœ˜Jšœœ˜Jšœœœ˜J˜J™žJšœœ)˜IJšœ)Ÿ/˜XJšœŸ˜&Jšœ#œ*˜QJšœ7œŸ˜XJšœŸ˜0Jšœ(Ÿ˜@Jšœœ7Ÿ˜WJšœB˜BJ˜J˜—šžœ œ!œœ˜LJšœP™Pšœœœ˜š˜šœœ˜Jšœ(˜(Jšœ3˜3Jšœ"˜)—Jšœ œ˜Jšœœ ˜Jš˜——J˜—J˜š žœœœ œœœ˜Cšœ  œΟe™/šœ™Jšœ œ™ —šœ#™#Jšœ™Jšœ™J™——Jšœœ*Ÿ˜SJš œœœœœ˜!Jšœ!˜!J˜Jšœ˜Jšœœ˜Jšœ˜—J˜šž œœœœ˜FJ™UJ˜@J˜J˜—J˜šž œ œ4œ˜o™Jšœ™šœžœ ™&šœ ™ Jšœž œž œ™1Jšœžœž œ™4—Jšœ™J™——Jšœ˜J˜*Jšœ ™ Jšœ˜J™šœœŸ˜?J˜Jšœ˜Jšœœ˜Jšœœ˜J˜.Jšœ-˜-šœœŸ ˜/Jšœ ™ šœ˜ šœœœŸ˜@Jšœ˜Jšœ˜—Jšœ0˜0Jšœ2˜2JšœŸ%˜:Jšœ˜—Jšœ ™ šœœœŸ˜@Jšœ˜Jšœ˜—Jšœ1˜1J˜—š˜Jšœž™ Jšœ#˜#—Jšœ ™ Jšœ˜J˜Jšœ˜J˜——J˜šžœ œœœ˜AJ™7JšœŸ(˜AJ˜—J˜šžœ œ˜1Jšœ.™.™WJšœ9™9Jšœ7™7—Jšœ&™&™MJšœ2™2Jšœ0™0—šœ3™3šœ<™J˜—šœœ˜JšœŸ˜7—šœœŸ˜(šœ)˜/JšœŸ˜%——šœ œŸ˜*Jšœ?˜?—Jšœœ œœŸ˜DJ˜J˜—šž œ œœ˜AJ˜Jšœœ˜/Jšœœ˜#JšœŸ˜1JšœœŸ<˜JJšœœ˜Jšœ˜J˜J˜J™qJ™Jšœ ™ šœœ˜šœ˜JšœO˜OJšœœœŸ&˜BJšœ˜šœ  ˜Jšœ$˜$Jšœœ˜J˜—šœ%œ˜.Jšœœ˜Jšœ"˜"J˜—šœœœ ˜)Jšœ œŸ˜C—Jšœ œ˜J˜—šœ˜JšœL˜LJšœ˜šœ œœ  ˜)Jšœ$˜$Jšœœ˜J˜—šœ#œ˜,Jšœœ˜Jšœ"˜"Jšœ˜—šœœœ ˜)Jšœ œŸ˜D—J˜J˜—šœ˜JšœL˜LJšœ˜šœ œœ  ˜)Jšœ$˜$Jšœœ˜J˜—šœ$œ˜-Jšœœ˜Jšœ"˜"J˜—šœœœ ˜)Jšœ œŸ˜C—Jšœ˜J˜—Jšœœ˜—J˜J˜šœœ˜&Jšœ˜Jšœ œ˜J˜J˜—šœ ˜Jšœ˜J˜—J™ JšœŸ˜3šœ œŸ)˜?Jšœ+œœ˜9Jšœ œŸ˜EJ˜—J™šœ œ˜Jšœ'Ÿ˜E—šœ˜Jšœ*˜*Jšœ˜J˜—šœœ˜JšœŸ˜/—šœ œŸ˜*Jšœ?˜?—J˜—J˜šž œœ œœ˜AJšœ˜Jšœ˜—J˜˜J˜—Jšœ5™5J™ΨJ˜Jšœ œ˜Jš œ œœœœŸ ˜:Jšœœœ ˜'šœ œœ˜JšœœŸ ˜,Jšœœœ ˜(J˜J˜—Jšœ#œ˜4Jšœ0˜0Jšœœ˜J˜J˜šžœ œ˜#J™šJšœ4œ)˜aJšœ4œ)˜aJšœ ˜ šœœ˜#šœœœ˜Jšœœ˜%Jšœ&˜&J˜—Jšœ!˜!Jšœ˜J˜—J˜J˜—šžœ œœ˜6J™VJšœœ˜Jšœ˜Jšœœœœ)˜PJšœœœœ)˜PJ˜šœœ˜*šœœŸ˜4Jšœ!˜!Jšœ˜Jš œœœœŸ˜;J˜—š˜Jšœ ˜ —Jšœ'˜'Jšœ˜Jšœ˜Jšœ˜J˜Jšœ˜—Jšœ˜J˜J˜—J˜>J˜4Jšœ˜J˜#J˜,šœ˜J™——šžœœœœ"˜ZJšœœD˜IJšœœ˜Jšœ ˜ Jšœ˜J˜—šžœœœœ$˜XJšœœD˜IJ˜Jšœ ˜ Jšœ˜J˜—šžœœœ;˜WJšœœ,˜1J˜Jšœ ˜ Jšœ˜J˜—š žœœœ!œœ˜_Jšœœ œ˜-J˜Jšœœ˜"Jšœ˜J˜—š ž œœœ7œœ˜_š žœœœœœ˜