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 18, 1986 8:57:45 pm PDT
DIRECTORY
Atom USING [GetPName],
CedarProcess USING [Priority, DoWithPriority],
FS USING [Create, OpenFile, StreamFromOpenFile],
IO USING [Close, Error, GetChar, GetIndex, noWhereStream, PutChar, PutRope, RopeFromROS, ROS, SetIndex, SetLength, STREAM],
PGSupport USING [CreatePGF, EnterLooks, EnterProp, EnterFormatName, FreePGF, PGF],
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, IntBytes, LengthByte, look1, look2, look3, looks, looksFirst, LooksIndex, prop, PropIndex, propShort, rope, runs, startNode, startNodeFirst, terminalTextNode, terminalTextNodeFirst, ThirdByte],
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;
PutPackedLen:
PROC [stream:
IO.
STREAM, len:
INT] = {
first, second, fourth: TiogaFile.LengthByte;
third: TiogaFile.ThirdByte;
lenBytes: TiogaFile.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;
IO.PutChar[stream, LOOPHOLE[first]];
IF first.others
THEN {
IO.PutChar[stream, LOOPHOLE[second]];
IF second.others
THEN {
IO.PutChar[stream, LOOPHOLE[third]];
IF third.others
THEN {
IO.PutChar[stream, LOOPHOLE[fourth]]
}
}
}
};
lenSize: INT ~ 4;
LenIndex: TYPE ~ [0..lenSize);
LenBytes:
TYPE ~
PACKED
ARRAY LenIndex
OF
CHAR;
PutLen:
PROC [stream:
IO.
STREAM, len:
INT] ~ {
FOR i: LenIndex
IN LenIndex
DO
IO.PutChar[stream, LOOPHOLE[len, LenBytes][i]];
ENDLOOP;
};
GetLen:
PROC [stream:
IO.
STREAM]
RETURNS [len:
INT] ~ {
FOR i: LenIndex
IN LenIndex
DO
LOOPHOLE[len, LenBytes][i] ← IO.GetChar[stream];
ENDLOOP;
};
fileIdSize: INT ~ TiogaFile.fileIdSize;
FileIdIndex:
TYPE ~ [0..TiogaFile.fileIdSize);
PutId:
PROC [stream:
IO.
STREAM, id: TiogaFile.FileId] ~ {
FOR i: FileIdIndex IN FileIdIndex DO IO.PutChar[stream, id[i]] ENDLOOP;
};
GetId:
PROC [stream:
IO.
STREAM]
RETURNS [id: TiogaFile.FileId] ~ {
FOR i: FileIdIndex IN FileIdIndex DO id[i] ← IO.GetChar[stream] ENDLOOP;
};
PutDoc:
PROC [data, comment, control:
IO.
STREAM, root: Node, flatten:
BOOL] = {
WriteLen: PROC [len: INT] ~ INLINE { PutPackedLen[control, len] };
WriteControlChar: PROC [c: CHAR] ~ INLINE { IO.PutChar[control, c] };
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];
};
};
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;
};
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,
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:
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
};
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];
};
};
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 {
commentRope: ROPE ~ IO.RopeFromROS[comment];
controlRope: ROPE ~ IO.RopeFromROS[control];
commentLen: INT ~ fileIdSize+lenSize+Rope.Size[commentRope];
controlLen: INT ~ fileIdSize+lenSize+Rope.Size[controlRope]+TiogaFile.endSize;
count ← dataLen+commentLen+controlLen;
PutId[stream, TiogaFile.commentHeaderId];
PutLen[stream, commentLen];
IO.PutRope[stream, commentRope];
PutId[stream, TiogaFile.controlHeaderId];
PutLen[stream, controlLen];
IO.PutRope[stream, controlRope];
PutId[stream, TiogaFile.controlTrailerId];
PutLen[stream, 0];
PutLen[stream, dataLen];
PutLen[stream, count];
IF (IO.GetIndex[stream]-dataStart)#count THEN ERROR;
};
};
};
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.