DIRECTORY Commander, CommandTool, CommandToolLookup USING [LOR, LORA, WithRulesProc], FS, IO, Process, Rope, RopeList; CommandToolLookupImpl: CEDAR PROGRAM IMPORTS Commander, CommandTool, FS, IO, Process, Rope, RopeList EXPORTS CommandToolLookup = BEGIN OPEN CommandToolLookup; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; DoLookup: PUBLIC PROC [cmd: Commander.Handle, arg: ROPE] RETURNS [paths: LOR _ NIL, procData: Commander.CommandProcHandle _ NIL] = { DoSearch: PROC [arg: ROPE, exact: BOOL] RETURNS [merged: LOR _ NIL, procData: Commander.CommandProcHandle _ NIL] = { cmdPaths: LOR _ NIL; loadPaths: LOR _ NIL; cmPaths: LOR _ NIL; Process.CheckForAbort[]; [cmdPaths, procData] _ FindMatchingCommands[root: arg, requireExact: exact, searchRules: searchRules]; merged _ RemoveDuplicateShorts[cmdPaths, NIL]; IF exact AND merged # NIL AND merged.rest = NIL THEN RETURN [LIST[merged.first], procData]; IF merged # NIL AND merged.rest # NIL THEN RETURN [merged, procData]; Process.CheckForAbort[]; loadPaths _ FindMatchingFiles[root: arg, defaultExtension: ".load", requireExact: exact, searchRules: searchRules]; merged _ RemoveDuplicateShorts[merged, loadPaths]; IF exact AND merged # NIL AND merged.rest = NIL THEN RETURN [LIST[merged.first], NIL]; Process.CheckForAbort[]; cmPaths _ FindMatchingFiles[root: arg, defaultExtension: ".cm", requireExact: exact, searchRules: searchRules]; merged _ RemoveDuplicateShorts[merged, cmPaths]; IF exact AND merged # NIL AND merged.rest = NIL THEN RETURN [LIST[merged.first], NIL]; }; searchRules: REF _ MaybeAddWorkingDir[CommandTool.GetProp[cmd, $SearchRules]]; [paths, procData] _ DoSearch[arg, TRUE]; IF paths = NIL THEN [paths, procData] _ DoSearch[arg, FALSE]; }; FindMatchingFiles: PUBLIC PROC [root: ROPE, defaultExtension: ROPE, requireExact: BOOL _ TRUE, searchRules: REF ANY] RETURNS [paths: LOR _ NIL] = { eachRule: WithRulesProc = { pat: ROPE _ FS.ExpandName[name: pattern, wDir: rule ! FS.Error => GO TO zilch; ].fullFName; angles _ CountAngles[pat]; FS.EnumerateForNames[pattern: pat, proc: eachName ! FS.Error => GO TO zilch; ]; EXITS zilch => {}; }; eachName: FS.NameProc = { IF angles = CountAngles[fullFName] THEN { new: LOR = LIST[fullFName]; IF pathsTail = NIL THEN paths _ new ELSE pathsTail.rest _ new; pathsTail _ new; }; RETURN [TRUE]; }; pattern: ROPE _ root; len: INT _ Rope.Length[pattern]; bang, dot, dir, star: INT _ len; pathsTail: LOR _ NIL; local: BOOL _ TRUE; angles: NAT _ 0; FOR pos: INT IN [0..len) DO SELECT Rope.Fetch[pattern, pos] FROM '! => bang _ pos; '. => dot _ pos; '/ => IF pos = 0 THEN local _ FALSE ELSE dir _ pos; '> => dir _ pos; '* => star _ pos; '[ => IF pos = 0 THEN local _ FALSE; ENDCASE; ENDLOOP; IF dir # len AND dot < dir THEN dot _ len; IF star = len AND NOT requireExact AND bang = len THEN { pattern _ Rope.Concat[pattern, "*"]; }; IF Rope.Length[defaultExtension] # 0 THEN { IF bang = dot THEN { pattern _ Rope.Concat[pattern, defaultExtension]; }; }; IF bang = len THEN { pattern _ Rope.Concat[pattern, "!H"]; }; IF local AND searchRules # NIL THEN { searchRules _ MaybeAddWorkingDir[searchRules]; [] _ DoWithRules[searchRules, eachRule]; } ELSE [] _ eachRule[pattern]; IF star = len AND requireExact AND paths # NIL THEN paths.rest _ NIL; }; FindMatchingCommands: PUBLIC PROC [root: ROPE, requireExact: BOOL, searchRules: REF] RETURNS [paths: LOR _ NIL, data: Commander.CommandProcHandle _ NIL] = { eachRule: WithRulesProc = { eachCommand: Commander.EnumerateAction = { keyLen: INT _ Rope.Length[key]; ruleRun: INT _ Rope.Run[rule, 0, key, 0, FALSE]; IF rule = NIL OR (ruleRun = ruleLen AND Rope.SkipTo[key, ruleRun, "/"] = keyLen) THEN { rootRun: INT _ Rope.Run[root, 0, key, ruleRun, FALSE]; keyLen: INT _ Rope.Length[key]; SELECT TRUE FROM rootRun = rootLen AND rootRun = keyLen-ruleRun => { paths _ pathsTail _ LIST[key]; data _ procData; RETURN [TRUE]; }; requireExact => { }; starPos = rootRun => { IF starPos+1 >= rootLen THEN addName[key, procData] ELSE { short: ROPE _ Rope.Substr[key, ruleRun]; IF Rope.Match[root, short, FALSE] THEN addName[key, procData]; }; }; ENDCASE; }; }; ruleLen: INT _ Rope.Length[rule]; IF requireExact THEN { fullName: ROPE _ Rope.Concat[rule, root]; data _ Commander.Lookup[fullName]; IF data # NIL THEN {paths _ LIST[fullName]; RETURN [stop: TRUE]}; RETURN [FALSE]; }; IF Commander.Enumerate[eachCommand].key # NIL THEN RETURN [TRUE]; }; rootLen: INT _ Rope.Length[root]; starPos: INT _ Rope.SkipTo[root, 0, "*"]; addName: PROC [name: ROPE, procData: Commander.CommandProcHandle] = { new: LOR _ LIST[name]; IF pathsTail = NIL THEN {paths _ new; data _ procData} ELSE {pathsTail.rest _ new; data _ NIL}; pathsTail _ new; }; pathsTail: LOR _ NIL; IF Rope.Match["/*", root] OR searchRules = NIL THEN [] _ eachRule[NIL] ELSE { searchRules _ MaybeAddWorkingDir[searchRules]; [] _ DoWithRules[searchRules, eachRule]; }; paths _ RopeList.Sort[paths, RopeList.IgnoreCase]; }; DoWithRules: PUBLIC PROC [rules: REF, inner: WithRulesProc] RETURNS [stop: BOOL _ FALSE] = { WITH rules SELECT FROM lora: LORA => WHILE lora # NIL DO IF DoWithRules[lora.first, inner] THEN RETURN [TRUE]; lora _ lora.rest; ENDLOOP; lor: LOR => WHILE lor # NIL DO IF inner[lor.first] THEN RETURN; lor _ lor.rest; ENDLOOP; rope: ROPE => RETURN [inner[rope]]; ENDCASE; }; ShowAmbiguous: PUBLIC PROC [out: STREAM, list: LOR] = { IO.PutRope[out, "{Ambiguous:\n"]; FOR each: LOR _ list, each.rest WHILE each # NIL DO IO.PutF1[out, " %g\n", [rope[each.first]] ]; ENDLOOP; IO.PutRope[out, " }\n"]; }; MaybeAddWorkingDir: PROC [rules: REF] RETURNS [REF] = { wDir: ROPE _ CommandTool.CurrentWorkingDirectory[]; checkRule: WithRulesProc = { IF Rope.Equal[rule, wDir, FALSE] THEN RETURN [TRUE]; }; IF rules = NIL THEN RETURN [LIST[wDir]]; IF NOT DoWithRules[rules, checkRule] THEN { newList: LIST OF REF ANY _ LIST[wDir, rules]; RETURN [newList]; }; RETURN [rules]; }; RemoveDuplicateShorts: PROC [list: LOR, tail: LOR] RETURNS [LOR] = { list _ RopeList.Append[list, tail]; FOR each: LOR _ list, each.rest WHILE each # NIL DO lag: LOR _ each; eachStart, eachLen: INT; IF each.rest = NIL THEN EXIT; [eachStart, eachLen] _ FindShortPart[lag.first]; FOR other: LOR _ each.rest, other.rest WHILE other # NIL DO otherStart, otherLen: INT; [otherStart, otherLen] _ FindShortPart[other.first]; IF eachLen = otherLen THEN IF Rope.Run[each.first, eachStart, other.first, otherStart, FALSE] >= otherLen THEN { lag.rest _ other.rest; LOOP; }; lag _ other; ENDLOOP; ENDLOOP; RETURN [list]; }; FindShortPart: PROC [name: ROPE] RETURNS [start: INT _ 0, len: INT _ 0] = { end: INT _ Rope.Length[name]; start _ end; WHILE start > 0 DO SELECT Rope.Fetch[name, start _ start - 1] FROM '/, '>, '] => {start _ start + 1; EXIT}; '. => end _ start; ENDCASE; ENDLOOP; len _ end - start; }; CountAngles: PROC [name: ROPE] RETURNS [count: NAT _ 0] = { inner: Rope.ActionType = { IF c = '> THEN count _ count + 1; }; [] _ Rope.Map[base: name, action: inner] }; TestLookupProc: Commander.CommandProc = { out: STREAM = cmd.out; ProcessArgument: PROC [arg: ROPE] = { paths: LOR _ DoLookup[cmd, arg].paths; SELECT TRUE FROM paths = NIL => IO.PutF1[out, "Not found: %g\n", [rope[arg]] ]; paths.rest = NIL => IO.PutF1[out, "Found: %g\n", [rope[paths.first]] ]; ENDCASE => ShowAmbiguous[out, paths]; }; argsProcessed: NAT _ 0; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd, starExpand: FALSE ! CommandTool.Failed => {msg _ errorMsg; GO TO failed}]; FOR i: NAT IN [1..argv.argc) DO arg: ROPE = argv[i]; Process.CheckForAbort[]; IF Rope.Length[arg] = 0 THEN LOOP; ProcessArgument[arg]; ENDLOOP; EXITS failed => {result _ $Failure}; }; doc: ROPE = "Tests command lookup."; Commander.Register[ key: "///Commands/Lookup", proc: TestLookupProc, doc: doc, clientData: NIL, interpreted: TRUE ]; END. ªCommandToolLookupImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) November 4, 1985 8:38:06 pm PST the search rules now in effect Now try the search all over, except that we don't have to have an exact match [rule: ROPE] RETURNS [stop: BOOL _ FALSE] [fullFName: ROPE] RETURNS [continue: BOOL] Scan through the file name looking for the various kinds of punctuation. There is no star in the pattern, yet we can allow inexact matches, so we append a star (provided that there is no version info provided) There is an extension given, so we append it if not already in the name Need to append the extension to the pattern Need to limit the match to the highest versions For a "local" name we have to search all of the directories in the searchRules. If the current workingDirectory is not in the list, then we put it into the list. We are looking for an exact match, and there is no pattern, so the very first file found in the search path is the right one. [rule: ROPE] RETURNS [stop: BOOL _ FALSE] [key: ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL _ FALSE] Found an exact match, so we can stop here We are looking for an exact match, so don't try any fancy matching At this point we have matched as far as the first star. The star is trailing, so we can check for a match cheaply The star is embedded, so we have to check expensively Do this lookup the fast way to avoid the enumeration For a "local" name we have to search all of the directories in the searchRules. If the current workingDirectory is not in the list, then we put it into the list. [rule: ROPE] RETURNS [stop: BOOL _ FALSE] The working directory is not in the current rules, so add it as the first one to search A duplicate short name to remove [c: CHAR] RETURNS [quit: BOOL _ FALSE] [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...] # of arguments processed When parsing the command line, be prepared for failure. The error is reported to the user Each argument can either be a switch specification or a genuine argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user. It is a good idea to periodically check for a process abort request. Ignore null arguments (it is not easy to generate them, even). Perform whatever processing is necessary for a normal argument. Ê4˜codešœ™Kšœ Ïmœ1™˜NK™—Kšœ"žœ˜(šžœ žœž˜KšœM™MKšœ"žœ˜)—K˜K˜—šŸœžœžœžœžœžœžœžœžœžœ žœžœ˜“šœ˜Kš œžœžœžœžœ™)•StartOfExpansion"[name: ROPE, wDir: ROPE _ NIL]šœžœžœ%˜3Kšœžœ žœžœ˜Kšœ ˜ —Kšœ˜šžœ/˜1Kšœžœ žœžœ˜Kšœ˜—Kšžœ ˜K˜—šœ žœ ˜Kšœ žœžœ žœ™*šžœ!žœ˜)Kšœžœžœ ˜Kšžœ žœžœ žœ˜>Kšœ˜K˜—Kšžœžœ˜K˜—Kšœ žœ˜Kšœžœ˜ Kšœžœ˜ Kšœ žœžœ˜Kšœžœžœ˜Kšœžœ˜K˜KšœH™Hšžœžœžœ ž˜šžœž˜$Kšœ˜Kšœ˜Kš œžœ žœ žœžœ ˜3Kšœ˜Kšœ˜Kšœžœ žœ žœ˜$Kšžœ˜—Kšžœ˜—Kšžœ žœ žœ ˜*K˜š žœ žœžœžœ žœ˜8Kšœˆ™ˆKšœ$˜$K˜K˜—šžœ#žœ˜+KšœG™Gšžœ žœ˜Kšœ+™+Kšœ1˜1K˜—K˜K˜—šžœ žœ˜Kšœ/™/K˜%K˜K˜—šžœžœž˜šžœ˜Kšœ¢™¢Kšœ.˜.Kšœ(˜(K˜—Kšžœ˜K˜—š žœ žœžœ žœž˜3K™}Kšœ žœ˜—K˜K˜—šŸœž œžœžœžœžœ žœžœ&žœ˜œšœ˜Kš œžœžœžœžœ™)–S -- [key: ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL _ FALSE]šœ*˜*KšÐckO™OKšœžœ˜Kšœ žœžœ˜0š žœžœžœžœ*žœ˜WKšœ žœ#žœ˜6Kšœžœ˜šžœžœž˜šœžœ˜3Kšœ)™)Kšœžœ˜Kšœ˜Kšžœžœ˜K˜—šœ˜KšœB™BKšœ˜—šœ˜Kšœ7™7šžœ˜šž˜Kšœ9™9Kšœ˜—šžœ˜Kšœ5™5Kšœžœ˜(Kšžœžœžœ˜>K˜——Kšœ˜—Kšžœ˜—K˜—K˜—Kšœ žœ˜!šžœžœ˜Kšœ4™4Kšœ žœ˜)Kšœ"˜"Kš žœžœžœ žœ žœžœ˜AKšžœžœ˜K˜—Kš žœ(žœžœžœžœ˜AK˜—Kšœ žœ˜!Kšœ žœ˜)šœ žœžœ,˜EKšœžœžœ˜šžœ ž˜Kšžœ˜#Kšžœžœ˜(—Kšœ˜K˜—Kšœ žœžœ˜šžœžœž˜.Kšžœžœ˜šžœ˜Kšœ.˜.Kšœ(˜(K˜——Kšœ2˜2K˜K˜—š Ÿ œž œ žœžœžœžœ˜\šžœžœž˜šœžœ˜ šžœžœž˜Kšžœ žœžœžœ˜5K˜Kšžœ˜——šœžœ˜ šžœžœž˜Kšžœžœžœ˜ K˜Kšžœ˜——šœžœ˜ Kšžœ˜—Kšžœ˜—K˜K˜—šŸ œž œžœžœ˜7Kšžœ˜!š žœžœžœžœž˜3Kšžœ+˜-Kšžœ˜—Kšžœ˜K˜K˜—š Ÿœžœ žœžœžœ˜7Kšœ¢™¢Kšœžœ)˜3šœ˜Kš œžœžœžœžœ™)Kš žœžœžœžœžœ˜4K˜—Kš žœ žœžœžœžœ˜(šžœžœžœ˜+KšœW™WKš œ žœžœžœžœžœ˜-Kšžœ ˜K˜—Kšžœ ˜K˜K˜—š Ÿœžœžœžœžœžœ˜DKšœ#˜#š žœžœžœžœž˜3Kšœžœ˜Kšœžœ˜Kšžœ žœžœžœ˜Kšœ0˜0š žœžœžœ žœž˜;Jšœžœ˜Kšœ4˜4šžœž˜šžœ:žœžœ˜UJšœ ™ Jšœ˜Jšžœ˜J˜——J˜ Jšžœ˜—Kšžœ˜—Kšžœ˜K˜K˜—š Ÿ œžœžœžœ žœ žœ ˜KKšœžœ˜Kšœ ˜ šžœ ž˜šžœ%ž˜/Kšœ"žœ˜(Kšœ˜Kšžœ˜—Kšžœ˜—K˜K˜K˜—š Ÿ œžœžœžœ žœ ˜;–* -- [c: CHAR] RETURNS [quit: BOOL _ FALSE]˜Kš &™&Kšžœžœ˜!K˜—K–T[base: ROPE, start: INT _ 0, len: INT _ 2147483647, action: Rope.ActionType]šœ(˜(K˜K˜—šœ)˜)š œžœ žœžœžœžœ™:Kšœžœžœ™G—Kšœžœ ˜šŸœžœžœ˜%Kšœžœ˜&šžœžœž˜Kšœžœžœ-˜>Kšœ žœžœ1˜GKšžœ˜%—K˜—šœžœ˜Kšœ™—–I[cmd: Commander.Handle, starExpand: BOOL _ FALSE, switchChar: CHAR]šœKž˜PKšœ)žœžœ ˜8KšœZ™ZK˜—šžœžœžœž˜KšœÒ™ÒKšœžœ ˜˜KšœD™D—šžœžœžœ˜"Kšœ>™>—šœ˜Kšœ?™?—Kšžœ˜—šž˜Kšœ˜—K˜K˜—Kšœžœ˜$K˜–x[key: ROPE, proc: Commander.CommandProc, doc: ROPE _ NIL, clientData: REF ANY _ NIL, interpreted: BOOL _ TRUE]šœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ žœ˜Kšœ ž˜Kšœ˜—K˜Kšžœ˜K˜—…—‚7`