TiogaExecCommandsImpl.mesa
Copyright Ó 1985, 1987, 1988, 1989, 1990, 1991, 1992, 1993 by Xerox Corporation. All rights reserved.
Paxton, October 26, 1982 10:12 am
Maxwell, January 14, 1983 8:46 am
Plass, April 20, 1983 9:26 am
Russ Atkinson, September 29, 1983 11:17 am
Mike Spreitzer February 19, 1987 3:51:44 pm PST
Christian Jacobi, March 17, 1993 5:49 pm PST
Eduardo Pelegri-Llopart, July 26, 1989 12:51:00 pm PDT
Jules Bloomenthal November 25, 1990 12:32 pm PST
Willie-s, June 13, 1991 4:21 pm PDT
Michael Plass, January 30, 1992 2:04 pm PST
Doug Wyatt, October 19, 1992 3:57 pm PDT
Viewer independent commands.
See TiogaExecViewerCommands.mesa for viewer dependent commands.
Bier, June 24, 1993 5:10 pm PDT
DIRECTORY Tioga, Ascii, Char, Commander, CommanderOps, Convert, IO, NodeProps, PFS, PFSNames, Process, TiogaIO, TiogaIOExtras, Rope, TextEdit, TextNode, TiogaExecCommands, TiogaMesaOps, TiogaAccess;
TiogaExecCommandsImpl: CEDAR PROGRAM
IMPORTS Char, Commander, CommanderOps, Convert, IO, NodeProps, PFS, PFSNames, Process, TiogaIO, TiogaIOExtras, Rope, TextEdit, TextNode, TiogaMesaOps, TiogaAccess
EXPORTS TiogaExecCommands
~ BEGIN
Offset:  TYPE ~ TextNode.Offset;
RefTextNode: TYPE ~ TextNode.Ref;
STREAM:  TYPE ~ IO.STREAM;
ROPE:  TYPE ~ Rope.ROPE;
Real Work
ReadIndent: PUBLIC PROC [rope: ROPE, tabIndent: NAT ¬ 4] RETURNS [result: RefTextNode] ~ {
end: INT ~ Rope.Size[rope];
index: INT ¬ 0;
writer: TiogaAccess.Writer ~ TiogaAccess.Create[];
maxOpen: NAT = 40;
openIndents: ARRAY [0..maxOpen] OF INTEGER ¬ ALL[0];
level: NAT ¬ 0;
PutChar: PROC [c: CHAR] RETURNS [quit: BOOL ¬ FALSE] ~ {
TiogaAccess.Put[writer, [charSet: 0, char: c, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]];
};
Finish: PROC [root, first, last: Tioga.Node] ~ { result ¬ root };
TiogaAccess.Put[writer, [charSet: 0, char: Ascii.LF, looks: ALL[FALSE], format: NIL, comment: TRUE, endOfNode: TRUE, deltaLevel: 1, propList: NIL]];
WHILE index < end DO
emptyLine: BOOL ¬ FALSE;
indent: NAT ¬ 0;
lineEnd: INT ¬ 0;
WHILE index < end DO
SELECT Rope.Fetch[rope, index] FROM
Ascii.CR, Ascii.LF => { emptyLine ¬ TRUE; EXIT};
Ascii.TAB => indent ¬ indent+tabIndent;
Ascii.SP => indent ¬ indent+1;
ENDCASE => EXIT;
index ¬ index + 1;
ENDLOOP;
lineEnd ¬ Rope.SkipTo[rope, index, "\r\l"];
IF NOT emptyLine THEN {
WHILE level > 0 AND indent < openIndents[level] DO
level ¬ level - 1;
TiogaAccess.Nest[writer, -1];
ENDLOOP;
IF level < maxOpen AND indent > openIndents[level] THEN {
level ¬ level + 1;
TiogaAccess.Nest[writer, 1];
};
openIndents[level] ¬ indent;
};
[] ¬ Rope.Map[base: rope, start: index, len: lineEnd-index, action: PutChar];
index ¬ lineEnd + 1;
TiogaAccess.Put[writer, [charSet: 0, char: Ascii.LF, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: TRUE, deltaLevel: 0, propList: NIL]];
ENDLOOP;
TiogaAccess.FinishWrite[writer, Finish];
NodeProps.PutProp[n: result, name: $NewlineDelimiter, value: Rope.Literal["\n"]];
};
TiogaMesaLooksCommand: Commander.CommandProc ~ {result ¬ DoTiogaMesa[cmd, FALSE, TRUE]};
TiogaMesaCommand: Commander.CommandProc ~ {result ¬ DoTiogaMesa[cmd, TRUE, TRUE]};
ReadIndentCommand: Commander.CommandProc ~ {result ¬ DoTiogaMesa[cmd, TRUE, FALSE]};
Get: PROC [name: PFSNames.PATH, readindent: BOOL ¬ FALSE] RETURNS [root: TextNode.Ref] ~ {
IF readindent
THEN {
contents: ROPE ~ PFS.RopeOpen[fileName: name, checkMutability: FALSE].rope;
root ¬ ReadIndent[contents, tabIndent];
}
ELSE {
[root: root] ¬ TiogaIO.FromFile[name];
};
};
tabIndent: NAT ¬ 8;
DoTiogaMesa: PROC [cmd: Commander.Handle, readindent, mesaLooks: BOOL] RETURNS [result: ATOM ¬ NIL] ~ {
Writer: DoWriteProc ~ {
IF mesaLooks THEN [] ¬ TiogaMesaOps.SpanMesaLooks[
span: TextNode.MakeNodeSpan[root,TextNode.LastWithin[root]], root: NIL];
[] ¬ TiogaIO.ToStream[out, root];
};
tabIndent ¬ 8;
result ¬ Dudley[Writer, cmd, readindent];
};
levelIndent: ROPE ¬ " ";
SetLevelIndent: Commander.CommandProc ~ {
ENABLE Convert.Error => {
ERROR CommanderOps.Failed["(number of spaces, | tab)"];
};
r: ROPE ¬ CommanderOps.NextArgument[cmd];
SELECT TRUE FROM
Rope.Equal[r, "tab", FALSE] => levelIndent ¬ "\t";
Rope.Equal[r, "default", FALSE] => levelIndent ¬ " ";
ENDCASE => {
n: CARD ¬ Convert.CardFromRope[r];
IF n>50 THEN CommanderOps.Failed["too big"];
r ¬ NIL;
WHILE n>0 DO r ¬ Rope.Concat[r, " "]; n ¬ n-1 ENDLOOP;
levelIndent ¬ Rope.Flatten[r];
};
};
WriteMesaPlainCommand: Commander.CommandProc ~ {
Writer: DoWriteProc ~ {TiogaIOExtras.WritePlain[s: out, root: root, restoreDashes: TRUE, indent: levelIndent]};
result ¬ Dudley[Writer, cmd];
};
WritePlainCommand: Commander.CommandProc ~ {
Writer: DoWriteProc ~ {TiogaIOExtras.WritePlain[s: out, root: root, restoreDashes: FALSE, indent: levelIndent]};
result ¬ Dudley[Writer, cmd];
};
WriteAsciiCommand: Commander.CommandProc ~ {
Writer: DoWriteProc ~ {DoWriteAscii[h: out, root: root, restoreDashes: FALSE]};
result ¬ Dudley[Writer, cmd];
};
DoWriteAscii: PROC [h: IO.STREAM, root: TextNode.Ref, restoreDashes: BOOL ¬ FALSE] ~ {
node: TextNode.Ref ¬ root;
level: INTEGER ¬ 0;
levelDelta: INTEGER;
first: BOOL ¬ TRUE;
DO
text: TextNode.RefTextNode;
[node, levelDelta] ¬ TextNode.Forward[node];
IF node=NIL THEN EXIT;
level ¬ level+levelDelta;
text ¬ TextNode.NarrowToTextNode[node];
IF text = NIL THEN LOOP;
IF Rope.Length[text.rope] = 0 THEN LOOP;
IF first THEN first ¬ FALSE
ELSE IO.PutRope[h, "\n\n"]; -- carriage returns between nodes
THROUGH [1..level) DO IO.PutChar[h, '\t]; ENDLOOP; -- output level-1 tabs
SELECT text.format FROM
$body => IO.PutChar[h, '\t];
$equation => IO.PutRope[h, "\t\t\t"];
$display => NULL;
ENDCASE;
IO.PutRope[h, text.rope];
ENDLOOP;
{ENABLE IO.Error => IF ec = NotImplementedForThisStream OR ec = Failure -- Failure is raised when you use WriteAscii in a vux directory (Bier).
THEN GOTO Exit;
IO.SetLength[h, IO.GetIndex[h]]
};
EXITS Exit => RETURN;
};
WriteBrokenAsciiCommand: Commander.CommandProc ~ {
DoWriteBrokenAscii: PROC [h: IO.STREAM, root: TextNode.Ref, newline: ROPE] ~ {
node: TextNode.Ref ¬ root;
level: INTEGER ¬ 0;
levelDelta: INTEGER;
first: BOOL ¬ TRUE;
DO
text: TextNode.RefTextNode;
col, start, len: INT ¬ 0;
[node, levelDelta] ¬ TextNode.Forward[node];
IF node=NIL THEN EXIT;
level ¬ level+levelDelta;
text ¬ node;
IF text = NIL THEN LOOP;
IF first THEN first ¬ FALSE
ELSE {
n: NAT ~ (SELECT text.format FROM $body, $equation, $display => 2, ENDCASE => 1);
THROUGH [0..n) DO IO.PutRope[h, newline] ENDLOOP;
};
len ¬ text.rope.Length[];
WHILE start < len DO
end: INT ¬ -1;
hyph: BOOL ¬ FALSE;
indent: NAT ~ (level-1)*4;
THROUGH [0..indent) DO IO.PutChar[h, ' ] ENDLOOP;
col ¬ col + indent;
IF maxWidth>col THEN {
lim: INT ~ (end ¬ MIN[text.rope.SkipTo[start, "\r\l"], start+maxWidth-col]);
IF end<len AND NOT Newline[text.rope.Fetch[end]] THEN {
FOR end ¬ end, end-1 WHILE end > start AND NOT White[text.rope.Fetch[end]] DO NULL ENDLOOP;
IF end=start THEN {
end ¬ lim;
IF AlphaNumeric[text.rope.Fetch[end]] THEN FOR end ¬ end, end-1 WHILE end > start AND AlphaNumeric[text.rope.Fetch[end-1]] DO NULL ENDLOOP;
};
IF end # start THEN NULL
ELSE IF maxWidth=col+1 THEN end ¬ -1 ELSE hyph ¬ TRUE;
};
};
IF end = -1 THEN FOR end ¬ start+1, end+1 WHILE end < len AND AlphaNumeric[text.rope.Fetch[end]] DO NULL ENDLOOP;
IO.PutRope[h, text.rope.Substr[start, end-start]];
col ¬ col + end-start;
IF hyph THEN {IO.PutRope[h, "-"]; col ¬ col+1};
start ¬ text.rope.SkipOver[end, " \t"];
IF start < len THEN {
IO.PutRope[h, newline];
col ¬ 0;
start ¬ text.rope.SkipOver[start, "\r\l"];
IF start < len THEN { IO.PutRope[h, " "]; col ¬ col + 2 };
};
ENDLOOP;
ENDLOOP;
{ENABLE IO.Error => IF ec = NotImplementedForThisStream OR ec = Failure -- Failure is raised when you use WriteAscii in a vux directory (Bier).
THEN GOTO Exit;
IO.SetLength[h, IO.GetIndex[h]]
};
EXITS Exit => RETURN
};
newline: ROPE ~ WITH cmd.procData.clientData SELECT FROM
rope: ROPE => rope, ENDCASE => "\n";
Writer: DoWriteProc ~ { DoWriteBrokenAscii[out, root, newline] };
result ¬ Dudley[Writer, cmd];
};
maxWidth: INT ¬ 72;
White: PROC [c: CHAR] RETURNS [BOOL] ~ INLINE {RETURN [c=' OR c='\t]};
Newline: PROC [c: CHAR] RETURNS [BOOL] ~ INLINE {RETURN [c='\r OR c='\l]};
AlphaNumeric: PROC [c: CHAR] RETURNS [BOOL]
~ INLINE {RETURN[c IN ['a .. 'z] OR c IN ['A .. 'Z] OR c IN ['0 .. '9]]};
ChangeNewlinesCommand: Commander.CommandProc ~ {
to: ROPE ~ NARROW[cmd.procData.clientData];
ChangeNewlines: DoWriteProc ~ {
NodeProps.PutProp[n: root, name: $NewlineDelimiter, value: to];
root.comment ¬ TRUE;
FOR each: TextNode.Ref ¬ TextNode.StepForward[root], TextNode.StepForward[each] UNTIL each = NIL DO
size: INT ~ Rope.Size[each.rope];
FOR index: INT ¬ Rope.SkipTo[s: each.rope, pos: 0, skip: "\l\r"], Rope.SkipTo[s: each.rope, pos: index+1, skip: "\l\r"] UNTIL index = size DO
IF TextEdit.FetchChar[each, index].Set = 0 THEN
[] ¬ TextEdit.ReplaceByRope[root: root, dest: each, start: index, len: 1, rope: Rope.Substr[to, 0, 1]];
ENDLOOP;
ENDLOOP;
[] ¬ TiogaIO.ToStream[s: out, root: root];
};
result ¬ Dudley[writer: ChangeNewlines, cmd: cmd];
};
DoWriteProc: TYPE = PROC [out: IO.STREAM, root: TextNode.Ref];
HasTilde: PROC [fullFName: PFS.PATH] RETURNS [BOOL] ~ {
name: PFSNames.ComponentNamePart ~ PFSNames.ShortName[fullFName].name;
size: INT ~ Rope.Size[name.base];
start: INT ~ MIN[MAX[name.start, 0], size];
len: INT ~ MIN[MAX[name.len, 0], size-start];
RETURN[len > 0 AND Rope.Fetch[name.base, start+len-1] = '~];
};
Dudley: PROC [writer: DoWriteProc, cmd: Commander.Handle, readindent: BOOL ¬ FALSE] RETURNS [result: ATOM ¬ NIL] ~ {
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
PrintError: PROC [rope: ROPE] ~ {
cmd.err.PutRope[rope];
cmd.err.PutRope["\n"];
result ¬ $Failure;
};
FOR n: NAT IN [1..argv.argc) DO
DoToPattern: PROC [pattern: ROPE] ~ {
ForEachFile: PFS.NameProc ~ {
root: TextNode.Ref;
Process.CheckForAbort[];
IF HasTilde[name] THEN RETURN;
root ¬ Get[name, readindent ! PFS.Error => {
PrintError[error.explanation];
GOTO Punt;
}];
IF root = NIL THEN RETURN;
atLeastOne ¬ TRUE;
name ¬ PFSNames.StripVersionNumber[name];
IO.PutChar[cmd.out, ' ];
IO.PutRope[cmd.out, PFS.RopeFromPath[name]];
IO.PutChar[cmd.out, ' ];
{
h: IO.STREAM ¬ PFS.StreamOpen[fileName: name, accessOptions: $create];
writer[h, root];
IO.Close[h];
};
IO.PutRope[cmd.out, "\n"];
EXITS Punt => {}
};
atLeastOne: BOOL ¬ FALSE;
IF Rope.Find[pattern, "!"] = -1 THEN pattern ¬ Rope.Concat[pattern, "!h"];
PFS.EnumerateForNames[PFS.PathFromRope[pattern], ForEachFile
! PFS.Error => { PrintError[error.explanation]; CONTINUE }];
IF NOT atLeastOne THEN PrintError[Rope.Concat["no matches for pattern: ", pattern]];
};
IF Rope.Size[argv[n]] = 1 AND Rope.Fetch[argv[n], 0] IN ['1..'9]
THEN tabIndent ¬ LOOPHOLE[Rope.Fetch[argv[n], 0]-'1]
ELSE DoToPattern[argv[n]];
ENDLOOP;
};
Commander.Register["TiogaMesa", TiogaMesaCommand, "Convert Mesa files to Tioga tree format.\nNumber in line gives spaces/tab -- must be in [1..9]; default is 8"];
Commander.Register["TiogaMesaLooks", TiogaMesaLooksCommand, "Add mesa looks to a Tioga document"];
Commander.Register["ReadIndent", ReadIndentCommand, "Convert to tree structure based on indenting."];
Commander.Register["WritePlain", WritePlainCommand, "Convert Tioga file to unformatted file."];
Commander.Register[key: "WriteBrokenAscii", proc: WriteBrokenAsciiCommand,
doc: "Convert Tioga file to unformatted ASCII, with lines broken.",
clientData: NIL];
Commander.Register[key: "CRWriteBrokenAscii", proc: WriteBrokenAsciiCommand,
doc: "Convert Tioga file to unformatted ASCII; break lines with \\r.",
clientData: Rope.Literal["\r"]];
Commander.Register[key: "LFWriteBrokenAscii", proc: WriteBrokenAsciiCommand,
doc: "Convert Tioga file to unformatted ASCII; break lines with \\l.",
clientData: Rope.Literal["\l"]];
Commander.Register[key: "CRLFWriteBrokenAscii", proc: WriteBrokenAsciiCommand,
doc: "Convert Tioga file to unformatted ASCII; break lines with \\r\\l.",
clientData: Rope.Literal["\r\l"]];
Commander.Register["WriteAscii", WriteAsciiCommand, "Convert Tioga file to unformatted ASCII."];
Commander.Register["WriteMesaPlain", WriteMesaPlainCommand, "Convert Tioga file to unformatted file.\n\tRestores dashes before comments if necessary."];
Commander.Register[key: "LFNewlines", proc: ChangeNewlinesCommand, doc: "Convert Tioga file use LineFeed as a newline delimiter.", clientData: Rope.Literal["\l"], interpreted: TRUE];
Commander.Register[key: "CRNewlines", proc: ChangeNewlinesCommand, doc: "Convert Tioga file use CarriageReturn as a newline delimiter.", clientData: Rope.Literal["\r"], interpreted: TRUE];
Commander.Register[key: "SetLevelIndent", proc: SetLevelIndent, doc: "Defines indentation for WritePlain and WriteMesaPlain"];
END.