<> <> <> <> <> <> DIRECTORY Atom USING [GetPName], CedarProcess USING [Priority, DoWithPriority], FS USING [Create, OpenFile, StreamFromOpenFile], IO USING [Close, Error, GetIndex, noWhereStream, PutChar, PutRope, RopeFromROS, ROS, SetIndex, SetLength, STREAM], PGSupport, Rope USING [Balance, Fetch, Flatten, ROPE, Size, SkipTo], RopeEdit USING [BlankChar], RopeReader USING [FreeRopeReader, GetRopeReader, Ref], RunReader USING [FreeRunReader, GetRunReader, MergedGet, Ref, SetPosition], TextLooks USING [CountRuns, Flatten, Look, Looks, LooksBytes, noLooks, Runs], Tioga USING [FirstChild, Forward, GetFormat, IsLastSibling, MapProps, Next, Node, Parent, Size, WriteProp], TiogaFile USING [comment, commentHeaderId, controlHeaderId, controlTrailerId, endNode, endOfFile, endSize, FileId, fileIdSize, FormatIndex, look1, look2, look3, looks, looksFirst, LooksIndex, prop, PropIndex, propShort, rope, runs, startNode, startNodeFirst, terminalTextNode, terminalTextNodeFirst, trailerLengthSize], UserProfile USING [Number]; PutFileImpl: CEDAR PROGRAM IMPORTS Atom, CedarProcess, FS, IO, PGSupport, Rope, RopeEdit, RopeReader, RunReader, TextLooks, Tioga, UserProfile EXPORTS Tioga = BEGIN ROPE: TYPE = Rope.ROPE; Runs: TYPE = TextLooks.Runs; Node: TYPE = Tioga.Node; SimpleDoc: PROC [root: Node] RETURNS [simple: BOOL _ FALSE, rope: ROPE _ NIL] = { HasInterestingProp: PROC [node: Node] RETURNS [BOOL] = { interestingProp: PROC [name: ATOM, value: REF] RETURNS [BOOL] = { RETURN [SELECT name FROM $Viewer, $LockedViewer, $FromTiogaFile, $DocumentLock, $FileCreateDate, $FileExtension => FALSE, <> ENDCASE => TRUE]; }; RETURN [Tioga.MapProps[node, interestingProp, FALSE, FALSE]]; }; NoLooks: PROC [node: Node] RETURNS [BOOL] = { RETURN[TextLooks.CountRuns[runs: node.runs, start: 0, len: Tioga.Size[node], merge: TRUE, firstLooks: TextLooks.noLooks].count=0]; }; SimpleNode: PROC [node: Node] RETURNS [simple: BOOL _ FALSE, rope: ROPE _ NIL] = { IF (node.formatName=NIL) AND (NOT node.comment) AND (NOT HasInterestingProp[node]) AND (node.runs=NIL OR NoLooks[node]) THEN RETURN [TRUE, node.rope]; }; IF root=NIL THEN RETURN [TRUE, NIL]; [simple, rope] _ SimpleNode[root]; IF NOT simple THEN RETURN; -- not a simple root node IF root.child=NIL THEN RETURN; -- simple root and no child IF rope#NIL THEN RETURN [FALSE, NIL]; -- root has child and text, so not simple IF root.child.last AND root.child.child=NIL THEN [simple, rope] _ SimpleNode[root.child] ELSE RETURN [FALSE, NIL]; -- more than one child, so not simple }; PutDoc: PROC [data, comment, control: IO.STREAM, root: Node, flatten: BOOL] = { WriteControlChar: PROC [c: CHAR] ~ { IO.PutChar[control, c] }; WriteLen: PROC [len: INT] ~ INLINE { PGSupport.PutLength[WriteControlChar, len] }; WriteControlRope: PROC [r: ROPE] ~ { WriteLen[Rope.Size[r]]; IO.PutRope[control, r]; }; WriteRope: PROC [r: ROPE, stream: IO.STREAM] = { WriteLen[Rope.Size[r]]; IO.PutRope[stream, r]; IO.PutChar[stream, 15C]; }; CountLookBits: PROC [lks: TextLooks.Looks] RETURNS [cnt: NAT _ 0] = { FOR c: TextLooks.Look IN TextLooks.Look DO IF lks[c] THEN cnt _ cnt+1; ENDLOOP; }; WriteLookChars: PROC [lks: TextLooks.Looks] = { FOR c: TextLooks.Look IN TextLooks.Look DO IF lks[c] THEN WriteControlChar[c]; ENDLOOP; }; WriteLookBits: PROC [lks: TextLooks.Looks] = { bytes: TextLooks.LooksBytes ~ LOOPHOLE[lks]; WriteControlChar[LOOPHOLE[bytes.byte0]]; WriteControlChar[LOOPHOLE[bytes.byte1]]; WriteControlChar[LOOPHOLE[bytes.byte2]]; WriteControlChar[LOOPHOLE[bytes.byte3]]; }; pgf: PGSupport.PGF ~ PGSupport.CreatePGF[]; runReader: RunReader.Ref ~ RunReader.GetRunReader[]; ropeReader: RopeReader.Ref ~ RopeReader.GetRopeReader[]; node: Node _ root; DO child: Node ~ Tioga.FirstChild[node]; terminal: BOOL ~ (child=NIL); { -- write format formatName: ATOM ~ Tioga.GetFormat[node]; ok: BOOL; formatindex: TiogaFile.FormatIndex; [ok, formatindex] _ PGSupport.EnterFormatName[formatName, pgf]; IF ok THEN { WriteControlChar[(IF terminal THEN TiogaFile.terminalTextNodeFirst ELSE TiogaFile.startNodeFirst)+formatindex]; } ELSE { WriteControlChar[IF terminal THEN TiogaFile.terminalTextNode ELSE TiogaFile.startNode]; WriteControlRope[IF formatName=NIL THEN NIL ELSE Atom.GetPName[formatName]]; }; }; { -- write node props writeProp: PROC [name: ATOM, value: REF] RETURNS [quit: BOOL _ FALSE] = { specs: ROPE ~ Tioga.WriteProp[name, value]; -- write specs as a rope IF specs#NIL THEN { ok: BOOL; propindex: TiogaFile.PropIndex; [ok, propindex] _ PGSupport.EnterProp[name, pgf]; IF ok THEN { -- can use short form WriteControlChar[TiogaFile.propShort]; WriteControlChar[VAL[propindex]]; } ELSE { -- must write full prop name WriteControlChar[TiogaFile.prop]; WriteControlRope[Atom.GetPName[name]]; }; WriteControlRope[specs]; }; }; IF node.props#NIL THEN [] _ Tioga.MapProps[node, writeProp, FALSE, FALSE]; }; { -- now write contents size: INT ~ Tioga.Size[node]; rope: ROPE ~ node.rope; runs: TextLooks.Runs ~ node.runs; IF flatten THEN { -- flatten rope and runs node.rope _ Rope.Balance[rope]; node.runs _ TextLooks.Flatten[runs]; }; IF runs#NIL THEN { -- write looks, if any loc, numRuns: INT _ 0; [numRuns, , ] _ TextLooks.CountRuns[runs, 0, size]; WriteControlChar[TiogaFile.runs]; WriteLen[numRuns]; RunReader.SetPosition[runReader, runs, 0]; THROUGH [0..numRuns) DO len: INT; looks: TextLooks.Looks; ok: BOOL; looksindex: TiogaFile.LooksIndex; [len, looks] _ RunReader.MergedGet[runReader]; [ok, looksindex] _ PGSupport.EnterLooks[looks, pgf]; IF ok THEN WriteControlChar[TiogaFile.looksFirst+looksindex] ELSE { -- must write out the looks SELECT CountLookBits[looks] FROM 1 => { WriteControlChar[TiogaFile.look1]; WriteLookChars[looks] }; 2 => { WriteControlChar[TiogaFile.look2]; WriteLookChars[looks] }; 3 => { WriteControlChar[TiogaFile.look3]; WriteLookChars[looks] }; ENDCASE => { WriteControlChar[TiogaFile.looks]; WriteLookBits[looks] }; }; WriteLen[len]; loc _ loc+len; ENDLOOP; IF loc#size THEN ERROR; }; IF node.comment THEN { -- put text in comment area of file WriteControlChar[TiogaFile.comment]; WriteRope[rope, comment]; } ELSE { -- put text in data area of file WriteControlChar[TiogaFile.rope]; WriteRope[rope, data]; }; }; <> IF NOT terminal THEN node _ child ELSE DO -- node has no children IF node=root THEN GOTO Finis; IF NOT Tioga.IsLastSibling[node] THEN { node _ Tioga.Next[node]; EXIT } ELSE { node _ Tioga.Parent[node]; WriteControlChar[TiogaFile.endNode] }; ENDLOOP; REPEAT Finis => { WriteControlChar[TiogaFile.endOfFile]; RunReader.FreeRunReader[runReader]; RopeReader.FreeRopeReader[ropeReader]; PGSupport.FreePGF[pgf] }; ENDLOOP; }; ToStream: PUBLIC PROC [stream: IO.STREAM, node: Node, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: INT] = { simple: BOOL; rope: ROPE; [simple, rope] _ SimpleDoc[node]; IF simple THEN { IO.PutRope[stream, rope]; dataLen _ count _ Rope.Size[rope] } ELSE { dataStart: INT ~ IO.GetIndex[stream]; comment, control: IO.STREAM _ NIL; IF textOnly THEN comment _ control _ IO.noWhereStream ELSE { comment _ IO.ROS[]; control _ IO.ROS[] }; PutDoc[data: stream, comment: comment, control: control, root: node, flatten: flatten]; dataLen _ IO.GetIndex[stream]-dataStart; IF textOnly THEN count _ dataLen ELSE { PutChar: PROC [c: CHAR] ~ { IO.PutChar[stream, c] }; PutId: PROC [id: TiogaFile.FileId] ~ INLINE { PGSupport.PutFileId[PutChar, id] }; PutLen: PROC [len: INT] ~ INLINE { PGSupport.PutTrailerLength[PutChar, len] }; PutRope: PROC [rope: ROPE] ~ INLINE { IO.PutRope[stream, rope] }; commentRope: ROPE ~ IO.RopeFromROS[comment]; controlRope: ROPE ~ IO.RopeFromROS[control]; commentLen: INT ~ TiogaFile.fileIdSize+TiogaFile.trailerLengthSize+ Rope.Size[commentRope]; controlLen: INT ~ TiogaFile.fileIdSize+TiogaFile.trailerLengthSize+ Rope.Size[controlRope]+TiogaFile.endSize; count _ dataLen+commentLen+controlLen; PutId[TiogaFile.commentHeaderId]; PutLen[commentLen]; PutRope[commentRope]; PutId[TiogaFile.controlHeaderId]; PutLen[controlLen]; PutRope[controlRope]; PutId[TiogaFile.controlTrailerId]; PutLen[0]; PutLen[dataLen]; PutLen[count]; IF (IO.GetIndex[stream]-dataStart)#count THEN ERROR; }; }; }; ToRope: PUBLIC PROC [node: Node, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: INT, output: ROPE] = { simple: BOOL; [simple, output] _ SimpleDoc[node]; IF simple THEN { dataLen _ count _ Rope.Size[output] } ELSE { stream: IO.STREAM ~ IO.ROS[]; [dataLen, count] _ ToStream[stream, node, flatten, textOnly]; output _ IO.RopeFromROS[stream]; }; }; CreateFile: PROC [fileName: ROPE] RETURNS [FS.OpenFile] ~ { keep: INT ~ UserProfile.Number["Tioga.defaultKeep", 2]; RETURN[FS.Create[name: fileName, keep: keep]]; }; ToFile: PUBLIC PROC [fileName: ROPE, node: Node, start: INT _ 0, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: INT] = { createName: ROPE ~ fileName.Flatten[0, fileName.SkipTo[0, "!"]]; -- strip version, if any file: FS.OpenFile ~ CreateFile[createName]; [dataLen, count] _ ToFileC[file, node, start, flatten, textOnly]; }; ToFileC: PUBLIC PROC [file: FS.OpenFile, node: Node, start: INT _ 0, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: INT] = { innerFileIt: PROC ~ { stream: IO.STREAM ~ FS.StreamFromOpenFile[file, $write]; IF start#0 THEN IO.SetIndex[stream, start]; [dataLen, count] _ ToStream[stream, node, flatten, textOnly]; }; CedarProcess.DoWithPriority[savePriority, innerFileIt]; }; savePriority: CedarProcess.Priority _ normal; WritePlain: PUBLIC PROC [h: IO.STREAM, root: Node, restoreDashes: BOOL _ FALSE] = { HasInitialDashes: PROC [r: 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-2 OR c # '- OR Rope.Fetch[r, loc+1] # '- THEN RETURN [FALSE]; RETURN [TRUE] }; node: Node _ root; level: INTEGER _ 0; levelDelta: INTEGER; first: BOOL _ TRUE; DO [node, levelDelta] _ Tioga.Forward[node]; IF node=NIL THEN EXIT; IF first THEN first _ FALSE ELSE IO.PutChar[h, '\n]; -- carriage returns between nodes level _ level+levelDelta; THROUGH [1..level) DO IO.PutChar[h, '\t]; ENDLOOP; -- output level-1 tabs IF restoreDashes AND node.comment AND NOT HasInitialDashes[node.rope] THEN IO.PutRope[h, "-- "]; -- restore the leading dashes for Mesa comments IO.PutRope[h, node.rope]; ENDLOOP; { ENABLE IO.Error => IF ec=NotImplementedForThisStream THEN CONTINUE; IO.SetLength[h, IO.GetIndex[h]] }; }; WriteFilePlain: PUBLIC PROC [fileName: ROPE, root: Node] = { h: IO.STREAM ~ FS.StreamFromOpenFile[CreateFile[fileName], $write]; WritePlain[h, root]; IO.Close[h]; }; WriteFileCPlain: PUBLIC PROC [file: FS.OpenFile, root: Node] = { h: IO.STREAM ~ FS.StreamFromOpenFile[file, $write]; WritePlain[h, root]; IO.Close[h]; }; WriteMesaFilePlain: PUBLIC PROC [fileName: ROPE, root: Node] = { h: IO.STREAM ~ FS.StreamFromOpenFile[CreateFile[fileName], $write]; WritePlain[h, root, TRUE]; IO.Close[h]; }; WriteRopePlain: PUBLIC PROC [root: Node, restoreDashes: BOOL _ FALSE] RETURNS [output: ROPE] = { h: IO.STREAM = IO.ROS[]; WritePlain[h, root, restoreDashes]; RETURN [IO.RopeFromROS[h]] }; END.