PutFileImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, October 31, 1985 10:52:43 am PST
Rick Beach, March 27, 1985 1:04:56 pm PST
Doug Wyatt, April 10, 1985 8:33:58 pm PST
Russ Atkinson (RRA) August 7, 1985 12:49:05 pm PDT
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: BOOLFALSE] 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: BOOLFALSE] 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: BOOLFALSE] 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: BOOLFALSE] 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,
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.
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 TEXTNEW[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]
};
write node props
IF node.props # NIL THEN [] ← npI.MapProps[node, WriteProp, FALSE, FALSE];
now write contents
first the rope, then the looks
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𡤌nt+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]
};
move to the next node
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.STREAMFS.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.STREAMFS.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: BOOLFALSE]
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: BOOLFALSE] = {
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: BOOLTRUE;
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.