<> <> <> <> <> <> DIRECTORY Atom USING [GetPName], CedarProcess USING [Priority, DoWithPriority], FileOps USING [comment, endNode, endOfFile, IntBytes, LengthByte, look1, look2, look3, looks, looksFirst, LooksIndex, prop, PropIndex, propShort, rope, runs, startNode, startNodeFirst, terminalTextNode, terminalTextNodeFirst, ThirdByte, FormatIndex], FileWriter USING [Close, OpenC, Ref, ToRope, ToStream, WriteChar, WriteRope], FS USING [Create, OpenFile, StreamFromOpenFile, StreamOpen], IO USING [Close, Error, GetIndex, PutChar, PutRope, RopeFromROS, ROS, SetLength, STREAM], NodeProps USING [GetSpecs, MapProps], PGSupport USING [CreatePGF, EnterLooks, EnterProp, EnterFormatName, FreePGF, PGF], PrincOpsUtils USING [], PutGet USING [], Rope, RopeEdit USING [BlankChar], RopeIO USING [PutRope, ToFileC], RopeReader USING [FreeRopeReader, GetRopeReader, Ref], RunReader USING [FreeRunReader, GetRunReader, MergedGet, Ref, SetPosition], TextLooks USING [CountRuns, Flatten, Look, Looks, LooksBytes, noLooks, Runs, RunsBody], TextNode USING [FirstChild, Forward, IsLastSibling, NarrowToTextNode, Next, NodeProps, Parent, Ref, RefTextNode], UserProfile USING [Number]; PutFileImpl: CEDAR PROGRAM IMPORTS Atom, CedarProcess, FileWriter, FS, IO, npI: NodeProps, PGSupport, Rope, RopeEdit, RopeIO, RopeReader, RunReader, TextLooks, TextNode, UserProfile EXPORTS PutGet = BEGIN OPEN PutGet; ROPE: TYPE = Rope.ROPE; ToRope: PUBLIC PROC [node: TextNode.Ref, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: INT, output: ROPE] = { control, comment, data: FileWriter.Ref; simple: BOOL; [output, simple] _ SimpleFile[node]; IF simple THEN { dataLen _ count _ Rope.Size[output]; RETURN }; [control, comment, data] _ FileWriter.ToRope[]; Finish[control, comment, data, node, flatten]; [dataLen, count, output] _ FileWriter.Close[control, comment, data, textOnly]; }; ToStream: PUBLIC PROC [stream: IO.STREAM, node: TextNode.Ref, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: INT] = { control, comment, data: FileWriter.Ref; rope: ROPE; simple: BOOL; [rope, simple] _ SimpleFile[node]; IF simple THEN { RopeIO.PutRope[stream, rope]; dataLen _ count _ Rope.Size[rope]; RETURN }; [control, comment, data] _ FileWriter.ToStream[stream]; Finish[control, comment, data, node, flatten]; [dataLen, count, ] _ FileWriter.Close[control, comment, data, textOnly]; }; ToFile: PUBLIC PROC [fileName: ROPE, node: TextNode.Ref, start: INT _ 0, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: INT] = { file: FS.OpenFile; fileName _ fileName.Flatten[0, fileName.SkipTo[0, "!"]]; file _ FS.Create[name: fileName, keep: UserProfile.Number["Tioga.defaultKeep", 2]]; [dataLen, count] _ ToFileC[file, node, start, flatten, textOnly]; }; ToFileC: PUBLIC PROC [file: FS.OpenFile, node: TextNode.Ref, start: INT _ 0, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: INT] = { ropeToFile: PROC [rope: ROPE] = { RopeIO.ToFileC[file, rope, start] }; opener: PROC RETURNS [control, comment, data: FileWriter.Ref] = { [control, comment, data] _ FileWriter.OpenC[file, start] }; [dataLen, count] _ FileIt[ropeToFile, opener, node, flatten, textOnly]; }; savePriority: CedarProcess.Priority _ normal; FileIt: PROC [ropeToFile: PROC [ROPE], opener: PROC RETURNS [control, comment, data: FileWriter.Ref], node: TextNode.Ref, flatten, textOnly: BOOL ] RETURNS [dataLen, count: INT] = { innerFileIt: PROC ~ { rope: ROPE; simple: BOOL; [rope, simple] _ SimpleFile[node]; IF simple THEN { ropeToFile[rope]; dataLen _ count _ Rope.Size[rope]; } ELSE { control, comment, data: FileWriter.Ref; [control, comment, data] _ opener[]; Finish[control, comment, data, node, flatten]; [dataLen, count, ] _ FileWriter.Close[control, comment, data, textOnly]; }; }; CedarProcess.DoWithPriority[savePriority, innerFileIt]; }; SimpleFile: PROC [root: TextNode.Ref] RETURNS [rope: ROPE, simple: BOOL] = { SimpleNode: PROC [node: TextNode.Ref] RETURNS [ROPE, BOOL] = { HasInterestingProp: PROC RETURNS [BOOL] = { Check: PROC [name: ATOM, value: REF] RETURNS [BOOL] = { RETURN [SELECT name FROM $Viewer, $LockedViewer, $FromTiogaFile, $DocumentLock, $FileCreateDate, $FileExtension => FALSE, <> ENDCASE => TRUE] }; RETURN [npI.MapProps[node, Check, FALSE, FALSE]] }; text: TextNode.RefTextNode = node; IF node.formatName=NIL AND NOT text.comment AND NOT HasInterestingProp[] THEN { IF node.runs=NIL THEN RETURN [node.rope, TRUE] ELSE WITH node.runs SELECT FROM r: REF TextLooks.RunsBody.base => IF r.length=1 AND r[0].looks=TextLooks.noLooks THEN RETURN [node.rope, TRUE]; ENDCASE; }; RETURN [NIL, FALSE] }; IF root=NIL THEN RETURN [NIL, TRUE]; [rope, simple] _ SimpleNode[root]; IF ~simple THEN RETURN; -- not a simple root node IF root.child=NIL THEN RETURN; -- simple root and no child IF rope # NIL OR -- root has child and text, so not simple ~root.child.last OR root.child.child # NIL THEN RETURN [NIL, FALSE]; -- more than one child, so not simple [rope, simple] _ SimpleNode[root.child]; }; Finish: PROC [control, comment, data: FileWriter.Ref, root: TextNode.Ref, flatten: BOOL] = { WriteChar: PROC [c: CHAR, writer: FileWriter.Ref] = { FileWriter.WriteChar[c, writer] }; WriteAtom: PROC [atom: ATOM] = { WriteControlRope[Atom.GetPName[atom]] }; WriteRope: PROC [r: ROPE, writer: FileWriter.Ref] = { WriteLen[Rope.Size[r]]; FileWriter.WriteRope[r, Rope.Size[r], writer, ropeReader]; WriteChar[15C, writer] }; WriteControlRope: PROC [r: ROPE] = { size: INT _ Rope.Size[r]; WriteLen[size]; FileWriter.WriteRope[r, size, control, ropeReader] }; WriteLen: PROC [len: INT] = { first, second, fourth: FileOps.LengthByte; third: FileOps.ThirdByte; lenBytes: FileOps.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; WriteChar[LOOPHOLE[first], control]; IF first.others THEN { WriteChar[LOOPHOLE[second], control]; IF second.others THEN { WriteChar[LOOPHOLE[third], control]; IF third.others THEN { WriteChar[LOOPHOLE[fourth], control] } } } }; WriteLooks: PROC [lks: TextLooks.LooksBytes] = { WriteChar[LOOPHOLE[lks.byte0], control]; WriteChar[LOOPHOLE[lks.byte1], control]; WriteChar[LOOPHOLE[lks.byte2], control]; WriteChar[LOOPHOLE[lks.byte3], control] }; CountLookBits: PROC [lks: TextLooks.Looks] RETURNS [cnt: NAT] = { cnt _ 0; FOR c: CHAR IN TextLooks.Look DO IF lks[c] THEN cnt _ cnt+1; ENDLOOP }; WriteLookChars: PROC [lks: TextLooks.Looks] = { FOR c: CHAR IN TextLooks.Look DO IF lks[c] THEN WriteChar[c, control]; ENDLOOP }; WriteProp: PROC [name: ATOM, value: REF] RETURNS [BOOL] = { -- write specs as a rope specs: ROPE _ npI.GetSpecs[name, value]; IF specs=NIL THEN RETURN [FALSE]; [ok, propindex] _ PGSupport.EnterProp[name, pgf]; IF ok THEN { -- can use short form WriteChar[FileOps.propShort, control]; WriteChar[LOOPHOLE[propindex], control] } ELSE { -- must write full prop name WriteChar[FileOps.prop, control]; WriteAtom[name] }; WriteControlRope[specs]; RETURN [FALSE] }; formatName: ATOM; ok, terminal: BOOL; formatindex: FileOps.FormatIndex; looksindex: FileOps.LooksIndex; propindex: FileOps.PropIndex; node, nodeChild: TextNode.Ref; pgf: PGSupport.PGF _ PGSupport.CreatePGF[]; runReader: RunReader.Ref _ RunReader.GetRunReader[]; ropeReader: RopeReader.Ref _ RopeReader.GetRopeReader[]; nameText: REF TEXT _ NEW[TEXT[32]]; node _ root; DO -- first write format rope: ROPE; size: INT; runs: TextLooks.Runs; terminal _ (nodeChild_TextNode.FirstChild[node])=NIL; [ok, formatindex] _ PGSupport.EnterFormatName[formatName _ node.formatName, pgf]; IF ok THEN { WriteChar[formatindex+(IF ~terminal THEN FileOps.startNodeFirst ELSE FileOps.terminalTextNodeFirst), control] } ELSE { WriteChar[IF ~terminal THEN FileOps.startNode ELSE FileOps.terminalTextNode, control]; WriteControlRope[IF formatName#NIL THEN Atom.GetPName[formatName] ELSE NIL] }; <> IF node.props # NIL THEN [] _ npI.MapProps[node, WriteProp, FALSE, FALSE]; <> <> IF flatten THEN { -- flatten rope and runs node.rope _ Rope.Balance[node.rope]; node.runs _ TextLooks.Flatten[node.runs]; }; rope _ node.rope; size _ Rope.Size[rope]; IF (runs _ node.runs) # NIL THEN { loc, cnt, numRuns: INT _ 0; [numRuns, , ] _ TextLooks.CountRuns[runs, 0, size]; WriteChar[FileOps.runs, control]; WriteLen[numRuns]; RunReader.SetPosition[runReader, runs, 0]; WHILE (cnt_cnt+1) <= numRuns DO looks: TextLooks.Looks; len: INT; [len, looks] _ RunReader.MergedGet[runReader]; [ok, looksindex] _ PGSupport.EnterLooks[looks, pgf]; IF ok THEN WriteChar[FileOps.looksFirst+looksindex, control] ELSE { -- must write out the looks SELECT CountLookBits[looks] FROM 1 => { WriteChar[FileOps.look1, control]; WriteLookChars[looks] }; 2 => { WriteChar[FileOps.look2, control]; WriteLookChars[looks] }; 3 => { WriteChar[FileOps.look3, control]; WriteLookChars[looks] }; ENDCASE => { WriteChar[FileOps.looks, control]; WriteLooks[LOOPHOLE[looks]] } }; WriteLen[len]; loc _ loc+len; ENDLOOP; IF loc # size THEN ERROR }; IF node.comment THEN { -- put text in comment area of file WriteChar[FileOps.comment, control]; WriteRope[rope, comment] } ELSE { -- put text in data area of file WriteChar[FileOps.rope, control]; WriteRope[rope, data] }; <> IF ~terminal THEN node _ nodeChild ELSE { -- node has no children DO IF node=root THEN GOTO Finis; IF ~TextNode.IsLastSibling[node] THEN { node _ TextNode.Next[node]; EXIT }; node _ TextNode.Parent[node]; WriteChar[FileOps.endNode, control]; ENDLOOP }; REPEAT Finis => { WriteChar[FileOps.endOfFile, control]; RunReader.FreeRunReader[runReader]; RopeReader.FreeRopeReader[ropeReader]; PGSupport.FreePGF[pgf] }; ENDLOOP }; WriteMesaFilePlain: PUBLIC PROC [fileName: ROPE, root: TextNode.Ref] = { h: IO.STREAM _ FS.StreamOpen[fileName: fileName, accessOptions: $create, keep: UserProfile.Number["Tioga.defaultKeep", 2]]; WritePlain[h, root, TRUE]; IO.Close[h]; }; WriteFilePlain: PUBLIC PROC [fileName: ROPE, root: TextNode.Ref] = { h: IO.STREAM _ FS.StreamOpen[fileName: fileName, accessOptions: $create, keep: UserProfile.Number["Tioga.defaultKeep", 2]]; WritePlain[h, root]; IO.Close[h]; }; WriteFileCPlain: PUBLIC PROC [file: FS.OpenFile, root: TextNode.Ref] = { h: IO.STREAM = FS.StreamFromOpenFile[file, $write]; WritePlain[h, root]; IO.Close[h]; }; WriteRopePlain: PUBLIC PROC [root: TextNode.Ref, restoreDashes: BOOL _ FALSE] RETURNS [output: ROPE] = { h: IO.STREAM = IO.ROS[]; WritePlain[h, root, restoreDashes]; RETURN [IO.RopeFromROS[h]] }; WritePlain: PUBLIC PROC [h: IO.STREAM, root: TextNode.Ref, 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: TextNode.Ref _ root; level: INTEGER _ 0; levelDelta: INTEGER; first: BOOL _ TRUE; DO text: TextNode.RefTextNode; [node, levelDelta] _ TextNode.Forward[node]; IF node=NIL THEN EXIT; IF first THEN first _ FALSE ELSE IO.PutChar[h, '\n]; -- carriage returns between nodes level _ level+levelDelta; IF (text _ TextNode.NarrowToTextNode[node])=NIL THEN LOOP; THROUGH [1..level) DO IO.PutChar[h, '\t]; ENDLOOP; -- output level-1 tabs IF restoreDashes AND text.comment AND ~HasInitialDashes[text.rope] THEN IO.PutRope[h, "-- "]; -- restore the leading dashes for Mesa comments IO.PutRope[h, text.rope]; ENDLOOP; { ENABLE IO.Error => IF ec = NotImplementedForThisStream THEN GOTO Exit; IO.SetLength[h, IO.GetIndex[h]] } EXITS Exit => RETURN }; END.