<<>> <> <> <> <> <> <> <> <> <> DIRECTORY Atom USING [DottedPair, GetPName, MakeAtom, PropList, PutPropOnList], BasicTime USING [GetClockPulses, GMT, Now, Period, Pulses, PulsesToMicroseconds, PulsesToSeconds], Commander, CommanderBackdoor, CommanderOps, CommanderRegistry USING [ EnumeratePattern ], Convert, ConvertUnsafe USING [ToRope], InstallationComforts USING [ProcName], IO, List, Process USING [CheckForAbort, GetPriority, Pause, Priority, priorityBackground, priorityForeground, priorityNormal, SecondsToTicks, SetPriority], ProcessProps USING [GetPropList], RefText USING [TrustTextAsRope], Rope, RopeList USING [IgnoreCase, Reverse, Sort], SymTab, SystemVersion USING [release]; CommanderBasicCommandsImpl: CEDAR PROGRAM IMPORTS Atom, BasicTime, Commander, CommanderBackdoor, CommanderOps, CommanderRegistry, Convert, ConvertUnsafe, InstallationComforts, IO, List, Process, ProcessProps, Rope, RefText, RopeList, SymTab, SystemVersion ~ BEGIN ProcAny: TYPE = PROC ANY RETURNS ANY; ROPE: TYPE ~ Rope.ROPE; AliasCell: TYPE = REF AliasCellObject; AliasCellObject: TYPE = RECORD [ args: LIST OF ROPE ¬ NIL, def: ROPE, quiet: BOOL ¬ FALSE, -- don't expand the alias at command time originalProc: Commander.CommandProc ¬ NIL ]; <> AbortCommand: Commander.CommandProc = { ERROR ABORTED }; AliasCommand: Commander.CommandProc = { commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; quiet: BOOL ~ FALSE; name, def: ROPE; args: LIST OF ROPE ¬ NIL; aliasCell: AliasCell; originalProc: Commander.CommandProc ¬ NIL; { ENABLE { IO.EndOfStream, IO.Error => GOTO Die; }; name ¬ GetWord[commandLineStream]; [] ¬ IO.SkipWhitespace[commandLineStream, FALSE]; IF NOT IO.EndOf[commandLineStream] AND IO.PeekChar[commandLineStream] = '( THEN { FOR l: LIST OF REF ANY ¬ NARROW[IO.GetRefAny[commandLineStream]], l.rest UNTIL l = NIL DO WITH l.first SELECT FROM r: ROPE => args ¬ CONS[r, args]; a: ATOM => args ¬ CONS[Atom.GetPName[a], args]; ENDCASE => GOTO Die; ENDLOOP; TRUSTED {args ¬ RopeList.Reverse[args]; }; }; def ¬ RopeSubst[old: "\n", new: " ", base: IO.GetRope[commandLineStream]]; IF Rope.Equal[GetWord[IO.RIS[def]], name, FALSE] THEN { -- recursive definition procData: Commander.CommandProcHandle ¬ Commander.Lookup[name]; IF procData # NIL THEN originalProc ¬ procData.proc ELSE RETURN[$Failure, "undefined recursive alias"]; }; aliasCell ¬ NEW[AliasCellObject ¬ [args, def, quiet, originalProc]]; Commander.Register[ key: name, proc: AliasImplProc, doc: Rope.Concat["Alias ", cmd.commandLine], clientData: aliasCell]; IO.Close[commandLineStream]; EXITS Die => CommanderOps.Failed["bad parameter list for alias definition"]; }; }; AliasImplProc: Commander.CommandProc = { aliasCell: AliasCell ¬ NARROW[cmd.procData.clientData]; <> newCommandLine: ROPE ¬ aliasCell.def; commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; SynonymRecord: TYPE = RECORD [key, val: ROPE]; synonyms: LIST OF REF SynonymRecord ¬ NIL; gennum: CARD ¬ 1; UniqueRope: PROC RETURNS [ROPE] = { gennum ¬ gennum + 1; RETURN[Rope.Cat["\200", Convert.RopeFromInt[gennum, 10, FALSE], "\201"]]; }; FOR l: LIST OF ROPE ¬ aliasCell.args, l.rest UNTIL l = NIL DO token: CommanderOps.Token ~ CommanderOps.GetCmdToken[commandLineStream]; new: ROPE ¬ token.literal; IF aliasCell.args.rest # NIL THEN { <> dummy: ROPE = UniqueRope[]; synonyms ¬ CONS[NEW[SynonymRecord ¬ [new, dummy]], synonyms]; new ¬ dummy; }; newCommandLine ¬ RopeSubst[old: l.first, new: new, base: newCommandLine]; ENDLOOP; FOR l: LIST OF REF SynonymRecord ¬ synonyms, l.rest UNTIL l = NIL DO newCommandLine ¬ RopeSubst[old: l.first.val, new: l.first.key, base: newCommandLine]; ENDLOOP; newCommandLine ¬ Rope.Concat[newCommandLine, IO.GetRope[commandLineStream]]; IO.Close[commandLineStream]; IF aliasCell.originalProc # NIL THEN { s: IO.STREAM = IO.RIS[newCommandLine]; cmd.command ¬ GetWord[s]; cmd.commandLine ¬ IO.GetTokenRope[s, NewlineBreak].token; [result, msg] ¬ aliasCell.originalProc[cmd]; } ELSE result ¬ CommanderOps.DoCommand[newCommandLine, cmd]; }; AliasesCommand: Commander.CommandProc = { matchList: LIST OF ROPE ¬ NIL; EachCommand: Commander.EnumerateAction = { <<[key: ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL _ FALSE]>> WITH procData.clientData SELECT FROM aliasCell: AliasCell => { matchList ¬ CONS[key, matchList]; }; ENDCASE => NULL; }; [] ¬ Commander.Enumerate[EachCommand]; matchList ¬ RopeList.Sort[list: matchList, compareProc: RopeList.IgnoreCase]; FOR each: LIST OF ROPE ¬ matchList, each.rest WHILE each # NIL DO name: ROPE ¬ each.first; procData: Commander.CommandProcHandle ¬ Commander.Lookup[name]; WITH procData.clientData SELECT FROM aliasCell: AliasCell => { IF NOT Rope.Match["(formerly *)*", procData.doc] THEN IO.PutRope[cmd.out, procData.doc]; -- there are already newlines there }; ENDCASE => NULL; ENDLOOP; }; GetWord: PROC [s: IO.STREAM] RETURNS [r: ROPE] ~ { r ¬ CommanderOps.GetCmdToken[s].value }; RopeMemb: PROC [rope: ROPE, lst: LIST OF ROPE] RETURNS[BOOL] = { FOR l: LIST OF ROPE ¬ lst, l.rest UNTIL l = NIL DO IF Rope.Equal[rope, l.first, FALSE] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE] }; AnswerbackCommand: PROC [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- = { s: IO.STREAM _ IO.RIS[cmd.commandLine]; result _ s.GetRefAny[ ! IO.EndOfStream => CONTINUE]; IF result # NIL THEN msg _ s.GetLineRope[]; s.Close[]; }; CommentCommand: Commander.CommandProc = { }; CommanderCommand: Commander.CommandProc = { child: Commander.Handle ~ CommanderOps.CreateFromStreams[parentCommander: cmd]; add: Atom.PropList ¬ NIL; DO keyRope: ROPE ~ CommanderOps.NextArgument[cmd]; val: ROPE ~ CommanderOps.NextArgument[cmd]; IF keyRope = NIL THEN EXIT; add ¬ Atom.PutPropOnList[propList: add, prop: Atom.MakeAtom[keyRope], val: val]; ENDLOOP; UNTIL add = NIL DO t: Atom.PropList ~ add; add ¬ t.rest; t.rest ¬ child.propertyList; child.propertyList ¬ t; ENDLOOP; IF CommanderOps.ReadEvalPrintLoop[child].hadFailure THEN result ¬ $Failure; }; DateCommand: Commander.CommandProc = { IO.PutF1[cmd.out, "%g\n", [time[BasicTime.Now[]]]]; }; EchoCommand: Commander.CommandProc = { FirstArg : BOOLEAN ¬ TRUE; nFlag : BOOLEAN ¬ TRUE; sFlag : BOOLEAN ¬ TRUE; argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd]; FOR i: NAT IN [1..argv.argc) DO arg : ROPE ¬ argv[i]; FirstArg ¬ FALSE; SELECT TRUE FROM Rope.Equal[arg, "-n", TRUE] => nFlag ¬ ~ nFlag; Rope.Equal[arg, "-s", TRUE] => sFlag ¬ ~ sFlag; ENDCASE => { IF sFlag AND ~ FirstArg THEN cmd.out.PutChar[' ]; cmd.out.PutRope[argv[i]]; }; Process.CheckForAbort[]; ENDLOOP; IF nFlag THEN cmd.out.PutChar['\n]; }; ErrorCommand: Commander.CommandProc = { ERROR }; FailCommand: Commander.CommandProc = { result ¬ $Failure; msg ¬ cmd.commandLine; IF Rope.Match[" *\n", msg] THEN msg ¬ Rope.Substr[msg, 1, Rope.Size[msg]-2]; }; FailSkipCommand: Commander.CommandProc = { result ¬ CommanderOps.GetProp[cmd, $Result]; IF result = $Failure THEN { IF Rope.SkipOver[s: cmd.commandLine, skip: " \t\r\l"] = Rope.Size[cmd.commandLine] THEN result ¬ NIL ELSE msg ¬ Rope.Concat["Skipped: ", cmd.commandLine]; RETURN }; [result: result] ¬ CommanderOps.ExecuteCommand[cmd, cmd.commandLine]; }; IsCommandPrefix: PROC [prefix, command: ROPE] RETURNS [is: BOOL] = { pLen: INT = prefix.Size[]; cLen: INT = command.Size[]; c: CHAR; first, afterLast: INT ¬ 0; FOR afterLast ¬ 0, afterLast+1 WHILE afterLast < cLen AND (c ¬ command.Fetch[afterLast]) # ' DO SELECT c FROM '/, '> => first ¬ afterLast+1; ENDCASE; ENDLOOP; is ¬ pLen <= afterLast-first AND prefix.Equal[command.Substr[start: first, len: pLen], FALSE]; }; HistoryCommand: Commander.CommandProc = { prompt: ROPE = NARROW[List.Assoc[key: $Prompt, aList: cmd.propertyList]]; toGive: LIST OF ROPE ¬ NIL; includeDuplicates: BOOL ¬ TRUE; includePrompt: BOOL ¬ TRUE; n: INT ¬ 10; seen: SymTab.Ref; commandToolData: CommanderBackdoor.CommandToolData ¬ CommanderBackdoor.GetCommandToolData[CommanderBackdoor.AdamOrEve[cmd]]; h: CommanderBackdoor.CommandHistoryList ¬ commandToolData.history; cls: IO.STREAM = IO.RIS[cmd.commandLine]; FOR i: INT ¬ cls.SkipWhitespace[], cls.SkipWhitespace[] WHILE NOT cls.EndOf[] DO toke: ROPE = cls.GetTokenRope[IO.IDProc].token; SELECT TRUE FROM toke.Equal["-%"] => includePrompt ¬ FALSE; toke.Equal["+%"] => includePrompt ¬ TRUE; toke.Equal["-d"] => includeDuplicates ¬ FALSE; toke.Equal["+d"] => includeDuplicates ¬ TRUE; ENDCASE => n ¬ Convert.IntFromRope[toke !Convert.Error => GOTO GiveUsage]; ENDLOOP; cls.Close[]; IF NOT includeDuplicates THEN seen ¬ SymTab.Create[case: FALSE]; WHILE n > 0 DO cr: ROPE; IF h = NIL THEN EXIT; cr ¬ h.first.wholeCommandLine; IF NOT Rope.Equal[cr, ""] THEN { IF includeDuplicates OR NOT seen.Fetch[cr].found THEN { toGive ¬ CONS[cr, toGive]; IF NOT includeDuplicates THEN [] ¬ seen.Insert[cr, $seen]; n ¬ n - 1; }; }; h ¬ h.rest; ENDLOOP; FOR toGive ¬ toGive, toGive.rest WHILE toGive # NIL DO IF includePrompt THEN cmd.out.PutRope["% "]; cmd.out.PutF1["%g\n", [rope[toGive.first]]]; ENDLOOP; result ¬ $OK; EXITS GiveUsage => {result ¬ $Failure; msg ¬ "Usage: History "} }; IndentCommand: Commander.CommandProc = { prefix: REF TEXT; terminator: ROPE ¬ NIL; terminatorSize: NAT ¬ 0; line: REF TEXT ¬ NEW[TEXT[200]]; argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd]; IF argv.argc > 2 OR (argv.argc < 2 AND cmd.procData.clientData # $Indent) THEN {msg ¬ Rope.Concat["Usage: ", cmd.procData.doc]; GO TO oops}; SELECT cmd.procData.clientData FROM $Indent => { depth: INT; depthNAT: NAT; IF argv.argc < 2 THEN { depth ¬ 8; depthNAT ¬ 8; } ELSE { depth ¬ Convert.IntFromRope[argv[1] ! Convert.Error => {msg ¬ "Bad arg"; GO TO oops}]; IF depth NOT IN [0..80] THEN {msg ¬ "depth should be in [0..80]"; GO TO oops}; depthNAT ¬ depth; }; prefix ¬ NEW[TEXT[depthNAT]]; FOR i: NAT IN [0..depthNAT) DO prefix[i] ¬ ' ; ENDLOOP; prefix.length ¬ depthNAT; }; $PrefixLines => { prefix ¬ Rope.ToRefText[argv[1]] }; $ReadTo => { terminator ¬ argv[1]; terminatorSize ¬ Rope.Size[terminator] }; ENDCASE => ERROR; DO IF cmd.in.EndOf[ ! IO.Error => EXIT] THEN EXIT; line ¬ cmd.in.GetLine[buffer: line ! IO.EndOfStream => GOTO Finished; IO.Error => EXIT; IO.Rubout => GOTO Finished; ]; IF terminator # NIL AND Rope.Equal[terminator, RefText.TrustTextAsRope[line]] THEN EXIT; IF prefix # NIL THEN cmd.out.PutBlock[block: prefix ! IO.Error => EXIT]; cmd.out.PutBlock[block: line ! IO.Error => EXIT]; cmd.out.PutChar['\n ! IO.Error => EXIT]; ENDLOOP; EXITS Finished => NULL; oops => result ¬ $Failure; }; PriorityCommand: Commander.CommandProc = TRUSTED { old: Process.Priority ~ Process.GetPriority[]; new: Process.Priority ~ SELECT cmd.procData.clientData FROM $Foreground => Process.priorityForeground, $Background => Process.priorityBackground, $Normal => Process.priorityNormal, ENDCASE => ERROR; Process.SetPriority[new]; [result: result, msg: msg] ¬ CommanderOps.ExecuteCommand[cmd, cmd.commandLine ! UNWIND => Process.SetPriority[old]; ]; Process.SetPriority[old]; }; docPropCommand: ROPE ~ "print a property value, providing a default args: [ switch ] propname [ defaultvalue ] switches: -q => always quote; -s => never quote; -f => fail if property is undefined"; SpecialChar: Rope.ActionType ~ { RETURN [SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9], '/, '*, '. => FALSE ENDCASE => TRUE ] }; PrevResultCommand: PROC [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- = { result _ List.Assoc[$Result, cmd.propertyList]; msg _ IO.PutFR1["previous Result = %g", IO.refAny[result]]; }; ResultOfCommand: PROC [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- = { result _ CommanderOps.DoCommand[cmd.commandLine, cmd]; msg _ IO.PutFR1["Result = %g", IO.refAny[result]]; }; PropCommand: Commander.CommandProc ~ { n: NAT ¬ 0; arg: ROPE _ CommanderOps.NextArgument[cmd]; v: REF ¬ NIL; quote: [0..2] ¬ 1; demand: BOOL ¬ FALSE; value: ROPE ¬ NIL; default: ROPE ¬ NIL; WHILE Rope.Match["-*", arg] DO FOR i: INT IN (0..Rope.Size[arg]) DO SELECT Rope.Lower[Rope.Fetch[arg, i]] FROM 'q => quote _ 2; 's => quote _ 0; 'f => demand _ TRUE; ENDCASE => CommanderOps.Failed[cmd.procData.doc]; ENDLOOP; arg ¬ CommanderOps.NextArgument[cmd]; ENDLOOP; IF arg = NIL THEN CommanderOps.Failed[cmd.procData.doc]; default ¬ CommanderOps.NextArgument[cmd]; IF CommanderOps.NextArgument[cmd] # NIL THEN CommanderOps.Failed[cmd.procData.doc]; value ¬ default; v ¬ CommanderOps.GetProp[cmd, Atom.MakeAtom[arg]]; IF v = NIL THEN WITH CommanderOps.GetProp[cmd, $CommandFileArgumentVector] SELECT FROM argv: CommanderOps.ArgumentVector => { ENABLE Convert.Error => CONTINUE; i: INT ~ Convert.IntFromRope[arg]; IF i+1 IN [0..argv.argc) THEN { v ¬ argv[i+1]; }; }; ENDCASE; WITH v SELECT FROM rope: ROPE => value ¬ rope; atom: ATOM => value ¬ Atom.GetPName[atom]; ENDCASE => { IF demand THEN CommanderOps.Failed[Rope.Cat["The required property ", arg, IF v = NIL THEN " is not defined." ELSE " is not printable."]]; }; IF quote = 1 THEN { quote ¬ IF Rope.IsEmpty[value] OR Rope.Map[base: value, action: SpecialChar] THEN 2 ELSE 0; }; IF quote # 0 THEN value ¬ Convert.RopeFromRope[value, TRUE]; IO.PutRope[cmd.out, value]; IO.PutRope[cmd.out, "\n"]; }; PropertiesCommand: Commander.CommandProc = TRUSTED { depth: INT ¬ 5; width: INT ¬ 32; props: List.AList; i: NAT ¬ 1; token: Rope.ROPE; any: BOOL ¬ FALSE; verboseFlag: BOOL ¬ FALSE; key: REF ANY; argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd ! CommanderOps.Failed => { msg ¬ errorMsg; GO TO oops}]; { IF cmd.procData.clientData = $ProcessProperties THEN { props ¬ ProcessProps.GetPropList[]; cmd.out.PutRope["process properties:\n"]; } ELSE { props ¬ cmd.propertyList; cmd.out.PutRope["commander properties:\n"]; }; DO Process.CheckForAbort[]; IF i >= argv.argc THEN EXIT; token ¬ argv[i]; i ¬ i + 1; IF token.Equal["-d", FALSE] THEN { IF i = argv.argc THEN GOTO BadArgs; depth ¬ Convert.IntFromRope[argv[i] ! Convert.Error => GOTO BadArgs]; IF width < 0 THEN GOTO BadArgs; i ¬ i + 1; LOOP; }; IF token.Equal["-w", FALSE] THEN { IF i = argv.argc THEN GOTO BadArgs; width ¬ Convert.IntFromRope[argv[i] ! Convert.Error => GOTO BadArgs]; IF width < 0 THEN GOTO BadArgs; i ¬ i + 1; LOOP; }; IF token.Equal["-v", FALSE] THEN { verboseFlag ¬ TRUE; LOOP; }; any ¬ TRUE; IF token.Size[] > 1 AND token.Fetch[0] = '$ THEN key ¬ Atom.MakeAtom[Rope.Substr[base: token, start: 1]] ELSE key ¬ Atom.MakeAtom[token]; cmd.out.PutRope[token]; cmd.out.PutRope[" = "]; PrintAny[List.Assoc[key: key, aList: props], cmd.out]; cmd.out.PutChar['\n]; ENDLOOP; IF NOT any THEN { FOR props ¬ props, props.rest WHILE props#NIL DO PrintAny[props.first.key, cmd.out]; cmd.out.PutRope[" = "]; PrintAny[props.first.val, cmd.out]; cmd.out.PutRope["\n"]; ENDLOOP; << PrintTV.Print[tv: AMBridge.TVForReferent[NEW[REF ANY ¬ props]], put: cmd.out, depth: depth, width: width, verbose: verboseFlag];>> cmd.out.PutChar['\n]; }; EXITS BadArgs => RETURN[$Failure, "Bad args"]; }; EXITS oops => result ¬ $Failure; }; RedoCommand: Commander.CommandProc = { commandToolData: CommanderBackdoor.CommandToolData ¬ CommanderBackdoor.GetCommandToolData[cmd]; cls: IO.STREAM = IO.RIS[cmd.commandLine]; trash: INT = cls.SkipWhitespace[]; key: ROPE = IF cls.EndOf[] THEN NIL ELSE cls.GetTokenRope[IO.IDProc].token; [] ¬ cls.SkipWhitespace[]; IF NOT cls.EndOf[] THEN GOTO GiveUsage; cls.Close[]; FOR h: CommanderBackdoor.CommandHistoryList ¬ commandToolData.history, h.rest WHILE h # NIL DO cr: ROPE ¬ h.first.wholeCommandLine; IF Rope.Equal[cr, ""] THEN LOOP; IF key = NIL OR IsCommandPrefix[key, cr] THEN BEGIN cmd.out.PutF1["%g\n", [rope[cr]]]; [result, msg] ¬ CommanderOps.ExecuteCommand[cmd, cr]; commandToolData.recent.wholeCommandLine ¬ cr; RETURN [result, msg]; END; REPEAT FINISHED => RETURN [$Failure, IO.PutFR1["No command in my history begins with \"%q\"", [rope[key]]]]; ENDLOOP; EXITS GiveUsage => {result ¬ $Failure; msg ¬ "Usage: Redo commandNamePrefix"} }; RegisteredHelpCommand: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> out: IO.STREAM ¬ cmd.out; argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd: cmd]; IF argv.argc <= 1 THEN { msg ¬ IO.PutFR1["Usage: %g pattern ...\n", [rope[cmd.command]]]; GO TO oops }; BEGIN matchList: LIST OF ROPE ¬ NIL; prev: ROPE ¬ NIL; EachCommand: Commander.EnumerateAction = { <<[key: ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL _ FALSE]>> FOR i: NAT IN [1..argv.argc) DO IF Rope.Match[pattern: argv[i], object: key, case: FALSE] THEN { matchList ¬ CONS[key, matchList]; EXIT; }; ENDLOOP; }; pattern: ROPE ¬ IF argv.argc#1 THEN "*" ELSE argv[1]; [] ¬ CommanderRegistry.EnumeratePattern[pattern, EachCommand]; IF matchList = NIL THEN { <> FOR i: NAT IN [1..argv.argc) DO rope: ROPE ~ argv[i]; len: INT ~ Rope.Size[argv[i]]; IF len > 0 AND rope.Fetch[len-1] # '* THEN {argv[i] ¬ rope.Concat["*"]}; ENDLOOP; pattern ¬ IF argv.argc#1 THEN "*" ELSE argv[1]; [] ¬ CommanderRegistry.EnumeratePattern[pattern, EachCommand]; }; IF matchList = NIL THEN { IO.PutF1[cmd.err, "No matching commands for %g", [rope[cmd.commandLine]] ]; GOTO oops }; matchList ¬ RopeList.Sort[list: matchList, compareProc: RopeList.IgnoreCase]; FOR each: LIST OF ROPE ¬ matchList, each.rest WHILE each # NIL DO name: ROPE ¬ each.first; IF NOT Rope.Equal[s1: name, s2: prev, case: FALSE] THEN { pData: Commander.CommandProcHandle ¬ Commander.Lookup[name]; prev ¬ name; IO.PutF[out, "%L%-20g%L", [rope["bf"]], [rope[name]], [rope["BF"]] ]; IF pData = NIL THEN { <> <