PutFileImpl.mesa
Copyright © 1985, 1986 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
Russ Atkinson (RRA) August 7, 1985 12:49:05 pm PDT
Doug Wyatt, September 22, 1986 1:27:09 pm PDT
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: BOOLFALSE, rope: ROPENIL] = {
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,
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 [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: BOOLFALSE, rope: ROPENIL] = {
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: BOOLFALSE] = {
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];
};
};
move to the next node
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: BOOLFALSE] 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.STREAMNIL;
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: BOOLFALSE] 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: BOOLFALSE] 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: BOOLFALSE] 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: 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: Node ← root;
level: INTEGER ← 0;
levelDelta: INTEGER;
first: BOOLTRUE;
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: BOOLFALSE]
RETURNS [output: ROPE] = {
h: IO.STREAM = IO.ROS[];
WritePlain[h, root, restoreDashes];
RETURN [IO.RopeFromROS[h]]
};
END.