DIRECTORY Args, Commander, CommanderOps, Convert, IO, Rope; ArgsImpl: CEDAR PROGRAM IMPORTS Commander, CommanderOps, Convert, IO, Rope EXPORTS Args ~ BEGIN ROPE: TYPE ~ Rope.ROPE; Arg: TYPE ~ Args.Arg; NArgs: PUBLIC PROC [cmd: Commander.Handle] RETURNS [INTEGER] ~ { RETURN[CommanderOps.Parse[cmd].argc-1]; }; GetRope: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0] RETURNS [ROPE] ~ { argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd]; RETURN [IF argv.argc < nArg+2 OR nArg < 0 THEN NIL ELSE argv[nArg+1]]; }; ArgRope: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0] RETURNS [a: Arg] ~ { a.rope ¬ GetRope[cmd, nArg]; a.ok ¬ a.rope # NIL; }; ArgReal: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0] RETURNS [a: Arg] ~ { r: ROPE ¬ GetRope[cmd, nArg]; a.ok ¬ r # NIL; IF a.ok THEN a.real ¬ Convert.RealFromRope[r ! Convert.Error => {a.ok ¬ FALSE; CONTINUE}]; }; ArgInt: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0] RETURNS [a: Arg] ~ { r: ROPE ¬ GetRope[cmd, nArg]; a.ok ¬ r # NIL; IF a.ok THEN a.int ¬ Convert.IntFromRope[r ! Convert.Error => {a.ok ¬ FALSE; CONTINUE}]; }; ArgIntDef: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0, defVal: INT] RETURNS [a: Arg] ~ { r: ROPE ¬ GetRope[cmd, nArg]; a.ok ¬ TRUE; a.int ¬ IF r = NIL THEN Convert.IntFromRope[r ! Convert.Error => {a.ok ¬ FALSE; CONTINUE}] ELSE defVal; }; ArgIntRange: PUBLIC PROC [cmd: Commander.Handle, n, min, max: INT] RETURNS [arg: Arg] ~ { arg ¬ ArgInt[cmd, n]; IF NOT arg.ok THEN RETURN; IF arg.ok THEN arg.ok ¬ arg.int >= min AND arg.int <= max; }; Error: PUBLIC ERROR [reason: ROPE] = CODE; ArgsGetFromRope: PUBLIC PROC [input, format: ROPE, caseSensitive: BOOL ¬ FALSE] RETURNS [Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg] ~ { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17: Arg; cmd: Commander.Handle ¬ CommanderOps.CreateFromStreams[]; cmd.commandLine ¬ input; [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17] ¬ ArgsGet[cmd, format, caseSensitive]; RETURN[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17]; }; ArgsGet: PUBLIC PROC [cmd: Commander.Handle, format: ROPE, caseSensitive: BOOL ¬ FALSE] RETURNS [Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg] ~ { maxNRet: INTEGER ~ 18; maxNArgs: INTEGER ~ 100; retArgs: ARRAY[0..maxNRet) OF Arg; usedArgs: PACKED ARRAY [0..maxNArgs) OF BOOL ¬ ALL[FALSE]; nArgs: INTEGER ¬ MIN[maxNArgs, NArgs[cmd]]; formatLength: INTEGER ¬ Rope.Length[format]; argIndex: INTEGER ¬ 0; formatIndex: INTEGER ¬ 0; retIndex: INTEGER ¬ 0; SetRets: PROC ~ { optional: BOOL ¬ Rope.Fetch[format, formatIndex] = '[; formatIndex ¬ formatIndex+1; WHILE formatIndex < formatLength AND retIndex < maxNRet DO c: CHAR ¬ Rope.Fetch[format, formatIndex]; SELECT c FROM '%, '[, '-, IO.SP => RETURN; 'b => retArgs[retIndex] ¬ [ok: TRUE, bool: TRUE]; 'i, 's, 'r => { WHILE argIndex < nArgs AND usedArgs[argIndex] DO argIndex ¬ argIndex+1; ENDLOOP; IF argIndex >= nArgs THEN { IF NOT optional THEN ERROR Error["Missing non-optional argument"]; formatIndex ¬ Rope.SkipTo[format, formatIndex, "-"]; RETURN }; retArgs[retIndex] ¬ SELECT c FROM 'i => ArgInt[cmd, argIndex], 's => ArgRope[cmd, argIndex], 'r => ArgReal[cmd, argIndex], ENDCASE => []; usedArgs[argIndex] ¬ TRUE; argIndex ¬ argIndex+1; IF NOT retArgs[retIndex].ok THEN { IF NOT optional THEN Error["Conversion error"]; formatIndex ¬ formatIndex+1; LOOP; }; }; ENDCASE => { formatIndex ¬ Rope.SkipTo[format, formatIndex, "%[-"]; Error["Unrecognized delimiter or specifier"]; }; formatIndex ¬ formatIndex+1; retIndex ¬ retIndex+1; ENDLOOP; }; SkipTokens: PROC ~ { formatIndex ¬ formatIndex+1; WHILE formatIndex < formatLength DO SELECT Rope.Fetch[format, formatIndex] FROM 'i, 's, 'r, 'b => retIndex ¬ retIndex+1; '%, '[, '- => RETURN; ENDCASE => Error["Unrecognized delimiter or specifier"]; formatIndex ¬ formatIndex+1; ENDLOOP; }; WHILE formatIndex < formatLength DO -- first, set hyphenated arguments: SELECT Rope.Fetch[format, formatIndex] FROM '%, '[ => SkipTokens[]; '- => { key: ROPE; n: INTEGER ¬ Rope.SkipTo[format, formatIndex, "[%"]; IF n = formatLength THEN Error["No matching specifier"]; key ¬ Rope.Substr[format, formatIndex, n-formatIndex]; formatIndex ¬ n; argIndex ¬ 1; WHILE argIndex <= nArgs DO IF Rope.Equal[GetRope[cmd, argIndex-1], key, caseSensitive] THEN { usedArgs[argIndex-1] ¬ TRUE; SetRets[]; EXIT; }; argIndex ¬ argIndex+1; REPEAT FINISHED => formatIndex ¬ Rope.SkipTo[format, formatIndex, "-%["]; ENDLOOP; }; ENDCASE => { formatIndex ¬ Rope.SkipTo[format, formatIndex, "-%["]; Error["Unrecognized delimiter"]; }; ENDLOOP; argIndex ¬ retIndex ¬ formatIndex ¬ 0; -- now set non-hyphenated arguments: WHILE formatIndex < formatLength DO SELECT Rope.Fetch[format, formatIndex] FROM '- => {formatIndex ¬ Rope.SkipTo[format, formatIndex, "%"]; SkipTokens[]}; '%, '[ => SetRets[]; ENDCASE => { formatIndex ¬ formatIndex+1; Error["Unrecognized delimiter"]; }; ENDLOOP; FOR n: INTEGER IN[0..nArgs) DO IF NOT usedArgs[n] THEN Error["Extraneous argument"]; ENDLOOP; RETURN[ retArgs[0], retArgs[1], retArgs[2], retArgs[3], retArgs[4], retArgs[5], retArgs[6], retArgs[7], retArgs[8], retArgs[9], retArgs[10], retArgs[11], retArgs[12], retArgs[13], retArgs[14], retArgs[15], retArgs[16], retArgs[17]]; }; ArgsTest: Commander.CommandProc ~ { args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd]; IF args.argc < 2 THEN RETURN[$Failure, "usage: ArgsTest , tests are: 1: [argument list] 2: [optional integer] 3: [optional real] 4: [optional real] 5: [optional real +/or integer] 6: [-switch ]"] ELSE { cmd.commandLine ¬ NIL; FOR i: INTEGER IN [2..args.argc) DO cmd.commandLine ¬ Rope.Cat[cmd.commandLine, " ", args[i]]; ENDLOOP; SELECT Convert.IntFromRope[args[1] ! Convert.Error => GOTO Bad] FROM 1 => [result, msg] ¬ ArgsTest1[cmd]; 2 => [result, msg] ¬ ArgsTest2[cmd]; 3 => [result, msg] ¬ ArgsTest3[cmd]; 4 => [result, msg] ¬ ArgsTest4[cmd]; 5 => [result, msg] ¬ ArgsTest5[cmd]; 6 => [result, msg] ¬ ArgsTest6[cmd]; ENDCASE => GOTO Bad; }; EXITS Bad => RETURN[$Failure, "bad test #"]; }; ArgsTest1: Commander.CommandProc ~ { I: PROC [a: Arg, r: ROPE] ~ { IF NOT a.ok THEN RETURN; IO.PutFL[cmd.out, "%g:\tbool: %g\tint: %g\treal: %g\trope: %g\n", LIST[IO.rope[r], IO.bool[a.bool], IO.int[a.int], IO.real[a.real], IO.rope[a.rope]]]; IF NOT a.ok THEN IO.PutRope[cmd.out, "\tNOT ok\n"]; }; a, b, c, d, e, f, g, i, l, m, n: Arg; [a, b, c, d, e, f, g, i, l, m, n] ¬ ArgsGet [cmd, "-a%b-b%b-c%b-d%r-e%i-f%r-g%s-i%s-l%s-m%s-n%b" ! Error => {msg ¬ reason; GOTO Bad}]; I[a,"a"];I[b,"b"];I[c,"c"];I[d,"d"];I[e,"e"];I[f,"f"];I[g,"g"];I[i,"i"];I[l,"l"];I[m,"m"];I[n,"n"]; EXITS Bad => RETURN[$Failure, msg]; }; ArgsTest2: Commander.CommandProc ~ { IO.PutF1[cmd.out, "arg = %g\n", IO.int[ArgIntDef[cmd, 0, 3].int]]; }; ArgsTest3: Commander.CommandProc ~ { value: REAL ¬ IF ArgReal[cmd].ok THEN ArgReal[cmd].real ELSE 1.2; IO.PutF1[cmd.out, "arg = %g\n", IO.real[value]]; }; ArgsTest4: Commander.CommandProc ~ { a: Args.Arg; [a] ¬ ArgsGet[cmd, "[r"]; IO.PutF1[cmd.out, "arg = %g\n", IO.real[IF a.ok THEN a.real ELSE 1.2]]; }; ArgsTest5: Commander.CommandProc ~ { a, b: Args.Arg; [a, b] ¬ ArgsGet[cmd, "[ri"]; IO.PutF[cmd.out, "a = %g, b = %g\n", IO.real[IF a.ok THEN a.real ELSE 1.2], IO.int[IF b.ok THEN b.int ELSE 3]]; }; ArgsTest6: Commander.CommandProc ~ { a, b: Args.Arg; [a, b] ¬ ArgsGet[cmd, "-a%r-b%i"]; IO.PutF[cmd.out, "a = %g, b = %g\n", IO.real[IF a.ok THEN a.real ELSE 1.2], IO.int[IF b.ok THEN b.int ELSE 3]]; }; Commander.Register["ArgsTest", ArgsTest, "\nArgsTest [argument list]."]; END.  ArgsImpl.mesa Copyright Σ 1985, 1991 by Xerox Corporation. All rights reserved. Bloomenthal, October 22, 1992 3:26 pm PDT Michael Plass, November 22, 1991 5:19 pm PST Public Procedures SetRets: enter at '% or ']; return at next '%, '[, '-, or end of format string. RETURN SkipTokens: enter at '% or ']; return at next '%, '[, '-, or end of format string. Tests General Test of ArgsGet: Tests for Defaults: ArgIntDef will return a default 3 if no command line argument is specified: If there is a real argument specified, return it, otherwise return 1.2: Use the optional delimiter ("[") to find an argument; if no such argument, use 1.2: Use the optional delimiter for two optional arguments (argument order is fixed): Use a key specifier for two optional arguments: Κ τ•NewlineDelimiter –(cedarcode) style™šœ ™ Jšœ Οeœ6™BJ™)J™,—Icode˜KšΟk œ)žœ˜;K˜šΠblœžœž˜Kšžœ#žœ˜2Kšžœ˜ —šœž˜K˜Kšžœžœžœ˜Kšœžœ ˜—headšΟl™šΟnœž œžœžœ˜@Kšžœ!˜'K˜K˜—š ‘œž œžœžœžœ˜RK˜