diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2016-11-03 06:26:42 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2016-11-03 06:26:42 -0700 |
commit | 935f58e8941f03590bbd5c8482a31b50cf233802 (patch) | |
tree | 60e7aba15bda7669cf005af9ce5d4e058e52348f | |
parent | d01a12405fbffb6a68345f72a510bf9e25e8ef95 (diff) | |
download | txr-935f58e8941f03590bbd5c8482a31b50cf233802.tar.gz txr-935f58e8941f03590bbd5c8482a31b50cf233802.tar.bz2 txr-935f58e8941f03590bbd5c8482a31b50cf233802.zip |
Introducing command line option processing system.
* lisplib.c (getopts_set_entries, getopts_instantiate): New
functions.
(lisplib_init): Register auto-loading for getopt.tl
via new functions.
* share/txr/stdlib/getopts.tl: New file.
* txr.1: Documented new library area.
-rw-r--r-- | lisplib.c | 19 | ||||
-rw-r--r-- | share/txr/stdlib/getopts.tl | 320 | ||||
-rw-r--r-- | txr.1 | 386 |
3 files changed, 725 insertions, 0 deletions
@@ -387,6 +387,24 @@ static val trace_instantiate(val set_fun) return nil; } +static val getopts_set_entries(val dlt, val fun) +{ + val name[] = { + lit("opt-desc"), lit("opts"), + lit("opt"), lit("getopts"), lit("opthelp"), + nil + }; + set_dlt_entries(dlt, name, fun); + return nil; +} + +static val getopts_instantiate(val set_fun) +{ + funcall1(set_fun, nil); + load(format(nil, lit("~agetopts.tl"), stdlib_path, nao)); + return nil; +} + val dlt_register(val dlt, val (*instantiate)(val), val (*set_entries)(val, val)) @@ -419,6 +437,7 @@ void lisplib_init(void) dlt_register(dl_table, awk_instantiate, awk_set_entries); dlt_register(dl_table, build_instantiate, build_set_entries); dlt_register(dl_table, trace_instantiate, trace_set_entries); + dlt_register(dl_table, getopts_instantiate, getopts_set_entries); } val lisplib_try_load(val sym) diff --git a/share/txr/stdlib/getopts.tl b/share/txr/stdlib/getopts.tl new file mode 100644 index 00000000..f5909873 --- /dev/null +++ b/share/txr/stdlib/getopts.tl @@ -0,0 +1,320 @@ +;; Copyright 2016 +;; Kaz Kylheku <kaz@kylheku.com> +;; Vancouver, Canada +;; All rights reserved. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following conditions are met: +;; +;; 1. Redistributions of source code must retain the above copyright notice, this +;; list of conditions and the following disclaimer. +;; +;; 2. Redistributions in binary form must reproduce the above copyright notice, +;; this list of conditions and the following disclaimer in the documentation +;; and/or other materials provided with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +;; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +(defex opt-error error) + +(defstruct opt-desc nil + short + long + helptext + arg-p + (type :bool) + (:static valid-types '(:bool :dec :hex :oct :cint :float :str)) + (:postinit (me) + me.(check) + (set me.arg-p (neq me.type :bool)))) + +(defstruct (sys:opt-parsed name arg desc) nil + name + arg ;; string, integer, real, ... + desc ;; opt-desc + (:postinit (me) me.(convert-type))) + +(defstruct opts nil + (opt-hash (hash :equal-based)) ;; string to sys:opt-parsed + in-args + out-args + opt-processor) ;; sys:opt-processor + +(defstruct sys:opt-processor nil + od-list + (od-hash (hash :equal-based)) ;; string to opt-desc + (:postinit (me) + me.(build-hash))) + +(defun sys:opt-err (. args) + (throwf 'opt-error . args)) + +(defun sys:opt-dash (name) + `@(if (> (length name) 1) "-")-@name`) + +(defmeth opt-desc check (me) + (unless (or (functionp me.type) + (fboundp me.type) + (member me.type me.valid-types)) + (error "getopts: type must be a function or valid keyword, not ~s" + me.type)) + (when me.long + (when (< (length me.long) 2) + (error "getopts: long option ~a has a short name" me.long)) + (when (eql [me.long 0] #\-) + (error "getopts: long option ~a starts with - character" me.long))) + (when me.short + (when (neq (length me.short) 1) + (error "getopts: short option ~a not one character long" me.short)) + (when (eql [me.short 0] #\-) + (error "getopts: short option ~a starts with - character" me.short)))) + +(defmeth sys:opt-parsed convert-type (me) + (let ((name (sys:opt-dash me.name))) + (when (and (neq me.desc.type :bool) + (eq me.arg :explicit-no)) + (sys:opt-err "Non-Boolean option ~a explicitly specified as false" name)) + (caseql me.desc.type + (:bool + (set me.arg (neq me.arg :explicit-no))) + (:dec (set me.arg + (or (and (r^$ #/[+\-]?\d+/ me.arg) (int-str me.arg)) + (sys:opt-err "option ~a needs decimal integer arg, not ~a" + name me.arg)))) + (:hex (set me.arg + (or (and (r^$ #/[+\-]?[\da-fA-F]+/ me.arg) (int-str me.arg 16)) + (sys:opt-err "option ~a needs hexadecimal integer arg, not ~a" + name me.arg)))) + (:oct (set me.arg + (or (and (r^$ #/[+\-]?[0-7]+/ me.arg) (int-str me.arg 8)) + (sys:opt-err "option ~a needs octal integer arg, not ~a" + name me.arg)))) + (:cint (set me.arg + (cond + ((r^$ #/[+\-]?0x[\da-fA-F]+/ me.arg) + (int-str (regsub #/0x/ "" me.arg) 16)) + ((r^$ #/[+\-]?0[0-7]+/ me.arg) + (int-str me.arg 8)) + ((r^$ #/[+\-]?0[\da-fA-F]+/ me.arg) + (sys:opt-err "option ~a argument ~a non octal, but leading 0" + name me.arg)) + ((r^$ #/[+\-]?\d+/ me.arg) + (int-str me.arg)) + (t (sys:opt-err "option ~a needs C style numeric arg, not ~a" + name me.arg))))) + (:float (set me.arg + (cond + ([[chand (orf (f^$ #/[+\-]?\d+[.]?([Ee][+\-]?\d+)?/) + (f^$ #/[+\-]?\d*[.]?\d+([Ee][+\-]?\d+)?/)) + flo-str] me.arg]) + (t (sys:opt-err "option ~a needs floating-point arg, not ~a" + name me.arg))))) + (:str (set me.arg + (or (ignerr (read `"@{me.arg}"`)) + (sys:opt-err "option ~a needs string lit syntax, ~a given" + name me.arg)))) + (t (set me.arg (call me.desc.type me.arg)))))) + +(defmeth opts lambda (me key : dfl) + (iflet ((o [me.opt-hash key])) o.arg dfl)) + +(defmeth opts lambda-set (me key val) + (iflet ((o [me.opt-hash key])) + (set o.arg val) + (error "opts: cannot set option ~s to ~s: no such option" key val))) + +(defmeth opts add (me opt) + (whenlet ((n opt.desc.short)) + (set [me.opt-hash n] opt)) + (whenlet ((n opt.desc.long)) + (set [me.opt-hash n] opt))) + +(defmeth sys:opt-processor build-hash (me) + (each ((od me.od-list)) + (each ((str (list od.long od.short))) + (when (and str [me.od-hash str]) + (error "opt-processor: duplicate option ~s" str))) + (unless (or od.long od.short) + (error "opt-processor: no short or long name in option ~s" od)) + (each ((str (list od.long od.short))) + (when str + (set [me.od-hash str] od))))) + +(defmeth sys:opt-processor parse-long (me out opt : arg) + (iflet ((ieq (unless (stringp arg) (break-str opt "=")))) + (let ((oname [opt 0..ieq]) + (arg [opt (succ ieq)..:])) + me.(parse-long out oname arg)) + (let ((od [me.od-hash opt])) + (cond + ((null od) + (sys:opt-err "unrecognized option: --~a" opt)) + ((and arg od.arg-p) + out.(add (new (sys:opt-parsed opt arg od)))) + ((stringp arg) + (sys:opt-err "option --~a doesn't take an argument" opt)) + (od.arg-p + (iflet ((arg (pop out.out-args))) + out.(add (new (sys:opt-parsed opt arg od))) + (sys:opt-err "option --~a requires an argument" opt))) + (out.(add (new (sys:opt-parsed opt arg od)))))))) + +(defmeth sys:opt-processor parse-shorts (me out opts) + (each ((o (split-str opts #//))) + (iflet ((od [me.od-hash o])) + (let ((arg (when od.arg-p + (when (> (length opts) 1) + (sys:opt-err "argument -~a includes -~a, which does not clump" + opts o)) + (unless out.out-args + (sys:opt-err "option -~a requires an argument" o)) + (pop out.out-args)))) + out.(add (new (sys:opt-parsed o arg od)))) + (sys:opt-err "unrecognized option: -~a" o)))) + +(defmeth sys:opt-processor parse-opts (me args) + (let ((out (new opts in-args args out-args args opt-processor me))) + (whilet ((arg (pop out.out-args))) + (cond + ((equal "--" arg) (return)) + ((r^ #/--no-/ arg) me.(parse-long out [arg 5..:] :explicit-no)) + ((r^ #/--/ arg) me.(parse-long out [arg 2..:])) + ((r^ #/-.+/ arg) me.(parse-shorts out [arg 1..:])) + (t (push arg out.out-args) + (return)))) + out)) + +(defun sys:wdwrap (string columns) + (let ((words (tok-str string #/\S+/)) + line) + (build + (whilet ((word (pop words)) + (wpart (cond + ((and word (r^$ #/\w+[\w\-]*\w[.,;:!?"]?/ word)) + (split-str word #/-/)) + (word (list word)))) + (wpart-orig wpart)) + (whilet ((wp0 (eq wpart wpart-orig)) + (wp (pop wpart)) + (w (if wp `@wp@(if wpart "-")`))) + (cond + ((not line) + (set line w)) + ((> (+ (length line) (length w) 1) columns) + (add line) + (set line w)) + (t (set line `@line@(if wp0 " ")@w`))))) + (if line + (add line))))) + +(defun opt (short long : (type :bool) helptext) + (new opt-desc short short long long helptext helptext type type)) + +(defun getopts (opt-desc-list args) + (let ((opr (new sys:opt-processor od-list opt-desc-list))) + opr.(parse-opts args))) + +(defun opthelp (opt-desc-list : (stream *stdout*)) + (let ((sorted [sort (copy-list (remove-if (op null @1.helptext) + opt-desc-list)) : + (do if @1.long @1.long @1.short)]) + (undocumented (keep-if (op null @1.helptext) opt-desc-list))) + (put-line "\nOptions:\n") + (each ((od sorted)) + (let* ((type (if (keywordp od.type) + (upcase-str (tostringp od.type)) + "ARG")) + (long (if od.long + `--@{od.long}@(if od.arg-p `=@type`)`)) + (short (if od.short + `-@{od.short}@(if od.arg-p ` @type`)`)) + (ls (cond + ((and long short) `@{long 21} (@short)`) + (long long) + (short `@{"" 21} @short`))) + (lines (if od.helptext (sys:wdwrap od.helptext 43)))) + (put-line ` @{ls 34}@(pop lines)`) + (while lines + (put-line ` @{"" 34}@(pop lines)`)))) + (put-line) + (when undocumented + (put-line "Undocumented options:\n") + (let* ((undoc-str `@{[mapcar sys:opt-dash + (flatten (mappend (op list @1.short @1.long) + undocumented))] ", "}`)) + (each ((line (sys:wdwrap undoc-str 75))) + (put-line ` @line`))) + (put-line)) + (put-line "Notes:\n") + (let* ((have-short (some sorted (usl short))) + (have-long (some sorted (usl long))) + (have-arg-p (some sorted (usl arg-p))) + (have-bool (some sorted (op eq @1.type :bool))) + (texts (list (if have-short + "Short options can be invoked with long syntax: \ + \ for example, --a can be used when -a exists.\ + \ Short no-argument options can be clumped into\ + \ one argument as exemplified by -xyz. ") + (if have-bool + (if have-arg-p + "Options that take no argument are Boolean:" + (if undocumented + "All documented options are Boolean:" + "All options are Boolean:"))) + (if have-bool + "they are true when present, false when absent.") + (if have-bool + (if have-arg-p + " The --no- prefix can explicitly specify \ + \ Boolean options as false: if a Boolean option\ + \ X exists,\ + \ --no-X specifies it as false. This is useful\ + \ for making false those options which default\ + \ to true." + " The --no- prefix can explicitly specify \ + \ options as false: if an X option exists,\ + \ --no-X specifies it as false. This is useful\ + \ for making false those options which default\ + \ to true.")) + (if (not have-long) + "Note the double dash on --no.") + (if (and have-short have-long) + "The --no- prefix can be applied to a short\ + \ or long option name.") + (if have-arg-p + "The argument to a long option can be given in one\ + \ argument as --option=arg or as a separate\ + \ argument using --option arg.") + "The special argument -- can be used where an option\ + \ may appear. It means \"end of options\": the\ + \ arguments which follow are not treated as options\ + \ even if they look like options."))) + (mapdo (do put-line ` @1`) + (sys:wdwrap `@{(flatten texts)}` 77))) + (put-line) + (whenlet ((types (remq :bool (uniq (mapcar (usl type) sorted))))) + (put-line "Type legend:\n") + (each ((ty types)) + (iflet ((ln (caseql ty + (:dec " DEC - Decimal integer: -123, 0, 5, +73") + (:hex " HEX - Hexadecimal integer -EF, 2D0, +9A") + (:oct " OCT - Octal integer: -773, 5677, +326") + (:cint " CINT - C-style integer: leading 0 octal,\ + \ leading 0x hex, else decimal;\n\ + \ leading sign allowed: -0777, 0xFDC, +123") + (:float " FLOAT - Floating-point: -1.3e+03, +5, 3.3,\ + \ 3., .5, .12e9, 53.e-3, 3e-015") + (:str " STR - String with embedded escapes, valid\ + \ as TXR Lisp string literals\n\ + \ syntax: foo, foo\\tbar, abc\\nxyz")))) + (put-line ln))) + (put-line)))) @@ -41322,6 +41322,392 @@ is restored by the .code unwind-protect cleanup form. +.SS* Command Line Option Processing + +\*(TL provides a support for recognizing, extracting and validating +the POSIX-style options from a list of command-line arguments. + +The supported options can be defined as a list of option descriptor +objects each of which is constructed by a call to the +.code opt +function. Each option can have a long or short name, or both, +a type, and a description. + +The +.code getopts +function takes a list of option descriptors, and a list of arguments, +producing a parse, or else throwing an exception of type +.code opt-error +if an error is detected. The returned object, an instance of struct type +.codn opts , +can then be queried for specific option values, or for the remaining non-option +arguments. + +The +.code opthelp +function takes a list of option descriptors and an output stream, +and generates help text on that stream. A program supporting a +.code --help +option can use this to generate that portion of its help text which +describes the available options, as well as the conventions that they use. + +.NP* Command Line Option Conventions + +A command line option can have a short or long name. A short name is always +one-character long, and treated specially in the command line syntax. Long +options have names two or more characters long. An option can have both a long +and short name. Options may not begin with the +.code - +(ASCII dash) character. A long option may not contain the +.code = +character. + +Short options are invoked by specifying an argument with a single leading +.code - +followed by the option character. Multiple short options which take +no argument can be "clumped": combined into a single argument consisting of +a single +.code - +followed by multiple short option characters. + +An option can take an argument, in which case the argument is required. +An option which takes no argument is Boolean, and a Boolean option +never takes an argument: "takes argument" and "Boolean" are synonymous. + +Long options are invoked as an argument which begins with a +.code -- +(double dash) +immediately followed by the name. When a long option takes an argument, +it is mandatory. It must be specified in the same argument, separated +from the name by the +.code = +character. If that is omitted, then the next command line argument +is taken as the argument. That argument is removed, and not recognized as +an option, even if it looks like one. + +A Boolean long option can be explicitly specified as false using the +.code --no- +prefix rather than the +.code -- +prefix. + +Short options may be invoked using long name syntax; if +.code a +is a short option, then it may be referenced on the command line as +.code --a +and treated as a long option in all other ways, including the use +of +.code --no- +to explicitly specify false for a Boolean option. + +If a short option takes an argument, it may not clump with other +short option. The following command line argument is taken as the +options argument. That argument is removed and is not recognized as +an option even if it looks like one. + +If the command line argument +.code -- +occurs in the command line where an option would otherwise be recognized, +it signifies the end of the options. The subsequent arguments are the +non-option arguments, even if they resemble options. + +.NP* Command Line Processing Example + +The following example illustrates a complete \*(TL program which +parses command line options: + +.cblk + (defvarl options + (list (opt "v" "verbose" :dec + "Verbosity level. Higher values produce more chatter.") + (opt nil "help" :bool + "List this help text.") + (opt "x" nil :hex + "The X factor: a number with a mysterious\e \e + interpretation, affecting the program\e \e + behavior in strange ways.") + (opt "z" nil) ;; undocumented option + (opt nil "cee" :cint + "C style integer.") + (opt "g" "gravity" :float + "Gravitational constant. This gives\e \e + the gravitational field\e \e + strength at the Earth's surface.") + (opt "l" "lit" :str + "A character string given in TXR Lisp notation.") + (opt "c" nil 'upcase-str + "Custom treatment: ARG is converted to upper case.") + (opt "b" "bool" :bool + "A flag you can flip true."))) + + (defvarl prog-name *load-path*) + + (let ((o (getopts options *args*))) + (when [o "help"] + (put-line "Usage:\en") + (put-line ` @{prog-name} [options] arg*`) + (opthelp options) + (exit 0)) + (put-line `args after opts are: @{o.out-args ", "}`)) +.cble + +.coNP Structure @ opt-desc +.synb +.mets (defstruct opt-desc +.mets \ \ short long helptext type +.mets \ \ ... < unspecified << slots ) +.syne +.desc +The +.code opt-desc +structure describes a single command line option. + +The +.code short +and +.code long +slots are either +.code nil +or else hold strings. +The +.code short +slot gives the option's short name: a one-character-long +string which may not be the ASCII dash character +.codn - . +The +.code long +slot gives the option's long name: a string two or more +characters long which doesn't begin with a dash. +An option must have at least one of these names. + +The +.code helptext +slot provides a descriptive string. This string may be long. The +.code opthelp +function displays this text, formatting into multiple lines as necessary. +If +.code helptext +is +.codn nil , +the option is considered undocumented. + +The +.code type +slot may be a symbol naming a global function which takes one argument, +or it may be such a function object. Otherwise it must be one of the +following keyword symbols: +.RS +.coIP :bool +This indicates that the type of the option is Boolean. Such +an option doesn't take any argument. Its value is +.code t +or +.codn nil . +.coIP :dec +This indicates that the option requires an argument, which is a +decimal integer with an optional positive or negative sign. +This argument is converted to an integer object. +.coIP :hex +This type indicates that the option requires an argument consisting +of a hexadecimal integer with an optional positive or negative sign. +This is converted to an integer object. +.coIP :oct +This type indicates that the option requires an argument consisting +of a octal integer with an optional positive or negative sign. +This is converted to an integer object. +.coIP :cint +This type indicates that the option requires an integer argument +whose format conforms to one of three C language conventions in most respects, +other than that this integer may have an arbitrary range. +All forms may carry an optional positive or negative leading sign +at the very beginning. +The first convention consists of decimal digits, which must not have +a superfluous leading zero. The second convention consists of octal +digits which are introduced by an extra leading zero. +The third convention consists of hexadecimal digits introduced by the +.code 0x +prefix. +.coIP :float +This type indicates a decimal floating-point argument, which is converted to +a floating-point number. Its basic form is: an optional leading plus or +minus sign, followed by a sequence of one or more digits which may contain +a single decimal point anywhere, including the very beginning of the +sequence or at the end, optionally followed by the letter +.code e +or +.code E +followed by a decimal integer which may have a leading positive or negative +sign, and include leading zeros. + +.coIP :str +This type indicates that the argument consists of the interior notation of +a TXR Lisp character string. It is processed by adding a double quote +at the beginning or end, and parsed as a string literal. This parsing must +successfully yield a string object, otherwise the argument is ill-formed. +.RE +.PP + +If +.code type +is a function, then the option requires an argument. The argument string +is passed to the function, and the value is whatever the function returns. + +The +.code opt-desc +structure may have additional slots which are not specified. + +The +.code opt +convenience function is provided for constructing +.code opt-desc +objects. + +.coNP Structure @ opts +.synb +.mets (defstruct opts nil +.mets \ \ in-args out-args +.mets \ \ ... < unspecified << slots ) +.syne +.desc +The +.code opts +structure represents a parsed command line, containing decoded +information obtained from the options, and an indication where +the non-option arguments start. + +The +.code opts +structure supports direct indexing for option retrieval. +That is the only documented interface for accessing the parsed +options; the implementation of the information set describing +the parsed options is unspecified. + +The +.code in-args +slot holds the original argument list. + +The +.code out-args +member holds the tail of the argument list consisting of the non-option +arguments. + +The mechanism by means of which +.code out-args +is calculated, and by means of which the information about the +options is populated, is unspecified. The only interface to that +mechanism is the +.code getopts +function. + +The +.code opts +object supports indexing, including indexed assignment. + +If +.code o +is an instance of +.code opts +returned by +.codn getopts , +then the expression +.code "[o \(dqv\(dq]" +tests whether the option +.str v +is available in +.codn o ; +that is, whether it has been specified in the command line. +If so, then its associated value is returned, otherwise +.code nil +is returned. This +.code nil +is ambiguous: for a Boolean option it indicates that either +the option was not specified, or that it was explicitly +specified as false. For a Boolean option that was specified +(positively), the value +.code t +is returned. + +The expression +.code "[o \(dqv\(dq dfl]" +yields the value of option +.str v +if that option has been specified. If the option hasn't +been specified, then the expression yields the value +.codn dfl . + +Assigning to +.code "[o \(dqv\(dq]" +is possible. This replaces the value associated with option +.strn v . +The assignment is erroneous if no such option was parsed +from the command line, even if it is a valid option. + +.coNP Function @ getopts +.synb +.mets (getopts < option-desc-list << arg-list ) +.syne +.desc +The +.code getopts +function takes a list of +.code opt-desc +structures and a list of strings +.meta arg-list +representing command line arguments. + +The +.meta arg-list +is parsed. If the parse is unsuccessful, an exception of type +.code opt-error +is thrown, derived from +.codn error . + +If there are problems in +.code option-desc-list +itself, then an exception of type +.code error +is thrown. + +If the parse is successful, +.code getopts +returns an instance of the +.code opts +structure describing the parsed opts, and listing the non-option +arguments. + +.coNP Function @ opthelp +.synb +.mets (opthelp < opt-desc-list <> [ stream ]) +.syne +.desc +The +.code opthelp +function processes the list of +.code opt-desc +structures +.meta opt-desc-list +and compiles a customized body of help text describing all of the +options, as well as general description of the command line option +conventions to guide the user in in the correct use of command +line options. + +The text is formatted to fit within 79 columns, and begins and ends with a +blank line. Its format consists of headings which begin in the first column, +and paragraphs and tables which feature a two space left margin. +A blank line follows each section heading. The heading begins with a capital +letter. Its remaining words are uncapitalized, and it ends with a colon. + +The text is sent to +.metn stream , +if specified. This argument defaults to +.codn *stdout* . + +If there are problems in +.code option-desc-list +itself, then an exception of type +.code error +is thrown. + .SS* System Programming .coNP Accessor @ errno .synb |