<> <> <> <> <> DIRECTORY Commander USING [CommandProc, CommandProcHandle, CommandProcObject, Enumerate, Handle, Lookup], CommandTool, CommandToolExtras, Convert USING [Error, RopeFromLiteral], FileNames USING [ConvertToSlashFormat, CurrentWorkingDirectory, Directory, FileWithSearchRules, ResolveRelativePath], FS USING [Error, Open, OpenFile, nullOpenFile, StreamFromOpenFile], IO USING [Close, CreateStream, CreateStreamProcs, GetInfo, PutChar, PutRope, RopeFromROS, ROS, STREAM], IOUtils USING [closedStreamProcs], List USING [AList, Append, Assoc, CopyTopList, DotCons, Memb, PutAssoc, Remove], ProcessExtras USING [CheckForAbort], ProcessProps USING [AddPropList], ReadEvalPrint USING [Handle], Rope USING [Cat, Concat, Equal, Fetch, Find, IsEmpty, Length, Match, Replace, ROPE, Substr], RopeFrom USING [Stream], ViewerClasses USING [Viewer]; CommandToolUtilitiesImpl: CEDAR PROGRAM IMPORTS Commander, CommandTool, Convert, FileNames, FS, IO, IOUtils, List, ProcessExtras, ProcessProps, Rope, RopeFrom EXPORTS CommandTool, CommandToolExtras SHARES IO = BEGIN <<>> commandFileProcData: Commander.CommandProcHandle _ NEW[Commander.CommandProcObject _ [CommandTool.CommandFile]]; GetViewer: PUBLIC PROC [cmd: Commander.Handle] RETURNS [ViewerClasses.Viewer] = { rep: ReadEvalPrint.Handle _ NIL; WHILE rep = NIL DO rep _ NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cmd.propertyList]]; IF rep # NIL AND rep.viewer # NIL THEN RETURN[rep.viewer] ELSE rep _ NIL; cmd _ NARROW[List.Assoc[key: $ParentCommander, aList: cmd.propertyList]]; IF cmd = NIL THEN EXIT; ENDLOOP; RETURN[NIL]; }; GetReadEvalPrint: PUBLIC PROC [cmd: Commander.Handle, topLevel: BOOL _ FALSE] RETURNS [ReadEvalPrint.Handle] = { rep: ReadEvalPrint.Handle _ NIL; <> WHILE rep = NIL DO rep _ NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cmd.propertyList]]; IF rep # NIL AND (NOT topLevel OR rep.topLevel) THEN EXIT ELSE rep _ NIL; cmd _ NARROW[List.Assoc[key: $ParentCommander, aList: cmd.propertyList]]; IF cmd = NIL THEN EXIT; ENDLOOP; RETURN[rep]; }; <> CopyAList: PUBLIC PROC [old: List.AList] RETURNS [new: List.AList] = { tail: List.AList _ NIL; new _ NIL; UNTIL old = NIL DO newItem: List.AList _ LIST[List.DotCons[key: old.first.key, val: old.first.val]]; IF tail = NIL THEN new _ newItem ELSE tail.rest _ newItem; old _ old.rest; tail _ newItem; ENDLOOP; }; <> PutLocalProperty: PUBLIC PROC [key, val: REF ANY, aList: List.AList, origList: List.AList _ NIL] RETURNS [List.AList] = { newList: List.AList; FOR l: List.AList _ aList, l.rest WHILE l # NIL AND l # origList DO IF l.first.key = key THEN { l.first.val _ val; RETURN[aList]; }; ENDLOOP; newList _ LIST[List.DotCons[key: key, val: val]]; newList.rest _ aList; RETURN[newList]; }; <> Insulate: PUBLIC PROC [stream: IO.STREAM] RETURNS [safeStream: IO.STREAM] = { <> safeStream _ IO.CreateStream[streamProcs: IO.CreateStreamProcs[variety: stream.GetInfo[].variety, class: stream.GetInfo[].class, close: IOUtils.closedStreamProcs.close], streamData: NIL, backingStream: stream]; }; <> <<>> <> CallList: PUBLIC PROC [property: REF ANY, cmd: Commander.Handle, proc: PROC [result: REF, msg: Rope.ROPE] RETURNS [stop: BOOL]] = { result: REF; msg: Rope.ROPE; WITH List.Assoc[key: property, aList: cmd.propertyList] SELECT FROM list: LIST OF REF ANY => FOR l: LIST OF REF ANY _ list, l.rest WHILE l # NIL DO WITH l.first SELECT FROM cpc: Commander.CommandProcHandle => { [result: result, msg: msg] _ cpc.proc[cmd]; <> <> <> IF proc # NIL AND proc[result: result, msg: msg] THEN EXIT; }; ENDCASE; ENDLOOP; ENDCASE; }; <> AddProcToList: PUBLIC PROC [aList: List.AList, listKey: REF ANY, proc: Commander.CommandProcHandle, append: BOOL _ TRUE] RETURNS [List.AList] = { maybeList: REF ANY _ List.Assoc[key: listKey, aList: aList]; list: LIST OF REF ANY; alreadyThere: BOOL; IF maybeList # NIL AND ISTYPE[maybeList, LIST OF REF ANY] THEN list _ NARROW[maybeList]; alreadyThere _ list # NIL AND List.Memb[ref: proc, list: list]; IF NOT alreadyThere THEN { IF append THEN { ra: REF ANY _ proc; list _ List.Append[list, LIST[ra]]; } ELSE list _ CONS[proc, list]; aList _ List.PutAssoc[key: listKey, val: list, aList: aList]; }; RETURN[aList]; }; <> RemoveProcFromList: PUBLIC PROC [aList: List.AList, listKey: REF ANY, proc: Commander.CommandProcHandle] RETURNS [List.AList] = { maybeList: REF ANY _ List.Assoc[key: listKey, aList: aList]; list: LIST OF REF ANY; alreadyThere: BOOL; IF maybeList # NIL AND ISTYPE[maybeList, LIST OF REF ANY] THEN list _ NARROW[maybeList]; alreadyThere _ list # NIL AND List.Memb[ref: proc, list: list]; IF alreadyThere THEN aList _ List.PutAssoc[key: listKey, val: List.Remove[ref: proc, list: list], aList: aList]; RETURN[aList]; }; <<>> <> FileWithSearchRules: PUBLIC PROC [root: Rope.ROPE, defaultExtension: Rope.ROPE, cmd: Commander.Handle] RETURNS [fullPath: Rope.ROPE _ NIL] = { RETURN[FileNames.FileWithSearchRules[root: root, defaultExtension: defaultExtension, searchRules: List.Assoc[key: $SearchRules, aList: cmd.propertyList]].fullPath]; }; <> <> ResolveRelativePath: PUBLIC PROC [path: Rope.ROPE] RETURNS [Rope.ROPE] = { RETURN[FileNames.ResolveRelativePath[path]]; }; ConvertToSlashFormat: PUBLIC PROC [path: Rope.ROPE] RETURNS [Rope.ROPE] = { RETURN[FileNames.ConvertToSlashFormat[path]]; }; <> CopyListOfRefAny: PUBLIC PROC [key: REF ANY, aList: List.AList] RETURNS [List.AList] = { ref: REF ANY _ List.Assoc[key: key, aList: aList]; IF ref = NIL THEN RETURN[aList]; WITH ref SELECT FROM lra: LIST OF REF ANY => RETURN[List.PutAssoc[key: key, val: List.CopyTopList[lra], aList: aList]]; ENDCASE; RETURN[aList]; }; <> AddSearchRule: PUBLIC PROC [cmd: Commander.Handle, dir: Rope.ROPE, append: BOOL _ TRUE] = { rules: LIST OF REF ANY; length: INT; first, last: CHAR; rules _ NARROW[List.Assoc[key: $SearchRules, aList: cmd.propertyList]]; IF dir = NIL THEN rules _ NIL ELSE { dir _ FileNames.ResolveRelativePath[dir]; length _ dir.Length[]; IF length < 3 THEN RETURN; dir _ FileNames.ConvertToSlashFormat[dir]; first _ dir.Fetch[0]; last _ dir.Fetch[length - 1]; IF first # '/ THEN RETURN; IF last # '/ THEN dir _ Rope.Concat[dir, "/"]; <> FOR r: LIST OF REF ANY _ rules, r.rest WHILE r # NIL DO IF Rope.Equal[NARROW[r.first, Rope.ROPE], dir, FALSE] THEN RETURN; ENDLOOP; IF append THEN { ra: REF ANY _ dir; rules _ List.Append[rules, LIST[ra]]; } ELSE rules _ CONS[dir, rules]; }; [] _ List.PutAssoc[key: $SearchRules, val: rules, aList: cmd.propertyList]; }; CurrentWorkingDirectory: PUBLIC PROC RETURNS [Rope.ROPE] = { RETURN[FileNames.CurrentWorkingDirectory[]]; }; <<>> <> <> <> <> <> <> <> <> <> <> <> <> <> LookupWithSearchRules: PUBLIC Commander.CommandProc = { root: Rope.ROPE _ cmd.command; wDir: Rope.ROPE _ NIL; wDirRoot: Rope.ROPE _ NIL; temp: Rope.ROPE _ NIL; ambiguous: BOOL _ List.Assoc[key: $Result, aList: cmd.propertyList] = $Ambiguous; rules: LIST OF REF ANY; fullPath: BOOL; Try: PROC [name: Rope.ROPE] RETURNS [foundSomething: BOOL] = { cmd.procData _ Commander.Lookup[name]; IF cmd.procData # NIL AND cmd.procData.proc # NIL THEN { cmd.command _ name; RETURN[TRUE]; }; cmd.procData _ NIL; RETURN[FALSE]; }; UniqueMatch: PROC [name: Rope.ROPE] RETURNS [foundSomething: BOOL _ FALSE] = { lst: LIST OF Rope.ROPE _ NIL; p: PROC [key: Rope.ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL _ FALSE] = { IF procData.proc # NIL AND Rope.Match[pattern: name, object: key, case: FALSE] THEN lst _ CONS[key, lst]; }; name _ Rope.Concat[name, "*"]; [] _ Commander.Enumerate[p]; IF lst = NIL THEN RETURN[FALSE]; IF lst.rest # NIL THEN { -- ambiguous prefix ros: IO.STREAM _ IO.ROS[]; ambiguous _ TRUE; result _ $Ambiguous; ros.PutRope[" . . . command ambiguous ( "]; WHILE lst # NIL DO ros.PutRope[lst.first]; ros.PutChar[' ]; lst _ lst.rest; ENDLOOP; ros.PutRope[")\n"]; msg _ IO.RopeFromROS[ros]; RETURN[TRUE]; }; <> cmd.command _ lst.first; cmd.procData _ Commander.Lookup[cmd.command]; RETURN[TRUE]; }; <<>> result _ NIL; IF root.Length[] = 0 THEN RETURN[$Failure, NIL]; root _ FileNames.ResolveRelativePath[root]; root _ FileNames.ConvertToSlashFormat[root]; <> IF Try[root] THEN RETURN; <<>> fullPath _ root.Fetch[0] = '/; IF NOT fullPath THEN { <> wDir _ FileNames.CurrentWorkingDirectory[]; wDirRoot _ Rope.Concat[wDir, root]; IF Try[wDirRoot] THEN RETURN; <<>> rules _ NARROW[List.Assoc[key: $SearchRules, aList: cmd.propertyList]]; <> <> FOR list: LIST OF REF ANY _ rules, list.rest WHILE list # NIL DO <> IF Try[Rope.Concat[NARROW[list.first], root]] THEN RETURN; ENDLOOP; }; <<>> <> IF ambiguous OR UniqueMatch[root] THEN RETURN; IF NOT fullPath THEN { <<>> <> IF ambiguous OR UniqueMatch[wDirRoot] THEN RETURN; <<>> <> <> FOR list: LIST OF REF ANY _ rules, list.rest WHILE list # NIL DO IF ambiguous OR UniqueMatch[Rope.Concat[NARROW[list.first], root]] THEN RETURN; ENDLOOP; }; }; <> <> <> CommandFileWithSearchRules: PUBLIC Commander.CommandProc = { commandFileName: Rope.ROPE; ambiguous: BOOL _ List.Assoc[key: $Result, aList: cmd.propertyList] = $Ambiguous; [commandFileName, ambiguous] _ FileNames.FileWithSearchRules[root: cmd.command, defaultExtension: ".cm", requireExtension: FALSE, requireExact: ambiguous, searchRules: List.Assoc[key: $SearchRules, aList: cmd.propertyList]]; result _ NIL; IF ambiguous THEN result _ $Ambiguous; IF commandFileName.IsEmpty[] THEN RETURN; cmd.commandLine _ Rope.Concat[commandFileName, " "]; cmd.command _ "Commander"; cmd.procData _ commandFileProcData; }; <> <> <> <<>> LoadAndRunWithSearchRules: PUBLIC Commander.CommandProc = { commandFileName: Rope.ROPE; ambiguous: BOOL; sc: SavedCommand _ NIL; IF cmd.command.Find["."] # -1 THEN RETURN; ambiguous _ List.Assoc[key: $Result, aList: cmd.propertyList] = $Ambiguous; [commandFileName, ambiguous] _ FileNames.FileWithSearchRules[root: cmd.command, defaultExtension: ".load", requireExtension: TRUE, requireExact: ambiguous, searchRules: List.Assoc[key: $SearchRules, aList: cmd.propertyList]]; result _ NIL; IF ambiguous THEN result _ $Ambiguous; IF commandFileName.IsEmpty[] THEN RETURN; <> sc _ NEW[SavedCommandObject _ [command: cmd.command, commandFileName: commandFileName]]; cmd.procData _ NEW[Commander.CommandProcObject _ [ReallyLoadAndRun, "Command not yet loaded", sc]]; }; <> SavedCommand: TYPE = REF SavedCommandObject; SavedCommandObject: TYPE = RECORD [ command: Rope.ROPE _ NIL, commandFileName: Rope.ROPE _ NIL ]; ReallyLookupHandle: Commander.CommandProcHandle _ NEW[Commander.CommandProcObject _ [ReallyLoadAndRun, "ReallyLoadAndRun", "execute .load file and run command"]]; <> ReallyLoadAndRun: Commander.CommandProc = { sc: SavedCommand _ NARROW[cmd.procData.clientData]; commandFileName: Rope.ROPE _ sc.commandFileName; originalCommandLine: Rope.ROPE _ cmd.commandLine; oldOut: IO.STREAM; oldIn: IO.STREAM; loadFileDirectory: Rope.ROPE; inner: PROC = { ENABLE UNWIND => {cmd.out _ oldOut; cmd.in _ oldIn}; [result, msg] _ CommandTool.CommandFile[cmd]; cmd.out _ oldOut; cmd.in _ oldIn; }; commandFileName _ FileNames.ConvertToSlashFormat[commandFileName]; loadFileDirectory _ FileNames.Directory[path: commandFileName]; cmd.command _ "CommandTool"; cmd.commandLine _ commandFileName; cmd.procData _ commandFileProcData; oldOut _ cmd.out; cmd.out _ Insulate[cmd.err]; oldIn _ cmd.in; cmd.in _ Insulate[oldIn]; <> ProcessProps.AddPropList[ List.PutAssoc[key: $WorkingDirectory, val: loadFileDirectory, aList: NIL], inner]; IF result = $Failure THEN RETURN[result, msg]; cmd.command _ sc.command; cmd.commandLine _ originalCommandLine; [result, msg] _ LookupWithSearchRules[cmd]; IF cmd.procData # NIL AND cmd.procData.proc # NIL THEN { <> CommandTool.ExecuteCommand[cmd, FALSE]; result _ List.Assoc[key: $Result, aList: cmd.propertyList]; } ELSE RETURN[$Failure, ".load file failed to register command"]; }; AtSignLimit: NAT _ 20; AtSignFile: PROC [name: Rope.ROPE] RETURNS [contents: Rope.ROPE] = { file: FS.OpenFile _ FS.nullOpenFile; fileStream: IO.STREAM; { ENABLE UNWIND => { IF fileStream # NIL THEN fileStream.Close[]; }; name _ FileNames.ResolveRelativePath[name]; file _ FS.Open[name: name ! FS.Error => IF error.group = user THEN CONTINUE]; IF file = FS.nullOpenFile THEN file _ FS.Open[name: Rope.Concat[name, ".cm"] ! FS.Error => IF error.group = user THEN CONTINUE]; IF file = FS.nullOpenFile THEN ERROR CommandTool.Failed[Rope.Cat["Can't find file |", name, "|"]]; <> fileStream _ FS.StreamFromOpenFile[openFile: file]; contents _ RopeFrom.Stream[stream: fileStream]; fileStream.Close[]; }; }; <<>> <> Pass1: PUBLIC PROC [initial: Rope.ROPE, nameOnly: BOOL] RETURNS [first: Rope.ROPE _ NIL, rest: Rope.ROPE _ NIL, terminator: CHAR _ '\n, someExpansion: BOOL _ FALSE] = { { state: {outside, insideQuotes, insideAtName} _ outside; c: CHAR; i: INT _ 0; atPosition: INT; atSignDepth: NAT _ 0; startPosition: INT _ 0; pastLeadingWhiteSpace: BOOL _ FALSE; IF NOT nameOnly THEN pastLeadingWhiteSpace _ TRUE; WHILE i < initial.Length[] DO ProcessExtras.CheckForAbort[]; { c _ initial.Fetch[i]; IF c = '\\ THEN { slashPosition: INT _ i; slashSequenceLength: INT _ 2; escapeRope: Rope.ROPE; i _ i + 1; -- pointing at char after the slash IF i >= initial.Length[] THEN ERROR CommandTool.Failed["Backslash at end of command"]; c _ initial.Fetch[i]; -- get the char after the slash IF c IN ['0..'9] THEN { IF (i + 2) >= initial.Length[] THEN ERROR CommandTool.Failed["Not enough characters for backslash convention"]; slashSequenceLength _ 4; }; escapeRope _ Convert.RopeFromLiteral[Rope.Cat["""", initial.Substr[start: slashPosition, len: slashSequenceLength], """"] ! Convert.Error => { ERROR CommandTool.Failed["Backslash sequence error"]}]; initial _ Rope.Replace[base: initial, start: slashPosition, len: slashSequenceLength, with: escapeRope]; i _ slashPosition; c _ initial.Fetch[i]; }; SELECT state FROM outside => { SELECT c FROM '" => { state _ insideQuotes; pastLeadingWhiteSpace _ TRUE; }; ';, '\n, '| => { terminator _ c; GOTO FoundTerminator; }; '@ => { state _ insideAtName; atPosition _ i; }; ' , ' => { IF nameOnly THEN { IF pastLeadingWhiteSpace THEN { terminator _ ' ; GOTO FoundTerminator; } ELSE startPosition _ startPosition + 1; }; }; '& => { IF nameOnly THEN { -- end of name terminator _ c; GOTO FoundTerminator; }; IF initial.Length[] > (i + 1) THEN { SELECT initial.Fetch[i+1] FROM ' , ' , '\n, '; => { -- followed by whitespace terminator _ c; GOTO FoundTerminator; }; ENDCASE => NULL; } ELSE { -- end of line terminator _ c; GOTO FoundTerminator; } }; ENDCASE => pastLeadingWhiteSpace _ TRUE; }; insideQuotes => { IF c = '" THEN { IF initial.Length[] > (i + 1) AND initial.Fetch[i+1] = '" THEN i _ i + 1 ELSE state _ outside; }; }; insideAtName => { IF c = '@ OR c = ' OR c = '\n THEN { nameLength: INT _ i - atPosition - 1; atSignFileName: Rope.ROPE _ initial.Substr[start: atPosition + 1, len: nameLength]; initial _ Rope.Replace[base: initial, start: atPosition, len: IF c = '@ THEN nameLength + 2 ELSE nameLength + 1, with: AtSignFile[name: atSignFileName]]; i _ atPosition - 1; state _ outside; atSignDepth _ atSignDepth + 1; IF atSignDepth > AtSignLimit THEN ERROR CommandTool.Failed["Exceeded limit on expansion of @ command files"]; someExpansion _ TRUE; }; }; ENDCASE => ERROR; i _ i + 1; }; REPEAT FoundTerminator => { rest _ Rope.Substr[base: initial, start: i + 1]; initial _ Rope.Substr[base: initial, start: startPosition, len: i - startPosition]; IF NOT nameOnly THEN initial _ Rope.Concat[initial, "\n"]; }; ENDLOOP; IF state = insideQuotes THEN ERROR CommandTool.Failed["Mismatched quotes"]; IF state = insideAtName THEN ERROR CommandTool.Failed["Improper @-file specification"]; }; first _ initial; }; END. October 7, 1983 9:38 am, Stewart, Created December 13, 1983 4:14 pm, Stewart, fixed bug in CallList January 14, 1984 7:45 pm, Stewart, fixed bugs in ReallyLoadAndRun