PutFileImpl.Mesa
Copyright (C) 1983, 1984, 1985, Xerox Corporation. All rights reserved.
written by Paxton. May 1981
Paxton. June 2, 1983 11:06 am
McGregor. September 10, 1982 1:16 pm
Maxwell, January 5, 1983 1:07 pm
Russ Atkinson, September 28, 1983 12:55 pm
Birrell, August 25, 1983 2:27 pm
Doug Wyatt, January 2, 1985 2:33:44 pm PST
DIRECTORY
Atom USING [GetPName],
BasicTime USING [],
FileOps USING [comment, endNode, endOfFile, IntBytes, LengthByte, look1, look2, look3, looks, looksFirst, LooksIndex, otherNode, otherNodeShort, otherNodeSpecs, otherNodeSpecsShort, prop, PropIndex, propShort, rope, runs, startNode, startNodeFirst, terminalTextNode, terminalTextNodeFirst, ThirdByte, TypeIndex],
FileWriter USING [Close, Offset, OpenC, Ref, ToRope, ToStream, WriteChar, WriteRope, WriteText],
FS USING [Create, OpenFile, StreamFromOpenFile, StreamOpen],
IO USING [Close, Error, GetIndex, PutChar, PutRope, RopeFromROS, ROS, SetLength, STREAM],
NameSymbolTable USING [FromName, Name, TextOverflow],
NodeProps USING [GetSpecs, MapProps],
OtherNode USING [GetSpecs],
PGSupport USING [CreatePGF, EnterLooks, EnterProp, EnterTypeName, FreePGF, PGF],
PrincOpsUtils USING [],
Process USING [GetPriority, Priority, priorityBackground, SetPriority],
PutFile USING [],
PutGet USING [],
PutGetExtras USING [],
Rope USING [Fetch, Flatten, ROPE, Size, SkipTo],
RopeEdit USING [BlankChar, Flatten],
RopeIO USING [PutRope, ToFileC],
RopeReader USING [FreeRopeReader, GetIndex, GetRopeReader, Ref, SetPosition],
RunReader USING [FreeRunReader, GetIndex, GetRunReader, MergedGet, noLooks, Ref, SetPosition],
TextLooks USING [CountRuns, Flatten, Look, Looks, LooksBytes, noLooks, Runs],
TextNode USING [FirstChild, Forward, IsLastSibling, NarrowToTextNode, Next, NodeProps, nullTypeName, Offset, Parent, pZone, Ref, RefTextNode, TypeName];
PutFileImpl: CEDAR PROGRAM
IMPORTS Atom, FileWriter, FS, IO, NameSymbolTable, npI:NodeProps, OtherNode, PGSupport, Process, Rope, RopeEdit, RopeIO, RopeReader, RunReader, TextLooks, TextNode
EXPORTS PutFile, PutGet, PutGetExtras
= BEGIN OPEN PutFile, PutGet, TextNode;
ROPE: TYPE = Rope.ROPE;
ToRope: PUBLIC PROC
[node: Ref, flatten, textOnly: BOOLFALSE]
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: BOOLFALSE]
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: BOOLFALSE]
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: BOOLFALSE]
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,
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: 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𡤏irstChild[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𡤌nt+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];
DOIF 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.STREAMFS.StreamOpen[fileName: fileName, accessOptions: $create, keep: 2];
WritePlain[h, root, TRUE];
IO.Close[h];
};
WriteFilePlain: PUBLIC PROC [fileName: ROPE, root: Ref] = {
h: IO.STREAMFS.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: PUBLIC PROC [h: IO.STREAM, root: 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: 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 };
-- ***** Initialization
StartPutFile: PUBLIC PROC = {
};
END.