<<>> <> <> <> <> <> <> <> <> <> <> <> <> <> <<>> <> <> <> <<>> 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; <> 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 <> 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 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.