Oberon || Library || Module Index || Search Engine || Definition || Module
CONST nameLen = 256; TYPE Name = ARRAY nameLen OF CHAR; (* the name of an argument *) TYPE Value = POINTER TO ValueRec; (* the value of an argument *) TYPE ValueRec = RECORD (PersistentDisciplines.ObjectRec) END; TYPE Arguments = POINTER TO ArgumentsRec; (* collection of arguments *) TYPE ArgumentsRec = RECORD (PersistentDisciplines.ObjectRec) END;
TYPE ReadProc = PROCEDURE (s: Streams.Stream; VAR value: Value) : BOOLEAN; TYPE PrintProc = PROCEDURE (s: Streams.Stream; value: Value); TYPE HelpProc = PROCEDURE (s: Streams.Stream); TYPE TypeInterface = POINTER TO TypeInterfaceRec; TYPE TypeInterfaceRec = RECORD (Objects.ObjectRec) read: ReadProc; print: PrintProc; help: HelpProc; (* may be NIL *) END; TYPE Type = POINTER TO TypeRec; TYPE TypeRec = RECORD (Disciplines.ObjectRec) name: Name; moduleName: Name; END;
TYPE ScanProc = PROCEDURE (args: Arguments); TYPE ScannerList = POINTER TO ScannerListRec; TYPE ScannerListRec = RECORD (Disciplines.ObjectRec) END;
TYPE Argument = POINTER TO ArgumentRec; TYPE ArgumentRec = RECORD (Disciplines.ObjectRec) name: Name; short: CHAR; type: Type; description: Name; END;
TYPE ChangeNotification = POINTER TO ChangeNotificationRec; TYPE ChangeNotificationRec = RECORD (Events.EventRec) args: Arguments; arg: Argument; END;
CONST lowPriority = 0; CONST middlePriority = 1; CONST highPriority = 2; TYPE Priority = INTEGER;
VAR systemScanners: ScannerList; (* predefined scanners *)
CONST cannotReadArgs = 0; (* failed to read list of arguments *) CONST unknownTypeName = 1; (* unknown type name encountered *) CONST twiceDefined = 2; (* an argument name has been defined twice *) CONST cannotWriteArgs = 3; (* failed to write list of arguments *) CONST unknownArgName = 4; (* an unknown argument name was given *) CONST namedErrors = {unknownTypeName, twiceDefined, unknownArgName}; CONST errorcodes = 5; (* number of error codes *) TYPE ErrorEvent = POINTER TO ErrorEventRec; TYPE ErrorEventRec = RECORD (Events.EventRec) errorcode: SHORTINT; name: Name; (* defined if errorcode IN namedErrors *) END; VAR errormsg: ARRAY errorcodes OF Events.Message; VAR error: Events.EventType;
(* client side *) PROCEDURE Create(VAR args: Arguments); PROCEDURE CreateCopyOf(VAR args: Arguments; orig: Arguments); PROCEDURE Define(args: Arguments; name: ARRAY OF CHAR; short: CHAR; type: Type; description: ARRAY OF CHAR); PROCEDURE Exists(args: Arguments; name: ARRAY OF CHAR) : BOOLEAN; PROCEDURE Scan(args: Arguments; scanners: ScannerList); PROCEDURE GetValue(args: Arguments; name: ARRAY OF CHAR; VAR value: Value); PROCEDURE SetValue(args: Arguments; name: ARRAY OF CHAR; value: Value); PROCEDURE GetNotification(args: Arguments; name: ARRAY OF CHAR; VAR eventType: Events.EventType); PROCEDURE Print(args: Arguments; name: ARRAY OF CHAR; s: Streams.Stream); PROCEDURE PrintValue(s: Streams.Stream; value: Value); PROCEDURE ReadValue(s: Streams.Stream; type: Type; VAR value: Value) : BOOLEAN; PROCEDURE IterateArgs(args: Arguments; VAR it: Iterators.Iterator); PROCEDURE Seek(args: Arguments; name: ARRAY OF CHAR; VAR arg: Argument); PROCEDURE String2Type(string: ARRAY OF CHAR) : Type; PROCEDURE TypeHelp(args: Arguments; type: Type; s: Streams.Stream); PROCEDURE IterateTypes(VAR it: Iterators.Iterator); PROCEDURE AssignPrintProc(s: Streams.Stream; type: Type; print: PrintProc);
(* scanner providers *) PROCEDURE CreateScannerList(VAR list: ScannerList); PROCEDURE RegisterScanner(scanners: ScannerList; scan: ScanProc; priority: Priority); PROCEDURE ScanValue(args: Arguments; name: ARRAY OF CHAR; stream: Streams.Stream) : BOOLEAN;
(* type providers *) PROCEDURE DefineType(type: Type; if: TypeInterface; valtype: Services.Type);
Arguments are name/value pairs with some additional components which are organized in argument collections. Argument values are typed and the types are associated with a set of interface procedures which allow to read and print them. A scanner knows how to retrieve arguments from a specific area (e.g. from the command line). Scan allows to fire a set of scanners for a given argument collection.
The following example shows the creation of an argument list with three arguments:
VAR args: Args.Arguments; (* argument list *)The name of an argument must be unique for the given argument list. Optionally, a short name may be given (one character long). The last parameter is a short help text which may be later retrieved by IterateArgs.
(* ... *)
Args.Create(args); Args.Define(args, "copies", "#", IntArgs.type, "number of copies"); Args.Define(args, "file", "f", StringArgs.type, "file to be printed"); Args.Define(args, "printer", "p", StringArgs.type, "name of the printer");
It is possible to give argument default values, or to modify them at a later time by SetValue. In the example above, the number of copies should probably default to 1:
VAR value: Args.Value;
(* ... *) IntArgs.Create(value, 1); Args.SetValue(args, "copies", value);
CreateCopyOf allows to create a new argument list as clone of the already existing list orig.
Scan allows to call all scanners of a scanner list for a given argument list. All indirectly invoked scanners try to locate settings for the arguments in their area and to assign them. Usually, systemScanners should be given as scanners to Scan. UnixCommandLine, for example, (which is member of systemScanners) would assign letter to file and hp to printer for following command line:
cmdname -p hp -file letter
After calling Scan, it is useful to retrieve values by GetValue. Note that GetValue returns NIL for undefined values. In the example above, the file which is to be printed may be retrieved by:
VAR file: StringArgs.Value; filename: ARRAY 80 OF CHAR;
(* ... *)
Args.GetValue(args, "file", file); IF file = NIL THEN (* a file name was not given anywhere *) ELSE COPY(file.string, filename); END;
PROCEDURE PrintHelpText(s: Streams.Stream; args: Args.Arguments); VAR it: Iterators.Iterator; arg: Args.Argument; BEGIN Args.IterateArgs(args, it); WHILE Iterators.Yield(it, arg) DO Print.S4(s, "%1s %-20s %-8s %s\n", arg.short, arg.name, arg.type.name, arg.description); END; END PrintHelpText;
Print and PrintValue allow to print arbitrary values to the given stream. Following example shows how the values of all defined arguments may be printed:
PROCEDURE PrintValues(s: Streams.Stream; args: Args.Arguments); VAR it: Iterators.Iterator; arg: Args.Argument; BEGIN Args.IterateArgs(args, it); WHILE Iterators.Yield(it, arg) DO Print.S2(s, "%-20s: %-8s = ", arg.name, arg.type.name); Args.Print(args, arg.name, s); Print.S(s, "\n"); END; END PrintValues;Note that it is possible to override for a given stream the original type-specific printing procedure for a specific type by AssignPrintProc.
Informations about the supported argument types may be retrieved by TypeHelp, and IterateTypes allows to iterate through all supported types. String2Type allows to convert a (probably user-supplied) type name into a type. NIL is returned if the type name is not known.
Scanners may check the existence of argument names by Exist. It depends usually on the scanner whether missing argument names are considered as an error (of the invoker / user) or not. UnixCommandLine, for example, generates error events for each option named on the command line which is not part of the argument list. Other scanners, however, are less restrictive.
If an argument has been found, it may be read in by ScanValue which, in turn, calls the type-specific read procedure of the argument. Note that these read procedures are expected to interpret field separators and line terminators (see StreamDisciplines) as delimiters. Alternatively, Seek may be called to access the argument directly.
Following example shows a simple implementation of a scanner which takes arguments out of a rc file whose lines have two fields which are separated by a colon. The first field is the name of the parameter and the second the associated value:
PROCEDURE ScanFile(args: Args.Arguments); VAR s: Streams.Stream; fieldseps: Sets.CharSet; name: Args.Name; BEGIN (* open the rc file and let s point to it *) RelatedEvents.Forward(s, args); Sets.InitSet(fieldseps); Sets.InclChar(fieldseps, ":"); StreamDisciplines.SetFieldSepSet(s, fieldseps); WHILE Read.FieldS(s, name) DO IF ~Args.Exists(args, name) OR ~Args.ScanValue(args, name, s) THEN (* raise an appropriate error event and relate it to args *) END; Read.LnS(s); END; Streams.Release(s); END ScanFile;
Additionally, type providers should export a simple constructor to allow default values to be set.
ReadValue allows to create a value of the given type and to scan it from s. This may be useful for type constructors whose read interface procedures want to read values of their subtypes.
Some errors are caught by assertions:
Oberon || Library || Module Index || Search Engine || Definition || Module