<> <> <> <> <> DIRECTORY AbbrevExpandExtras USING [ ChangeRegistry, FindEntryProc, FindEntryVal, PropList ], Args USING [Arg, ArgsGet ], Atom USING [GetPName, MakeAtom], Commander USING [CommandProc, CommandProcObject, CommandProcHandle, Handle, Register], CommanderRegistry USING [ ChangeRegistry, LookupProc, LookupProp, EnumPatternProc ], CommanderOps USING [ DoCommand, GetProp ], Convert USING [ RopeFromInt ], IO USING [BreakProc, char, Close, EndOf, EndOfStream, Error, GetChar, GetIndex, GetRefAny, GetToken, IDProc, PeekChar, PutF1, PutFR, PutFR1, RIS, rope, SkipWhitespace, STREAM, TokenProc], List USING [ PutAssoc ], LoganBerry USING [ AttributeType, EntryProc, EnumerateEntries, Entry, Error, nullDB, Open, OpenDB, ReadEntry, WriteEntry ], PopUpButtons USING [ AmbushInstance, Class, Instantiate, MakeClass, PopUpButtonProc ], Process USING [ Detach ], ProcessProps USING [ GetProp ], Prop USING [ PropList, Put, Rem ], Rope USING [Concat, Equal, Fetch, Find, FromRefText, Length, Match, Replace, ROPE, SkipTo, Substr], SystemNames USING [ UserName ], TextEdit USING [ DeleteText ], Tioga USING [ Node ], TiogaIO USING [ FromRope, ToRope ], TiogaOps USING [ GetSelection, Location ], UserProfile USING [ Boolean ], ViewerClasses USING [ Viewer ], ViewerOps USING [ FindViewer ] ; AkaImpl: CEDAR PROGRAM IMPORTS AbbrevExpandExtras, Args, Atom, Commander, CommanderRegistry, CommanderOps, Convert, IO, List, LoganBerry, PopUpButtons, Process, ProcessProps, Prop, Rope, SystemNames, TextEdit, TiogaIO, TiogaOps, UserProfile, ViewerOps = { OPEN IO; ROPE: TYPE = Rope.ROPE; LOR: TYPE = LIST OF ROPE; STREAM: TYPE = IO.STREAM; akaDB: LoganBerry.OpenDB ¬ LoganBerry.nullDB; akaDBName: ROPE ¬ NIL; tiogaLookupProc: AbbrevExpandExtras.FindEntryVal ¬ NEW[AbbrevExpandExtras.FindEntryProc ¬ AkaLookupForTioga]; commanderLookupProc: REF CommanderRegistry.LookupProc ¬ NEW[CommanderRegistry.LookupProc ¬ AkaLookup]; commanderLookupPropsProc: REF CommanderRegistry.LookupProp ¬ NEW[CommanderRegistry.LookupProp ¬ AkaLookupProp]; commanderEnumPatProc: REF CommanderRegistry.EnumPatternProc ¬ NEW[CommanderRegistry.EnumPatternProc ¬ AkaEnumeratePattern]; argIndices: LIST OF ROPE ¬ MakeArgIndices[20]; lastCmd: Commander.Handle; akaTiogaClass: PopUpButtons.Class ¬ NIL; <> rootPrefix: BOOL¬FALSE; -- All enumerations yield "Aka.result", not "result" parseRootPrefix: BOOL ¬ FALSE; -- If rootPrefix, user can enter "Aka.command" and it will match if "command" would match. AkaOptions: Commander.CommandProc ~ { a1, a2, a3, a4, a5, a6: Args.Arg; [a1, a2, a3, a5, a5, a6] ¬ Args.ArgsGet[cmd, "-rootPrefix%b-parseRootPrefix%b"]; rootPrefix ¬ a1.bool; parseRootPrefix ¬ a2.bool; <<booleanOption _ a3.bool;>> <<booleanOption _ a4.bool;>> <<booleanOption _ a5.bool;>> <<booleanOption _ a6.bool;>> }; <> << A name of the form a.b.c is stored, for the moment, in the name >> <> <<>> AkaOn: Commander.CommandProc = { viewer: ViewerClasses.Viewer; CExReg: PROC[old: Prop.PropList] RETURNS [new: AbbrevExpandExtras.PropList] ~ { RETURN[Prop.Put[old, $AkaFindEntry, tiogaLookupProc]]; }; CComReg: PROC[oldLk, oldEn, oldEnP, oldLkPrp: Prop.PropList] RETURNS [newLk, newEn, newEnP, newLkPrp: Prop.PropList] ~ { newLk ¬ Prop.Put[oldLk, $AkaLookup, commanderLookupProc]; newEn ¬ oldEn; -- We do not modify enumerator. newEnP ¬ Prop.Put[oldEnP, $AkaEnumPattern, commanderEnumPatProc]; newLkPrp ¬ Prop.Put[oldLkPrp, $AkaLookupProp, commanderLookupPropsProc]; }; lastCmd ¬ cmd; AbbrevExpandExtras.ChangeRegistry[CExReg]; CommanderRegistry.ChangeRegistry[CComReg]; IF akaDB=LoganBerry.nullDB THEN akaDB ¬ LoganBerry.Open[ dbName: akaDBName ¬ IO.PutFR1["/tilde/%g/Cedar/AkaLB.df", rope[SystemNames.UserName[]]]! LoganBerry.Error => { cmd.err.PutF1["Can't open alias database: %g\n", rope[explanation]]; akaDB ¬ LoganBerry.nullDB; CONTINUE; }]; IF akaTiogaClass=NIL THEN akaTiogaClass ¬ PopUpButtons.MakeClass[spec: [ proc: AkaFromTioga, choices: LIST[[key: $AliasFromSel, doc: "Define abbrev-style alias from primary selection"]] ]]; viewer ¬ ViewerOps.FindViewer["Aka"]; IF viewer#NIL THEN PopUpButtons.AmbushInstance[ button: viewer, class: akaTiogaClass] ELSE [] ¬ PopUpButtons.Instantiate[class: akaTiogaClass, viewerInfo: [name: "Aka"]]; }; AkaOff: Commander.CommandProc = { CExUnreg: PROC[old: Prop.PropList] RETURNS [new: AbbrevExpandExtras.PropList] ~ { RETURN[Prop.Put[old, $AkaFindEntry, tiogaLookupProc]]; }; CComUnreg: PROC[oldLk, oldEn, oldEnP, oldLkPrp: Prop.PropList] RETURNS [newLk, newEn, newEnP, newLkPrp: Prop.PropList] ~ { newLk ¬ Prop.Rem[oldLk, $AkaLookup]; newEn ¬ oldEn; -- We do not modify enumerator. newEnP ¬ Prop.Rem[oldEnP, $AkaEnumPattern]; newLkPrp ¬ Prop.Rem[oldLkPrp, $AkaLookupProp]; }; AbbrevExpandExtras.ChangeRegistry[CExUnreg]; CommanderRegistry.ChangeRegistry[CComUnreg]; }; Aka: Commander.CommandProc = { DefineAlias[cmd, cmd.commandLine, NIL]; }; AkaFromTioga: PopUpButtons.PopUpButtonProc = { <<[viewer: ViewerClasses.Viewer, instanceData: REF ANY, classData: REF ANY, key: REF ANY]>> loc: TiogaOps.Location ¬ TiogaOps.GetSelection[primary].start; def: ROPE; IF loc.node = NIL THEN RETURN; def ¬ TiogaIO.ToRope[loc.node]; TRUSTED{ Process.Detach[FORK DefineAlias[lastCmd, def, TiogaIO.FromRope[def], $Tioga]];}; }; <<$TiogaContents => { -- to split it apart!!>> <> <> <<[dataLen, count, contents] _ TiogaIO.ToRope[tdd.text];>> <> <> <> <<};>> DefineAlias: PROC[cmd: Commander.Handle, rawDef: ROPE, node: Tioga.Node ¬ NIL, entryType: ATOM¬NIL] = { <> defStream: IO.STREAM; newCommand, tail: LoganBerry.Entry ¬ NIL; name, def: ROPE; paramsExist: BOOL¬TRUE; token: REF TEXT ¬ NEW[TEXT[30]]; defStream ¬ IO.RIS[rawDef]; IF cmd=NIL THEN RETURN; []¬AkaOn[cmd]; { ENABLE { IO.EndOfStream => GOTO Die; IO.Error => GOTO Die; }; <> <> <> <> <> <> <> <> <<>> <> token.length ¬ 0; token ¬ defStream.GetToken[IO.TokenProc, token].token; name ¬ LowerCaseRope[Rope.FromRefText[token]]; newCommand ¬ LIST[[$command, name]]; tail ¬ newCommand; IF entryType#NIL THEN newCommand.rest ¬ tail ¬ LIST[[$type, Atom.GetPName[entryType]]]; [] ¬ defStream.SkipWhitespace[FALSE]; IF (NOT defStream.EndOf[]) AND defStream.PeekChar[] = '( THEN { FOR l: LIST OF REF ANY ¬ NARROW[defStream.GetRefAny[]], l.rest UNTIL l = NIL DO WITH l.first SELECT FROM r: ROPE => name ¬ r; a: ATOM => name ¬ Atom.GetPName[a]; i: REF INT => name ¬ Convert.RopeFromInt[i­]; ENDCASE => ERROR; tail.rest ¬ LIST[[$arg, name]]; tail ¬ tail.rest; ENDLOOP; } ELSE paramsExist ¬ FALSE; [] ¬ defStream.SkipWhitespace[FALSE]; IF (NOT defStream.EndOf[]) AND defStream.PeekChar[] = '= THEN { -- Optional "=" in definition. [] ¬ defStream.GetChar[]; <<[] _ defStream.SkipWhitespace[FALSE]; -- eats up placeholder brackets, etc.>> WHILE defStream.PeekChar[] = '\040 DO [] ¬ defStream.GetChar[]; ENDLOOP; }; IF node#NIL THEN { TextEdit.DeleteText[node, node, 0, defStream.GetIndex[]]; def ¬ TiogaIO.ToRope[node]; } ELSE { token.length ¬ 0; token ¬ defStream.GetToken[CRBreak, token!IO.EndOfStream =>{ token.length ¬ 0; CONTINUE}].token; def ¬ Rope.FromRefText[token]; }; IF (def=NIL OR def.Length[]=0) THEN IF paramsExist THEN GOTO Die ELSE { ChangeAkaValue[cmd, name, $state, "on"]; RETURN; }; tail.rest ¬ LIST[[$value, def]]; LoganBerry.WriteEntry[db: akaDB, entry: newCommand, replace: TRUE! LoganBerry.Error => { cmd.err.PutF1["Can't register alias: %g\n", rope[explanation]]; CONTINUE; }]; IO.Close[defStream]; } EXITS Die => NULL; }; Nka: Commander.CommandProc = { commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; name: ROPE; token: REF TEXT ¬ NEW[TEXT[30]]; []¬AkaOn[cmd]; { ENABLE { IO.EndOfStream => GOTO Die; IO.Error => GOTO Die; }; token.length ¬ 0; token ¬ commandLineStream.GetToken[IO.TokenProc, token].token; name ¬ LowerCaseRope[Rope.FromRefText[token]]; ChangeAkaValue[cmd, name, $state, "off"]; } EXITS Die => NULL; }; AkaDoc: Commander.CommandProc = { commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; name, doc: ROPE; token: REF TEXT ¬ NEW[TEXT[30]]; []¬AkaOn[cmd]; { ENABLE { IO.EndOfStream => GOTO Die; IO.Error => GOTO Die; }; token.length ¬ 0; token ¬ commandLineStream.GetToken[IO.TokenProc, token].token; name ¬ LowerCaseRope[Rope.FromRefText[token]]; token.length ¬ 0; token ¬ commandLineStream.GetToken[CRBreak, token!IO.EndOfStream =>{ token.length ¬ 0; CONTINUE}].token; doc ¬ Rope.FromRefText[token]; ChangeAkaValue[cmd, name, $doc, doc]; } EXITS Die => NULL; }; AkaLookup: PROC[key: ROPE] RETURNS[procData: Commander.CommandProcHandle ¬ NIL] ~ { entry: LoganBerry.Entry; entry ¬ GetEntry[key]; IF entry=NIL THEN RETURN; procData ¬ NEW[Commander.CommandProcObject ¬ [proc: AkaImpl, clientData: entry, doc: GetDoc[entry]]]; }; AkaEnumeratePattern: CommanderRegistry.EnumPatternProc ~ { EachEntry: LoganBerry.EntryProc = { key: Rope.ROPE ¬ Fetch[entry, $command].value; IF rootPrefix THEN key ¬ Rope.Concat["Aka.", key]; IF Fetch[entry, $state].value.Equal["off"] THEN RETURN[TRUE]; IF NOT pattern.Match[key, FALSE] THEN RETURN[TRUE]; []¬matchProc[key: key, procData: NIL]; <> <> RETURN[TRUE]; }; end: Rope.ROPE; start: Rope.ROPE ¬ LowerCaseRope[SkipToAndReturnPrefix[pattern, "*"]]; procData ¬ NEW[Commander.CommandProcObject ¬ [proc: AkaImpl, clientData: NIL]]; end ¬ start.Concat["\377"]; LoganBerry.EnumerateEntries[ db: akaDB, key: $command, start: start, end: end, proc: EachEntry! LoganBerry.Error => { cmd: Commander.Handle ¬ NARROW[ProcessProps.GetProp[$CommanderHandle]]; cmd.err.PutF1["Can't read from alias database: %g\n", rope[explanation]]; akaDB ¬ LoganBerry.nullDB; CONTINUE; } ]; }; AkaLookupProp: CommanderRegistry.LookupProp = { name: ROPE; entry: LoganBerry.Entry; WITH key SELECT FROM rope: ROPE => name ¬ rope; atom: ATOM => name ¬ Atom.GetPName[atom]; ENDCASE; WHILE name.Length[]>0 AND name.Fetch[0]='$ DO name ¬ name.Substr[start: 1]; ENDLOOP; IF name.Length[]>0 THEN entry ¬ GetEntry[name]; IF entry#NIL THEN RETURN[Fetch[entry, $value].value]; }; AkaLookupForTioga: AbbrevExpandExtras.FindEntryProc ~ { <> entry: LoganBerry.Entry; tail: LIST OF REF ANY; typeRope: ROPE; entry ¬ GetEntry[key]; IF entry=NIL THEN RETURN; keyRope ¬ Fetch[entry, $command].value; node ¬ TiogaIO.FromRope[Fetch[entry, $value].value]; typeRope ¬ Fetch[entry, $type].value; IF typeRope.Length[]<=0 OR Atom.MakeAtom[typeRope]#$Tioga THEN node ¬ node.child; <> commands ¬ NIL; FOR e: LoganBerry.Entry ¬ entry, e.rest WHILE e#NIL DO l: LIST OF REF ANY; IF e.first.type # $arg THEN LOOP; l ¬ LIST[Atom.MakeAtom[e.first.value]]; IF tail=NIL THEN commands ¬ l ELSE tail.rest ¬ l; tail ¬ l; ENDLOOP; }; AkaImpl: Commander.CommandProc = { <> aliasMode: REF ¬ CommanderOps.GetProp[cmd, $AliasMode]; token: REF TEXT ¬ NEW[TEXT[40]]; commandEntry: LoganBerry.Entry ¬ NARROW[cmd.procData.clientData]; commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; newCommandLine: ROPE ¬ Fetch[commandEntry, $value].value; new, restOfStream: ROPE; remainingIndices: LIST OF ROPE; <> remainingIndices ¬ argIndices; FOR e: LoganBerry.Entry ¬ commandEntry, e.rest WHILE e#NIL DO IF e.first.type # $arg THEN LOOP; newCommandLine ¬ RopeSubst[old: e.first.value, new: remainingIndices.first, base: newCommandLine]; remainingIndices ¬ remainingIndices.rest; ENDLOOP; <> remainingIndices ¬ argIndices; FOR e: LoganBerry.Entry ¬ commandEntry, e.rest WHILE e#NIL DO IF e.first.type # $arg THEN LOOP; token.length ¬ 0; token ¬ commandLineStream.GetToken[IO.IDProc, token ! IO.EndOfStream => CONTINUE; IO.Error => GO TO Nasty ].token; new ¬ Rope.FromRefText[token]; newCommandLine ¬ RopeSubst[old: remainingIndices.first, new: new, base: newCommandLine]; remainingIndices ¬ remainingIndices.rest; ENDLOOP; token.length ¬ 0; token ¬ commandLineStream.GetToken[CRBreak, token ! IO.EndOfStream => CONTINUE; IO.Error => GO TO Nasty ].token; restOfStream ¬ Rope.FromRefText[token]; newCommandLine ¬ Rope.Concat[newCommandLine, restOfStream]; IO.Close[commandLineStream]; IF aliasMode=$Quiet OR ((aliasMode=NIL OR aliasMode=$SeeProfile) AND UserProfile.Boolean["CommandTool.QuietAlias", FALSE]) THEN cmd.propertyList ¬ List.PutAssoc[$QuietAlias, $True, cmd.propertyList]; result ¬ CommanderOps.DoCommand[commandLine: newCommandLine, parent: cmd]; cmd.propertyList ¬ List.PutAssoc[$QuietAlias, NIL, cmd.propertyList]; EXITS Nasty => {msg ¬ "IO.Error while parsing arguments"; result ¬ $Failed}; }; <> <<>> ChangeAkaValue: PROC[ cmd: Commander.Handle, root: ROPE, type: LoganBerry.AttributeType, newValue: ROPE] = { others: BOOL; entry: LoganBerry.Entry; e: LoganBerry.Entry; val: Rope.ROPE; [entry, others] ¬ LoganBerry.ReadEntry[db: akaDB, key: $command, value: root! LoganBerry.Error => { cmd.err.PutF1["Can't read from alias database: %g\n", rope[explanation]]; akaDB ¬ LoganBerry.nullDB; CONTINUE; } ]; IF entry=NIL OR others THEN RETURN; [val, e] ¬ Fetch[entry, type]; IF e=NIL THEN ERROR; IF e.first.type=type THEN IF e.first.value.Equal[newValue] THEN RETURN ELSE e.first.value ¬ newValue ELSE e.rest ¬ LIST[[type, newValue]]; LoganBerry.WriteEntry[db: akaDB, entry: entry, replace: TRUE! LoganBerry.Error => { cmd.err.PutF1["Can't change alias: %g\n", rope[explanation]]; CONTINUE; }]; }; Fetch: PROC[entry: LoganBerry.Entry, type: LoganBerry.AttributeType] RETURNS[value: Rope.ROPE¬NIL, rest: LoganBerry.Entry¬NIL] = { FOR e: LoganBerry.Entry ¬ entry, e.rest WHILE e#NIL DO rest ¬ e; IF e.first.type = type THEN RETURN[e.first.value, e]; ENDLOOP; }; SkipToAndReturnPrefix: PROC[val: ROPE, chars: ROPE] RETURNS [ROPE] ~ { RETURN[val.Substr[len: val.SkipTo[0, chars]]]; }; GetEntry: PROC[key: ROPE] RETURNS [entry: LoganBerry.Entry] = { key ¬ LowerCaseRope[key]; [entry,] ¬ LoganBerry.ReadEntry[db: akaDB, key: $command, value: key! LoganBerry.Error => { cmd: Commander.Handle ¬ NARROW[ProcessProps.GetProp[$CommanderHandle]]; IF cmd#NIL THEN cmd.err.PutF1["Can't read from alias database: %g\n", rope[explanation]]; akaDB ¬ LoganBerry.nullDB; CONTINUE; } ]; IF entry#NIL AND Fetch[entry, $state].value.Equal["off"] THEN entry ¬ NIL; }; GetDoc: PROC[entry: LoganBerry.Entry] RETURNS[doc: Rope.ROPE¬NIL] = { firstArg: LoganBerry.Entry; formalDoc: Rope.ROPE ¬ Fetch[entry, $doc].value; firstArg ¬ Fetch[entry, $arg].rest; doc ¬ NIL; IF firstArg # NIL THEN FOR e: LoganBerry.Entry ¬ firstArg, e.rest WHILE e.first.type=$arg DO doc ¬ IO.PutFR["%g, %g", rope[doc], rope[e.first.value]]; ENDLOOP; IF doc#NIL THEN doc ¬ IO.PutFR1["(%g)", rope[doc.Substr[start: 2]]]; doc ¬ IO.PutFR["aka %g%g %g", rope[Fetch[entry, $command].value], rope[doc], rope[Fetch[entry, $value].value]]; IF formalDoc#NIL THEN doc ¬ IO.PutFR["&g\n&g", rope[doc], rope[formalDoc]]; }; RopeSubst: PROC [old, new, base: ROPE, case: BOOL ¬ FALSE, allOccurrences: BOOL ¬ TRUE] RETURNS [ROPE] = { <> <> lenOld: INT = old.Length[]; lenNew: INT = new.Length[]; i: INT ¬ 0; WHILE (i ¬ Rope.Find[s1: base, s2: old, case: case, pos1: i]) # -1 DO base ¬ Rope.Replace[base: base, start: i, len: lenOld, with: new]; IF ~allOccurrences THEN EXIT; i ¬ i + lenNew; ENDLOOP; RETURN[base]; }; LowerCaseRope: PUBLIC PROC[r: ROPE] RETURNS [ROPE] = { RETURN[r]}; <> LCFetch: PROC[data: REF, index: INT] RETURNS [c: CHAR] = { SELECT (c¬NARROW[data,ROPE].Fetch[index]) FROM IN ['A..'Z]=>c¬c+('a-'A); ENDCASE}; CRBreak: IO.BreakProc = { IF char = '\n THEN RETURN[break]; RETURN[other]; }; MakeArgIndices: PROC[numIndices: BYTE] RETURNS [aI: LIST OF ROPE] = { FOR i: BYTE IN [0 .. numIndices) DO c: CHAR ¬ LOOPHOLE[10-i]; aI ¬ CONS[IO.PutFR1["\376%g", char[c]], aI]; ENDLOOP; }; <<>> <> Init: PROC = { Commander.Register[key: "Aka", proc: Aka, doc: "Create a persistent alias for a command, or enable an old one", interpreted: FALSE]; Commander.Register[key: "Nka", proc: Nka, doc: "Disable a persistent alias", interpreted: FALSE]; Commander.Register[key: "AkaOn", proc: AkaOn, doc: "Enable persistent alias searching."]; Commander.Register[key: "AkaOff", proc: AkaOff, doc: "Disable persistent alias searching."]; Commander.Register[key: "AkaOptions", proc: AkaOptions, doc: "Set or clear program options; until easy GF variable access comes along. Current options Include: -rootPrefix: BOOL -parseRootPrefix: BOOL -- observed only if rootPrefix "]; Commander.Register[key: "AkaDoc", proc: AkaDoc, doc: "Add documentation to a persistent alias", interpreted: FALSE]; }; Init[]; }.