Copyright (C) 1994, Digital Equipment Corp.
This interface provides simple and robust tools for parsing the command line arguments given to a process when it is started (see
Params and Process.Create).
\index{parameters of a process} \index{process!parameters}
INTERFACENOTE: Before reading the details, check the usage example at the end of this interface.ParseParams ;
IMPORT Wr;
EXCEPTION Error;
TYPE
T <: Public;
(* A parser for UNIX-style command line arguments. *)
Public = OBJECT
arg: REF ARRAY OF TEXT;
(* Arguments given, including the command name "arg[0]",
but excluding any "@M3" directives. *)
parsed: REF ARRAY OF BOOLEAN;
(* Flag "parsed[i]" is "TRUE" if "arg[i]
has been parsed. *)
next: CARDINAL;
(* The next argument to parse is "arg[next]" *)
METHODS
init (wr: Wr.T): T;
(* Allocates the arrays "arg" and "parsed" and
initializes them with the parameters of the current
process. Marks "arg[0]" as parsed, all others as unparsed,
and sets "next" to 1. Any subsequent parsing errors
will be printed out to "wr". *)
keywordPresent (key: TEXT): BOOLEAN;
(* Looks for the first unparsed argument "arg[i]"
that is equal to "key". If found, marks it as parsed,
sets "next" to "i+1", and returns "TRUE".
Otherwise returns "FALSE" and leaves "next" unchanged. *)
getKeyword (key: TEXT) RAISES {Error};
(* Same as "keywordPresent", but raises "Error" if the
keyword is not found. *)
getNext (): TEXT RAISES {Error};
(* Returns "arg[next]", marks it as parsed and increments "next".
Raises "Error" if "arg[next]" does not exist or has already
been parsed. *)
testNext (key: TEXT): BOOLEAN RAISES {};
(* If "arg[next]" exists, is unparsed, and is equal to "key",
marks it as parsed, increments "next" and returns TRUE.
Otherwise does none of these things and returns "FALSE". *)
getNextInt (
min:=FIRST(INTEGER); max:=LAST(INTEGER)
): INTEGER RAISES {Error};
getNextReal (
min:=FIRST(REAL); max:=LAST(REAL)
): REAL RAISES {Error};
getNextLongReal (
min:=FIRST(LONGREAL); max:=LAST(LONGREAL)
): LONGREAL RAISES {Error};
(* Same as "getNext", but converts the result to the approriate
type (using "Scan.Int", "Scan.Real", "Scan.LongReal").
Raises "Error" if the parameter is not a valid literal, or
lies outside of the range "[min..max]". *)
error (msg: TEXT) RAISES {Error};
(* Prints the given message, and raises "Error". *)
skipParsed () RAISES {Error};
(* Points "next" at the first unparsed argument.
If there are parsed arguments beyond that one,
prints a message and raises "Error". *)
finish () RAISES {Error};
(* Checks if all parameters have been parsed; if not,
prints a message and raises "Error". *)
END;
END ParseParams.
In some popular operating systems, most programs expect their command-line arguments to consist of a string of keywords and keyword-labeled arguments (`options', `switches', etc.), followed by a list of positional arguments.
To help the user, programs generally allow the switches and keyword-labeled arguments to be given in any order. Some of those parameters may be optional and/or repeatable, some may be mandatory; some may be required or forbidden depending on the values of the other parameters. Furthermore, the value of an argument may be just a number or a text string, or may be a cluster of two or more values with their own little syntax.
This module simplifies the parsing of such command-line parameters,
by allowing the program to scan the arguments in their logical
order. It also detects automatically many kinds of common
mistakes---arguments that are missing, repeated, extraneous,
malformed, or out of range---and prints the appropriate error
messages.
For example, here is how this module could be used by an
hypothetical program prt that concatenates a bunch of files and
prints selected pages ranges, possibly in reverse order, with
several formatting options.
CONST
MaxPages = 10000;
VAR (* Arguments from command line:
| fontSize: CARDINAL;
| landscape: BOOLEAN;
| nRanges: CARDINAL := 0;
| ini, fin: ARRAY [0..99] OF [1..MaxPages];
| rev: ARRAY [0..99] OF BOOLEAN;
| files: REF ARRAY OF TEXT;
|
| PROCEDURE ParseCommandLine () =
| CONST
| Usage =
| "prt \\\n" &
| " -fontSize <n> \\\n" &
| " [ -landscape | -portrait ] \\\n" &
| " [ -pages <n> <n> [ -reverse ] ]... \\\n" &
| " file...\n";
| BEGIN
| WITH
| pp = NEW(ParseParams.T).init(Stdio.stderr)
| DO
| TRY
|
| (* The "-fontSize " parameter is mandatory: *)
| pp.getKeyword("-fontSize");
| fontSize := pp.getNextInt(1,100);
|
| (* Either "-landscape" or "-portrait", but not both: *)
| IF pp.keywordPresent("-landscape") THEN
| landscape := TRUE
| ELSIF pp.keywordPresent("-portrait") THEN
| landscape := FALSE
| ELSE
| (* Default is "-portrait" unless font is too big: *)
| landscape := (fontSize > 8)
| END;
|
| (* Parse the page ranges: *)
| nRanges := 0;
| WHILE pp.keywordPresent("-pages") DO
| IF nRanges > LAST(ini) THEN pp.error("Too many page ranges") END;
| ini[nRanges] := pp.getNextInt(1,MaxPages);
| fin[nRanges] := pp.getNextInt(ini[nRanges],MaxPages);
| rev[nRanges] := pp.testNext("-reverse");
| nRanges := nRanges+1;
| END;
| IF nRanges = 0 THEN
| ini[0] := 1; fin[0] := MaxPages; rev[0] := FALSE;
| nRanges := 1
| END;
|
| (* Parse the file list: *)
| pp.skipParsed();
| WITH nFiles = NUMBER(pp.arg^) - pp.next DO
| IF nFiles = 0 THEN pp.error("no files specified") END;
| files := NEW(REF ARRAY OF TEXT, );
| FOR i := 0 TO nFiles-1 DO
| files[i] := pp.getNext()
| END
| END;
|
| (* Check for any unparsed parameters: *)
| pp.finish();
|
| EXCEPT
| ParseParams.Error =>
| Wr.PutText(Stdio.stderr, Usage);
| Process.Exit(1);
| END
| END
| END ParseCommandLine;
Note that even though this code parses the parameters in a fixed
order, the user may give them in any order.
*)