DIRECTORY Atom USING [MakeAtom], Commander USING [Handle, GetProperty], CommandTool, Convert USING [Error, IntFromRope], FileNames USING [ResolveRelativePath], FS USING [AccessOptions, EnumerateForNames, Error, NameProc, StreamOpen], IO USING [STREAM], ProcessProps USING [GetPropList], PrincOpsUtils USING [LongCOPY], Rope USING [Cat, Concat, Equal, Fetch, Find, Flatten, FromRefText, Length, Replace, ROPE, Size, SkipTo, Substr]; CommandToolParseImpl: CEDAR PROGRAM IMPORTS Atom, Commander, Convert, FileNames, FS, PrincOpsUtils, ProcessProps, Rope EXPORTS CommandTool = { OPEN CommandTool; Failed: PUBLIC ERROR [errorMsg: Rope.ROPE] = CODE; Parse: PUBLIC PROC [cmd: Commander.Handle, switchChar: CHAR _ '-] RETURNS [argv: ArgumentVector] = { argv _ OldParse[commands: cmd.commandLine, switchchar: switchChar]; IF argv # NIL THEN argv[0] _ cmd.command; }; AmpersandSubstitution: PUBLIC PROC [cmd: Commander.Handle] = { AmpersandProc: PROC [thingName: Rope.ROPE] RETURNS [Rope.ROPE] = { arg: ATOM _ Atom.MakeAtom[thingName]; value: REF ANY; value _ Commander.GetProperty[key: arg, aList: ProcessProps.GetPropList[]]; IF value = NIL OR NOT ISTYPE[value, Rope.ROPE] THEN value _ Commander.GetProperty[key: arg, aList: cmd.propertyList]; IF NOT ISTYPE[value, Rope.ROPE] THEN RETURN[Rope.Concat["&", thingName]]; RETURN[NARROW[value, Rope.ROPE]]; }; Letters: PROC [c: CHAR] RETURNS [keep: BOOL] = { RETURN [c IN ['a..'z] OR c IN ['A..'Z] OR c IN ['0..'9]]; }; cmd.commandLine _ LookForThings[line: cmd.commandLine, thingChar: '&, charPredicate: Letters, proc: AmpersandProc]; }; DollarSubstitution: PUBLIC PROC [cmd: Commander.Handle] = { commandersArguments: ArgumentVector _ NARROW[Commander.GetProperty[key: $CommandFileArguments, aList: cmd.propertyList], ArgumentVector]; DollarProc: PROC [thingName: Rope.ROPE] RETURNS [Rope.ROPE] = { value: Rope.ROPE; failed: BOOL _ FALSE; argument: INT; argument _ Convert.IntFromRope[thingName ! Convert.Error => { value _ Rope.Concat["$", thingName]; failed _ TRUE; CONTINUE; }]; IF failed THEN RETURN[value]; IF commandersArguments = NIL OR commandersArguments.argc <= argument + 1 THEN RETURN [NIL]; RETURN[commandersArguments[argument + 1]]; }; Digits: PROC [c: CHAR] RETURNS [keep: BOOL] = { RETURN [c IN ['0..'9]]; }; cmd.commandLine _ LookForThings[line: cmd.commandLine, thingChar: '$, charPredicate: Digits, proc: DollarProc]; }; StarExpansion: PUBLIC PROC [cmd: Commander.Handle] = { token: Rope.ROPE; listOfTokens: LIST OF Rope.ROPE _ NIL; uecpArgv: ArgumentVector; IF cmd.commandLine.Find["*"] = -1 THEN RETURN; uecpArgv _ OldParse[commands: cmd.commandLine]; FOR i: NAT IN [1..uecpArgv.argc) DO token _ uecpArgv[i]; IF Rope.Find[token, "*"] # -1 THEN { ConsProc: FS.NameProc = { listOfTokens _ CONS[fullFName, listOfTokens]; -- on front of list RETURN[TRUE]; }; FS.EnumerateForNames[pattern: FileNames.ResolveRelativePath[token], proc: ConsProc]; } ELSE listOfTokens _ CONS[token, listOfTokens]; -- on front of list ENDLOOP; cmd.commandLine _ "\n"; WHILE listOfTokens # NIL DO cmd.commandLine _ Rope.Cat[" ", listOfTokens.first, cmd.commandLine]; listOfTokens _ listOfTokens.rest; ENDLOOP; cmd.commandLine _ Rope.Flatten[cmd.commandLine]; }; IORedirection: PUBLIC PROC [cmd: Commander.Handle] RETURNS [inRedirected: BOOL _ FALSE, outRedirected: BOOL _ FALSE] = { token: Rope.ROPE; listOfTokens: LIST OF Rope.ROPE _ NIL; uecpArgv: ArgumentVector; i: NAT; { IF cmd.commandLine.SkipTo[pos: 0, skip: "<>"] = cmd.commandLine.Length[] THEN RETURN; uecpArgv _ OldParse[commands: cmd.commandLine]; IF uecpArgv = NIL THEN ERROR Failed["Command line parse failure"]; cmd.commandLine _ NIL; i _ 1; DO IF i = uecpArgv.argc THEN EXIT; token _ uecpArgv[i]; i _ i + 1; SELECT TRUE FROM Rope.Equal[token, "<"] => { IF i = uecpArgv.argc THEN GOTO FailSyntax; IF inRedirected THEN ERROR Failed["input redirected twice"]; cmd.in _ OpenRedirect[uecpArgv[i], $read]; i _ i + 1; inRedirected _ TRUE; LOOP; }; Rope.Equal[token, ">"] => { IF i = uecpArgv.argc THEN GOTO FailSyntax; IF outRedirected THEN ERROR Failed["output redirected twice"]; cmd.out _ OpenRedirect[uecpArgv[i], $create]; i _ i + 1; outRedirected _ TRUE; LOOP; }; Rope.Equal[token, ">>"] => { IF i = uecpArgv.argc THEN GOTO FailSyntax; IF outRedirected THEN ERROR Failed["output redirected twice"]; cmd.out _ OpenRedirect[uecpArgv[i], $append]; i _ i + 1; outRedirected _ TRUE; LOOP; }; ENDCASE => cmd.commandLine _ Rope.Cat[cmd.commandLine, " ", token]; ENDLOOP; cmd.commandLine _ Rope.Flatten[Rope.Concat[cmd.commandLine, "\n"]]; EXITS FailSyntax => ERROR Failed["command line syntax error, redirection without filename"]; }; }; ParseToList: PUBLIC PROC [cmd: Commander.Handle, switchChar: CHAR _ '-] RETURNS [list: LIST OF Rope.ROPE, length: NAT _ 0] = { uecpArgv: ArgumentVector _ OldParse[commands: cmd.commandLine, switchchar: switchChar]; IF uecpArgv = NIL THEN RETURN[NIL]; length _ 0; FOR i: NAT DECREASING IN [1..uecpArgv.argc) DO list _ CONS[uecpArgv[i], list]; -- on front of list length _ length + 1; ENDLOOP; }; LookForThings: PROC [line: Rope.ROPE, thingChar: CHAR, charPredicate: PROC [c: CHAR] RETURNS [keep: BOOL], proc: PROC [thingName: Rope.ROPE] RETURNS [Rope.ROPE]] RETURNS [Rope.ROPE] = { state: {outside, insideQuotes, insideThing} _ outside; c: CHAR; i: INT _ 0; thingPosition: INT; WHILE i < line.Length[] DO c _ line.Fetch[i]; SELECT state FROM outside => { SELECT c FROM '" => { state _ insideQuotes; }; thingChar => { state _ insideThing; thingPosition _ i; }; ENDCASE; }; insideQuotes => { IF c = '" THEN { IF line.Length[] > (i + 1) AND line.Fetch[i+1] = '" THEN i _ i + 1 ELSE state _ outside; }; }; insideThing => { IF NOT charPredicate[c] THEN { thingName: Rope.ROPE _ Rope.Substr[base: line, start: thingPosition + 1, len: i - thingPosition - 1]; thingValue: Rope.ROPE _ proc[thingName: thingName]; line _ line.Replace[start: thingPosition, len: IF c = thingChar THEN thingName.Length[] + 2 ELSE thingName.Length[] + 1, with: thingValue]; i _ thingPosition + thingValue.Length[]; state _ outside; }; }; ENDCASE => ERROR; i _ i + 1; ENDLOOP; IF state = insideQuotes THEN ERROR Failed["Mismatched quotes"]; RETURN[line]; }; OpenRedirect: PROC [fileName: Rope.ROPE, accessOptions: FS.AccessOptions] RETURNS [stream: IO.STREAM _ NIL] = { errorMsg: Rope.ROPE; fileName _ FileNames.ResolveRelativePath[fileName]; stream _ FS.StreamOpen[fileName: fileName, accessOptions: accessOptions, keep: 2 ! FS.Error => IF error.group = user THEN { errorMsg _ error.explanation; CONTINUE }]; IF stream = NIL THEN ERROR Failed[Rope.Concat["Can't open file for IO redirection: ", errorMsg]]; }; UnmatchedQuote: ERROR = CODE; CPData: TYPE = RECORD [ ch: CHARACTER _ ' , -- first char of next token; initially blank swChar: CHARACTER, -- switch character ropeIndex: LONG INTEGER _ 0, -- index into Rope command line rope: Rope.ROPE _ NIL, -- command line passed from client done: BOOLEAN _ FALSE, -- used up all of command line argC: INT _ 0, -- number of tokens found args: LIST OF Rope.ROPE _ NIL -- list of tokens ]; OldParse: PROC [commands: Rope.ROPE, switchchar: CHARACTER _ '-] RETURNS [argv: ArgumentVector] = { cpd: REF CPData _ NEW[CPData]; Fooey: PROC RETURNS [LIST OF Rope.ROPE, INT] = { r: Rope.ROPE _ IGet[cpd]; IF r = NIL THEN RETURN [NIL, 1] ELSE { n: LIST OF Rope.ROPE; c: INT; [n, c] _ Fooey[]; RETURN [CONS[r, n], c+1]; }; }; cpd.swChar _ switchchar; cpd.rope _ commands; argv _ NIL; [cpd.args, cpd.argC] _ Fooey[! UnmatchedQuote => GOTO Lose]; argv _ NEW[ArgHandleObject[cpd.argC]]; argv[0] _ ""; FOR i: NAT IN [1..argv.argc) DO argv[i] _ cpd.args.first; cpd.args _ cpd.args.rest; ENDLOOP; EXITS Lose => Failed["Misatched quotes"]; };--Parse IGet: PROC [cpd: REF CPData] RETURNS [result: Rope.ROPE] = { chlook: CHARACTER; workstr: REF TEXT; RopeWP: PROC [c: CHARACTER] = { IF workstr=NIL THEN workstr _ NEW[TEXT[40]]; IF workstr.length >= workstr.maxLength THEN TRUSTED { old: REF TEXT _ workstr; workstr _ NEW[TEXT[2*old.maxLength]]; PrincOpsUtils.LongCOPY[ from: LOOPHOLE[old, LONG POINTER] + 2, nwords: (old.maxLength + 1)/2, to: LOOPHOLE[workstr, LONG POINTER] + 2]; workstr.length _ old.length; }; workstr[workstr.length] _ c; workstr.length _ SUCC[workstr.length]; }; WHILE (cpd.ch = ' OR cpd.ch = ' ) AND GetCh[cpd] DO ENDLOOP; IF cpd.done OR cpd.ch = '\n THEN RETURN[NIL]; chlook _ ' ; IF cpd.ch = '" THEN { [] _ GetCh[cpd]; chlook _ '"; }; DO IF cpd.done OR (chlook # '" AND cpd.ch = '\n) THEN GOTO EOL; SELECT chlook FROM ' , ' => SELECT cpd.ch FROM ' , '" => IF workstr#NIL THEN EXIT; cpd.swChar => IF cpd.ch#'- AND workstr#NIL THEN EXIT; ENDCASE; '" => SELECT cpd.ch FROM '" => IF (NOT GetCh[cpd]) OR (cpd.ch # '") THEN EXIT; ENDCASE; ENDCASE => ERROR; RopeWP[cpd.ch]; IF NOT GetCh[cpd] THEN GOTO EOL; REPEAT EOL => IF chlook = '" THEN ERROR UnmatchedQuote; ENDLOOP; IF workstr = NIL OR workstr.length=0 THEN RETURN [NIL]; result _ Rope.FromRefText[workstr]; };--IGet GetCh: PROC [cpd: REF CPData] RETURNS [BOOL] = { IF cpd.ropeIndex >= Rope.Size[cpd.rope] THEN GOTO EOS; cpd.ch _ Rope.Fetch[cpd.rope, cpd.ropeIndex]; cpd.ropeIndex _ SUCC[cpd.ropeIndex]; RETURN [TRUE]; EXITS EOS => { cpd.done _ TRUE; cpd.ch _ ' ; RETURN [FALSE]; }; }; -- GetCh }.-- UECPImpl CHANGE LOG Created by Stewart on 19-Mar-82 17:31 Rename Open to Parse by Gifford 23-Mar-82 14:46:08 CEDAR PROGRAM, Schroeder, September 17, 1982 3:20 pm October 5, 1983 4:05 pm, Stewart renamed Parse to OldParse. Handles TAB now (interpreted like SP) added new Parse routine December 2, 1983 10:22 pm, Stewart Fixed LookForThings to eat the trailing character if it is the same as the thingChar CommandToolParseImpl.mesa Gifford on June 18, 1982 10:55 am Paul Rovner on July 20, 1983 2:39 pm Stewart on December 12, 1983 12:45 pm Now the tokens must be glued back together again, but the listOfTokens is in reverse order Returns next token. This scanner (1) ignores leading blanks, (2) returns a quoted token intact (excluding the quotes), (3) returns an unquoted token delimited by a blank (not included) or switchchar (included in next token), the default switchchar is '/ (4) returns NIL at CR or end of file. (5) if the switchchar is '- then it must be preceded by a blank delimiter for token: either blank, meaning blank or switchchar (usually '/), or '", or '", meaning '" not followed by another '". ch now contains a character to be included in the token �Ê ‘˜šœ™Jšœ!™!Jšœ$™$Jšœ%™%—J˜šÏk ˜ Jšœ�œ ˜Jšœ �œ˜&Jšœ ˜ Jšœ�œ˜#Jšœ �œ˜&Jš�œ�œA˜IJš�œ�œ�œ˜Jšœ �œ˜!Jšœ�œ ˜Jšœ�œJ�œ˜pJ˜—šœ�œ�˜#Jš�œ&�œ#˜RJš�œ˜Jš�œ ˜J˜Jš œ�œ�œ�œ�œ˜2J˜š Ïnœ�œ�œ%�œ�œ˜dJšœC˜CJšœ)˜)J˜—J˜šžœ�œ�œ˜>š ž œ�œ�œ�œ�œ˜BJšœ�œ˜%Jšœ�œ�œ˜JšœK˜KJš�œ �œ�œ�œ�œ �œ�œB˜uJš �œ�œ�œ �œ�œ�œ˜IJš�œ�œ �œ˜!J˜—š žœ�œ�œ�œ�œ˜0Jš �œ�œ �œ�œ �œ�œ ˜9J˜—Jšœs˜sJ˜—J˜šžœ�œ�œ˜;Jšœ&�œ]˜‰š ž œ�œ�œ�œ�œ˜?Jšœ �œ˜Jšœ�œ�œ˜Jšœ �œ˜šœ=˜=Jšœ$˜$Jšœ �œ˜Jš�œ˜ Jšœ˜—Jš�œ�œ�œ˜Jš �œ�œ�œ*�œ�œ�œ˜[Jš�œ$˜*J˜—š žœ�œ�œ�œ�œ˜/Jš�œ�œ ˜J˜—Jšœo˜oJ˜—J˜šž œ�œ�œ˜6Jšœ �œ˜Jš œ�œ�œ�œ�œ˜&Jšœ˜Jš�œ �œ�œ˜.Jšœ/˜/š�œ�œ�œ�˜#Jšœ˜š�œ�œ˜$šœ �œ ˜Jšœ�œÏc˜BJš�œ�œ˜ J˜—Jš�œR˜TJ˜—Jš�œ�œŸ˜CJš�œ˜—JšœZ™ZJšœ˜š�œ�œ�˜JšœE˜EJšœ!˜!Jš�œ˜—Jšœ0˜0J˜—J˜šž œ�œ�œ�œ�œ�œ�œ�œ˜xJšœ �œ˜Jš œ�œ�œ�œ�œ˜&Jšœ˜Jšœ�œ˜J˜Jš�œG�œ�œ˜UJšœ/˜/Jš�œ �œ�œ�œ&˜BJšœ�œ˜J˜š�˜Jš�œ�œ�œ˜Jšœ˜J˜ š�œ�œ�˜šœ˜Jš�œ�œ�œ ˜*Jš�œ�œ�œ"˜Jšœ-˜-J˜ Jšœ�œ˜Jš�œ˜J˜—šœ˜Jš�œ�œ�œ ˜*Jš�œ�œ�œ#˜>Jšœ-˜-J˜ Jšœ�œ˜Jš�œ˜J˜—Jš�œ<˜C—Jš�œ˜—JšœC˜CJš�˜Jšœ�œC˜VJ˜J˜—J˜šž œ�œ�œ%�œ�œ�œ�œ�œ �œ ˜~JšœW˜WJšœ#˜#Jšœ ˜ š �œ�œ� œ�œ�˜.Jšœ�œŸ˜4Jšœ˜Jš�œ˜—J˜—J˜šž œ�œ �œ �œ�œ�œ�œ�œ �œ�œ�œ�œ�œ�œ˜¹J˜6Jšœ�œ˜Jšœ�œ˜ Jšœ�œ˜š�œ�˜Jšœ˜š�œ�˜šœ ˜ š�œ�˜ šœ˜Jšœ˜J˜—šœ˜Jšœ˜J˜J˜—Jš�œ˜—J˜—˜š�œ�œ˜Jš�œ�œ�œ ˜BJš�œ˜J˜—J˜—šœ˜š�œ�œ�œ˜Jšœ�œQ˜eJšœ�œ˜3Jšœ/�œ�œ�œ+˜‹Jšœ(˜(Jšœ˜J˜—J˜—Jš�œ�œ˜—J˜ Jš�œ˜—Jš�œ�œ�œ˜?Jš�œ˜ J˜—J˜šž œ�œ�œ�œ�œ �œ�œ�œ˜oJšœ�œ˜Jšœ3˜3Jš œ �œH�œ �œ�œ!�œ˜¦Jš�œ �œ�œ�œG˜aJ˜—J˜Jšœ�œ�œ˜J˜šœ�œ�œ˜Jšœ� œŸ,˜@Jšœ� œŸ˜&Jšœ �œ�œŸ˜=Jšœ �œ�œŸ"˜9Jšœ�œ�œŸ˜5Jšœ�œŸ˜(Jš œ�œ�œ�œ�œŸ˜0J˜J˜—š žœ�œ�œ� œ�œ˜cJšœ�œ �œ ˜šžœ�œ�œ�œ�œ�œ�œ˜0Jšœ�œ ˜Jš �œ�œ�œ�œ�œ˜š�œ˜Jšœ�œ�œ�œ˜Jšœ�œ˜J˜Jš�œ�œ ˜J˜—J˜—J˜J˜Jšœ�œ˜ Jšœ1�œ˜