<> <> <> <> <> <> <> DIRECTORY Atom, BasicTime, FileOps, FileWriter, FS, IO, NameSymbolTable, NodeProps, OtherNode, PGSupport, Process, PutFile, PutGet, Rope, RopeEdit, RopeIO, RopeReader, RunReader, TextLooks, TextNode; PutFileImpl: CEDAR PROGRAM IMPORTS Atom, FileWriter, FS, IO, NameSymbolTable, npI:NodeProps, OtherNode, PGSupport, Process, Rope, RopeEdit, RopeIO, RopeReader, RunReader, TextLooks, TextNode EXPORTS PutFile, PutGet = BEGIN OPEN PutFile, PutGet, TextNode; ToRope: PUBLIC PROC [node: Ref, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: Offset, 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: Ref, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: Offset] = { 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: Ref, start: Offset _ 0, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: Offset] = { file: FS.OpenFile; fileName _ fileName.Flatten[0, fileName.SkipTo[0, "!"]]; file _ FS.Create[name: fileName, keep: 2]; [dataLen,count] _ ToFileC[file, node, start, flatten, textOnly]; }; ToFileC: PUBLIC PROC [file: FS.OpenFile, node: Ref, start: Offset _ 0, flatten, textOnly: BOOL _ FALSE] RETURNS [dataLen, count: Offset] = { 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] ; }; FileIt: PROC [ ropeToFile: PROC [ROPE], opener: PROC RETURNS [control,comment,data: FileWriter.Ref], node: Ref, flatten, textOnly: BOOL] RETURNS [dataLen, count: Offset] = TRUSTED { control,comment,data: FileWriter.Ref; rope: ROPE; simple: BOOL; priority: Process.Priority = Process.GetPriority[]; Process.SetPriority[Process.priorityBackground]; [rope,simple] _ SimpleFile[node]; IF simple THEN { ropeToFile[rope]; dataLen _ count _ Rope.Size[rope]; RETURN }; [control,comment,data] _ opener[]; Finish[control,comment,data,node,flatten]; [dataLen,count,] _ FileWriter.Close[control,comment,data,textOnly]; Process.SetPriority[priority]; }; SimpleFile: PROC [root: Ref] RETURNS [rope: ROPE, simple: BOOL] = { SimpleNode: PROC [node: Ref] RETURNS [ROPE, BOOL] = TRUSTED { HasInterestingProp: PROC RETURNS [BOOL] = TRUSTED { Check: PROC [name: ATOM, value: REF] RETURNS [BOOL] = TRUSTED { RETURN [SELECT name FROM $Viewer, $FromTiogaFile, $DocumentLock, $FileCreateDate, $FileExtension => FALSE, <> ENDCASE => TRUE] }; RETURN [npI.MapProps[node,Check,FALSE,FALSE]] }; text: RefTextNode = NarrowToTextNode[node]; IF node.typename=nullTypeName AND ~text.comment AND ~HasInterestingProp[] THEN WITH n:node SELECT FROM text => IF n.runs=NIL THEN RETURN [n.rope,TRUE] ELSE WITH r:n.runs SELECT FROM base => IF r.length=1 AND r[0].looks=TextLooks.noLooks THEN RETURN [n.rope,TRUE]; ENDCASE; 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: Ref, flatten: BOOL] = { WriteChar: PROC [c: CHARACTER, writer: FileWriter.Ref] = { FileWriter.WriteChar[c,writer] }; WriteAtom: PROC [atom: ATOM] = INLINE { WriteControlRope[Atom.GetPName[atom]] }; WriteText: PROC [txt: REF READONLY TEXT] = INLINE { WriteLen[txt.length]; FileWriter.WriteText[txt,control] }; WriteRope: PROC [r: ROPE, writer: FileWriter.Ref] = INLINE { WriteLen[Rope.Size[r]]; FileWriter.WriteRope[r,Rope.Size[r],writer,ropeReader]; WriteChar[15C,writer] }; NameText: PROC [name: NameSymbolTable.Name] = INLINE { OPEN NameSymbolTable; FromName[name,nameText ! TextOverflow => { nameText _ pZone.NEW[TEXT[nameText.length*2]]; RETRY } ]}; WriteControlRope: PROC [r: ROPE] = { size: Offset _ Rope.Size[r]; WriteLen[size]; FileWriter.WriteRope[r,size,control,ropeReader] }; WriteLen: PROC [len: Offset] = { OPEN FileOps; first, second, fourth: LengthByte; third: ThirdByte; lenBytes: 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] = INLINE { 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] = INLINE { cnt _ 0; FOR c: CHARACTER IN TextLooks.Look DO IF lks[c] THEN cnt _ cnt+1; ENDLOOP }; WriteLookChars: PROC [lks: TextLooks.Looks] = INLINE { FOR c: CHARACTER 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 OPEN FileOps; 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[propShort,control]; WriteChar[LOOPHOLE[propindex],control] } ELSE { -- must write full prop name WriteChar[prop,control]; WriteAtom[name] }; WriteControlRope[specs]; RETURN [FALSE] }; typename: TypeName; propname: ATOM; ok, textnode, terminal: BOOL; typeindex: FileOps.TypeIndex; looksindex: FileOps.LooksIndex; propindex: FileOps.PropIndex; node,nodeChild: Ref; pgf: PGSupport.PGF _ PGSupport.CreatePGF[]; runReader: RunReader.Ref _ RunReader.GetRunReader[]; ropeReader: RopeReader.Ref _ RopeReader.GetRopeReader[]; nameText: REF TEXT _ pZone.NEW[TEXT[32]]; node _ root; DO -- first write type textnode _ node.kind=text; terminal _ (nodeChild_FirstChild[node])=NIL; [ok,typeindex] _ PGSupport.EnterTypeName[typename _ node.typename, pgf]; IF ok THEN IF ~textnode THEN { WriteChar[FileOps.otherNodeShort,control]; WriteChar[LOOPHOLE[typeindex],control] } ELSE WriteChar[typeindex+(IF ~terminal THEN FileOps.startNodeFirst ELSE FileOps.terminalTextNodeFirst),control] ELSE { WriteChar[IF ~textnode THEN FileOps.otherNode ELSE IF ~terminal THEN FileOps.startNode ELSE FileOps.terminalTextNode,control]; NameText[typename]; WriteText[nameText] }; <<-- write node props>> IF node.props # NIL THEN [] _ npI.MapProps[node,WriteProp,FALSE,FALSE]; <<-- now write contents>> TRUSTED {WITH n:node SELECT FROM text => { -- first the rope, then the looks rope: ROPE; size: Offset; runs: TextLooks.Runs; IF flatten THEN { -- flatten rope and runs n.rope _ RopeEdit.Flatten[n.rope]; n.runs _ TextLooks.Flatten[n.runs] }; rope _ n.rope; size _ Rope.Size[rope]; IF (runs _ n.runs) # NIL THEN { loc, cnt, numRuns: Offset _ 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: Offset; [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 n.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] }}; other => { propname _ n.variety; [ok,propindex] _ PGSupport.EnterProp[propname,pgf]; IF ok THEN { WriteChar[FileOps.otherNodeSpecsShort,control]; WriteChar[LOOPHOLE[propindex],control] } ELSE { WriteChar[FileOps.otherNodeSpecs,control]; WriteAtom[propname] }; WriteControlRope[OtherNode.GetSpecs[@n]] }; ENDCASE => ERROR}; <<-- move to the next node>> IF ~terminal THEN node _ nodeChild ELSE { -- node has no children IF ~textnode THEN WriteChar[FileOps.endNode,control]; DO IF node=root THEN GOTO Finis; IF ~IsLastSibling[node] THEN { node _ Next[node]; EXIT }; node _ 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: Ref] = { h: IO.STREAM _ FS.StreamOpen[fileName: fileName, accessOptions: $create, keep: 2]; WritePlain[h, root, TRUE]; IO.Close[h]; }; WriteFilePlain: PUBLIC PROC [fileName: ROPE, root: Ref] = { h: IO.STREAM _ FS.StreamOpen[fileName: fileName, accessOptions: $create, keep: 2]; WritePlain[h, root]; IO.Close[h]; }; WriteFileCPlain: PUBLIC PROC [file: FS.OpenFile, root: Ref] = { h: IO.STREAM = FS.StreamFromOpenFile[file, $write]; WritePlain[h, root]; IO.Close[h]; }; WriteRopePlain: PUBLIC PROC [root: Ref] RETURNS [output: ROPE] = { h: IO.STREAM = IO.ROS[]; WritePlain[h, root]; RETURN [IO.RopeFromROS[h]] }; WritePlain: PROC [h: IO.STREAM, root: 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 OR c # '- OR Rope.Fetch[r, loc+1] # '- THEN RETURN [FALSE]; RETURN [TRUE] }; node: 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 }; <<-- ***** Initialization>> StartPutFile: PUBLIC PROC = { }; END.