Next: , Previous: , Up: Textual Conversion Packages   [Contents][Index]

4.4 Program and Arguments


4.4.1 Getopt

(require 'getopt)

This routine implements Posix command line argument parsing. Notice that returning values through global variables means that getopt is not reentrant.

Obedience to Posix format for the getopt calls sows confusion. Passing argc and argv as arguments while referencing optind as a global variable leads to strange behavior, especially when the calls to getopt are buried in other procedures.

Even in C, argc can be derived from argv; what purpose does it serve beyond providing an opportunity for argv/argc mismatch? Just such a mismatch existed for years in a SLIB getopt-- example.

I have removed the argc and argv arguments to getopt procedures; and replaced them with a global variable:

Variable: *argv*

Define *argv* with a list of arguments before calling getopt procedures. If you don’t want the first (0th) element to be ignored, set *optind* to 0 (after requiring getopt).

Variable: *optind*

Is the index of the current element of the command line. It is initially one. In order to parse a new command line or reparse an old one, *optind* must be reset.

Variable: *optarg*

Is set by getopt to the (string) option-argument of the current option.

Function: getopt optstring

Returns the next option letter in *argv* (starting from (vector-ref argv *optind*)) that matches a letter in optstring. *argv* is a vector or list of strings, the 0th of which getopt usually ignores. optstring is a string of recognized option characters; if a character is followed by a colon, the option takes an argument which may be immediately following it in the string or in the next element of *argv*.

*optind* is the index of the next element of the *argv* vector to be processed. It is initialized to 1 by getopt.scm, and getopt updates it when it finishes with each element of *argv*.

getopt returns the next option character from *argv* that matches a character in optstring, if there is one that matches. If the option takes an argument, getopt sets the variable *optarg* to the option-argument as follows:

  • If the option was the last character in the string pointed to by an element of *argv*, then *optarg* contains the next element of *argv*, and *optind* is incremented by 2. If the resulting value of *optind* is greater than or equal to (length *argv*), this indicates a missing option argument, and getopt returns an error indication.
  • Otherwise, *optarg* is set to the string following the option character in that element of *argv*, and *optind* is incremented by 1.

If, when getopt is called, the string (vector-ref argv *optind*) either does not begin with the character #\- or is just "-", getopt returns #f without changing *optind*. If (vector-ref argv *optind*) is the string "--", getopt returns #f after incrementing *optind*.

If getopt encounters an option character that is not contained in optstring, it returns the question-mark #\? character. If it detects a missing option argument, it returns the colon character #\: if the first character of optstring was a colon, or a question-mark character otherwise. In either case, getopt sets the variable getopt:opt to the option character that caused the error.

The special option "--" can be used to delimit the end of the options; #f is returned, and "--" is skipped.

RETURN VALUE

getopt returns the next option character specified on the command line. A colon #\: is returned if getopt detects a missing argument and the first character of optstring was a colon #\:.

A question-mark #\? is returned if getopt encounters an option character not in optstring or detects a missing argument and the first character of optstring was not a colon #\:.

Otherwise, getopt returns #f when all command line options have been parsed.

Example:

#! /usr/local/bin/scm
(require 'program-arguments)
(require 'getopt)
(define argv (program-arguments))

(define opts ":a:b:cd")
(let loop ((opt (getopt (length argv) argv opts)))
  (case opt
    ((#\a) (print "option a: " *optarg*))
    ((#\b) (print "option b: " *optarg*))
    ((#\c) (print "option c"))
    ((#\d) (print "option d"))
    ((#\?) (print "error" getopt:opt))
    ((#\:) (print "missing arg" getopt:opt))
    ((#f) (if (< *optind* (length argv))
              (print "argv[" *optind* "]="
                     (list-ref argv *optind*)))
          (set! *optind* (+ *optind* 1))))
  (if (< *optind* (length argv))
      (loop (getopt (length argv) argv opts))))

(slib:exit)

4.4.2 Getopt—

Function: getopt-- optstring

The procedure getopt-- is an extended version of getopt which parses long option names of the form ‘--hold-the-onions’ and ‘--verbosity-level=extreme’. Getopt-- behaves as getopt except for non-empty options beginning with ‘--’.

Options beginning with ‘--’ are returned as strings rather than characters. If a value is assigned (using ‘=’) to a long option, *optarg* is set to the value. The ‘=’ and value are not returned as part of the option string.

No information is passed to getopt-- concerning which long options should be accepted or whether such options can take arguments. If a long option did not have an argument, *optarg* will be set to #f. The caller is responsible for detecting and reporting errors.

(define opts ":-:b:")
(define *argv* '("foo" "-b9" "--f1" "--2=" "--g3=35234.342" "--"))
(define *optind* 1)
(define *optarg* #f)
(require 'qp)
(do ((i 5 (+ -1 i)))
    ((zero? i))
  (let ((opt (getopt-- opts)))
    (print *optind* opt *optarg*)))
-|
2 #\b "9"
3 "f1" #f
4 "2" ""
5 "g3" "35234.342"
5 #f "35234.342"

4.4.3 Command Line

(require 'read-command)

Function: read-command port
Function: read-command

read-command converts a command line into a list of strings suitable for parsing by getopt. The syntax of command lines supported resembles that of popular shells. read-command updates port to point to the first character past the command delimiter.

If an end of file is encountered in the input before any characters are found that can begin an object or comment, then an end of file object is returned.

The port argument may be omitted, in which case it defaults to the value returned by current-input-port.

The fields into which the command line is split are delimited by whitespace as defined by char-whitespace?. The end of a command is delimited by end-of-file or unescaped semicolon (;) or newline. Any character can be literally included in a field by escaping it with a backslach (\).

The initial character and types of fields recognized are:

\

The next character has is taken literally and not interpreted as a field delimiter. If \ is the last character before a newline, that newline is just ignored. Processing continues from the characters after the newline as though the backslash and newline were not there.

"

The characters up to the next unescaped " are taken literally, according to [R4RS] rules for literal strings (see Strings in Revised(4) Scheme).

(’, ‘%'

One scheme expression is read starting with this character. The read expression is evaluated, converted to a string (using display), and replaces the expression in the returned field.

;

Semicolon delimits a command. Using semicolons more than one command can appear on a line. Escaped semicolons and semicolons inside strings do not delimit commands.

The comment field differs from the previous fields in that it must be the first character of a command or appear after whitespace in order to be recognized. # can be part of fields if these conditions are not met. For instance, ab#c is just the field ab#c.

#

Introduces a comment. The comment continues to the end of the line on which the semicolon appears. Comments are treated as whitespace by read-dommand-line and backslashes before newlines in comments are also ignored.

Function: read-options-file filename

read-options-file converts an options file into a list of strings suitable for parsing by getopt. The syntax of options files is the same as the syntax for command lines, except that newlines do not terminate reading (only ; or end of file).

If an end of file is encountered before any characters are found that can begin an object or comment, then an end of file object is returned.


4.4.4 Parameter lists

(require 'parameters)

Arguments to procedures in scheme are distinguished from each other by their position in the procedure call. This can be confusing when a procedure takes many arguments, many of which are not often used.

A parameter-list is a way of passing named information to a procedure. Procedures are also defined to set unused parameters to default values, check parameters, and combine parameter lists.

A parameter has the form (parameter-name value1 …). This format allows for more than one value per parameter-name.

A parameter-list is a list of parameters, each with a different parameter-name.

Function: make-parameter-list parameter-names

Returns an empty parameter-list with slots for parameter-names.

Function: parameter-list-ref parameter-list parameter-name

parameter-name must name a valid slot of parameter-list. parameter-list-ref returns the value of parameter parameter-name of parameter-list.

Function: remove-parameter parameter-name parameter-list

Removes the parameter parameter-name from parameter-list. remove-parameter does not alter the argument parameter-list.

If there are more than one parameter-name parameters, an error is signaled.

Procedure: adjoin-parameters! parameter-list parameter1 …

Returns parameter-list with parameter1 … merged in.

Procedure: parameter-list-expand expanders parameter-list

expanders is a list of procedures whose order matches the order of the parameter-names in the call to make-parameter-list which created parameter-list. For each non-false element of expanders that procedure is mapped over the corresponding parameter value and the returned parameter lists are merged into parameter-list.

This process is repeated until parameter-list stops growing. The value returned from parameter-list-expand is unspecified.

Function: fill-empty-parameters defaulters parameter-list

defaulters is a list of procedures whose order matches the order of the parameter-names in the call to make-parameter-list which created parameter-list. fill-empty-parameters returns a new parameter-list with each empty parameter replaced with the list returned by calling the corresponding defaulter with parameter-list as its argument.

Function: check-parameters checks parameter-list

checks is a list of procedures whose order matches the order of the parameter-names in the call to make-parameter-list which created parameter-list.

check-parameters returns parameter-list if each check of the corresponding parameter-list returns non-false. If some check returns #f a warning is signaled.

In the following procedures arities is a list of symbols. The elements of arities can be:

single

Requires a single parameter.

optional

A single parameter or no parameter is acceptable.

boolean

A single boolean parameter or zero parameters is acceptable.

nary

Any number of parameters are acceptable.

nary1

One or more of parameters are acceptable.

Function: parameter-list->arglist positions arities parameter-list

Returns parameter-list converted to an argument list. Parameters of arity type single and boolean are converted to the single value associated with them. The other arity types are converted to lists of the value(s).

positions is a list of positive integers whose order matches the order of the parameter-names in the call to make-parameter-list which created parameter-list. The integers specify in which argument position the corresponding parameter should appear.


4.4.5 Getopt Parameter lists

(require 'getopt-parameters)

Function: getopt->parameter-list optnames arities types aliases desc …

Returns *argv* converted to a parameter-list. optnames are the parameter-names. arities and types are lists of symbols corresponding to optnames.

aliases is a list of lists of strings or integers paired with elements of optnames. Each one-character string will be treated as a single ‘-’ option by getopt. Longer strings will be treated as long-named options (see getopt–).

If the aliases association list has only strings as its cars, then all the option-arguments after an option (and before the next option) are adjoined to that option.

If the aliases association list has integers, then each (string) option will take at most one option-argument. Unoptioned arguments are collected in a list. A ‘-1’ alias will take the last argument in this list; ‘+1’ will take the first argument in the list. The aliases -2 then +2; -3 then +3; … are tried so long as a positive or negative consecutive alias is found and arguments remain in the list. Finally a ‘0’ alias, if found, absorbs any remaining arguments.

In all cases, if unclaimed arguments remain after processing, a warning is signaled and #f is returned.

Function: getopt->arglist optnames positions arities types defaulters checks aliases desc …

Like getopt->parameter-list, but converts *argv* to an argument-list as specified by optnames, positions, arities, types, defaulters, checks, and aliases. If the options supplied violate the arities or checks constraints, then a warning is signaled and #f is returned.

These getopt functions can be used with SLIB relational databases. For an example, See make-command-server.

If errors are encountered while processing options, directions for using the options (and argument strings desc …) are printed to current-error-port.

(begin
  (set! *optind* 1)
  (set! *argv* '("cmd" "-?")
  (getopt->parameter-list
   '(flag number symbols symbols string flag2 flag3 num2 num3)
   '(boolean optional nary1 nary single boolean boolean nary nary)
   '(boolean integer symbol symbol string boolean boolean integer integer)
   '(("flag" flag)
     ("f" flag)
     ("Flag" flag2)
     ("B" flag3)
     ("optional" number)
     ("o" number)
     ("nary1" symbols)
     ("N" symbols)
     ("nary" symbols)
     ("n" symbols)
     ("single" string)
     ("s" string)
     ("a" num2)
     ("Abs" num3))))
-|
Usage: cmd [OPTION ARGUMENT ...] ...

  -f, --flag
  -o, --optional=<number>
  -n, --nary=<symbols> ...
  -N, --nary1=<symbols> ...
  -s, --single=<string>
      --Flag
  -B
  -a        <num2> ...
      --Abs=<num3> ...

ERROR: getopt->parameter-list "unrecognized option" "-?"

4.4.6 Filenames

(require 'filename)

Function: filename:match?? pattern
Function: filename:match-ci?? pattern

Returns a predicate which returns a non-false value if its string argument matches (the string) pattern, false otherwise. Filename matching is like glob expansion described the bash manpage, except that names beginning with ‘.’ are matched and ‘/’ characters are not treated specially.

These functions interpret the following characters specially in pattern strings:

*

Matches any string, including the null string.

?

Matches any single character.

[…]

Matches any one of the enclosed characters. A pair of characters separated by a minus sign (-) denotes a range; any character lexically between those two characters, inclusive, is matched. If the first character following the ‘[’ is a ‘!’ or a ‘^’ then any character not enclosed is matched. A ‘-’ or ‘]’ may be matched by including it as the first or last character in the set.

Function: filename:substitute?? pattern template
Function: filename:substitute-ci?? pattern template

Returns a function transforming a single string argument according to glob patterns pattern and template. pattern and template must have the same number of wildcard specifications, which need not be identical. pattern and template may have a different number of literal sections. If an argument to the function matches pattern in the sense of filename:match?? then it returns a copy of template in which each wildcard specification is replaced by the part of the argument matched by the corresponding wildcard specification in pattern. A * wildcard matches the longest leftmost string possible. If the argument does not match pattern then false is returned.

template may be a function accepting the same number of string arguments as there are wildcard specifications in pattern. In the case of a match the result of applying template to a list of the substrings matched by wildcard specifications will be returned, otherwise template will not be called and #f will be returned.

((filename:substitute?? "scm_[0-9]*.html" "scm5c4_??.htm")
 "scm_10.html")
⇒ "scm5c4_10.htm"
((filename:substitute?? "??" "beg?mid?end") "AZ")
⇒ "begAmidZend"
((filename:substitute?? "*na*" "?NA?") "banana")
⇒ "banaNA"
((filename:substitute?? "?*?" (lambda (s1 s2 s3) (string-append s3 s1)))
 "ABZ")
⇒ "ZA"
Function: replace-suffix str old new

str can be a string or a list of strings. Returns a new string (or strings) similar to str but with the suffix string old removed and the suffix string new appended. If the end of str does not match old, an error is signaled.

(replace-suffix "/usr/local/lib/slib/batch.scm" ".scm" ".c")
⇒ "/usr/local/lib/slib/batch.c"
Function: call-with-tmpnam proc k
Function: call-with-tmpnam proc

Calls proc with k arguments, strings returned by successive calls to tmpnam. If proc returns, then any files named by the arguments to proc are deleted automatically and the value(s) yielded by the proc is(are) returned. k may be ommited, in which case it defaults to 1.

Function: call-with-tmpnam proc suffix1 …

Calls proc with strings returned by successive calls to tmpnam, each with the corresponding suffix string appended. If proc returns, then any files named by the arguments to proc are deleted automatically and the value(s) yielded by the proc is(are) returned.


4.4.7 Batch

(require 'batch)

The batch procedures provide a way to write and execute portable scripts for a variety of operating systems. Each batch: procedure takes as its first argument a parameter-list (see Parameter lists). This parameter-list argument parms contains named associations. Batch currently uses 2 of these:

batch-port

The port on which to write lines of the batch file.

batch-dialect

The syntax of batch file to generate. Currently supported are:

  • unix
  • dos
  • vms
  • amigaos
  • system
  • *unknown*

The ‘batch’ module uses 2 enhanced relational tables (see Using Databases) to store information linking the names of operating-systems to batch-dialectes.

Function: batch:initialize! database

Defines operating-system and batch-dialect tables and adds the domain operating-system to the enhanced relational database database.

Variable: *operating-system*

Is batch’s best guess as to which operating-system it is running under. *operating-system* is set to (software-type) (see Configuration) unless (software-type) is unix, in which case finer distinctions are made.

Function: batch:call-with-output-script parms file proc

proc should be a procedure of one argument. If file is an output-port, batch:call-with-output-script writes an appropriate header to file and then calls proc with file as the only argument. If file is a string, batch:call-with-output-script opens a output-file of name file, writes an appropriate header to file, and then calls proc with the newly opened port as the only argument. Otherwise, batch:call-with-output-script acts as if it was called with the result of (current-output-port) as its third argument.

The rest of the batch: procedures write (or execute if batch-dialect is system) commands to the batch port which has been added to parms or (copy-tree parms) by the code:

(adjoin-parameters! parms (list 'batch-port port))
Function: batch:command parms string1 string2 …

Calls batch:try-command (below) with arguments, but signals an error if batch:try-command returns #f.

These functions return a non-false value if the command was successfully translated into the batch dialect and #f if not. In the case of the system dialect, the value is non-false if the operation suceeded.

Function: batch:try-command parms string1 string2 …

Writes a command to the batch-port in parms which executes the program named string1 with arguments string2 ….

Function: batch:try-chopped-command parms arg1 arg2 … list

breaks the last argument list into chunks small enough so that the command:

arg1 arg2chunk

fits withing the platform’s maximum command-line length.

batch:try-chopped-command calls batch:try-command with the command and returns non-false only if the commands all fit and batch:try-command of each command line returned non-false.

Function: batch:run-script parms string1 string2 …

Writes a command to the batch-port in parms which executes the batch script named string1 with arguments string2 ….

Note: batch:run-script and batch:try-command are not the same for some operating systems (VMS).

Function: batch:comment parms line1 …

Writes comment lines line1 … to the batch-port in parms.

Function: batch:lines->file parms file line1 …

Writes commands to the batch-port in parms which create a file named file with contents line1 ….

Function: batch:delete-file parms file

Writes a command to the batch-port in parms which deletes the file named file.

Function: batch:rename-file parms old-name new-name

Writes a command to the batch-port in parms which renames the file old-name to new-name.

In addition, batch provides some small utilities very useful for writing scripts:

Function: truncate-up-to path char
Function: truncate-up-to path string
Function: truncate-up-to path charlist

path can be a string or a list of strings. Returns path sans any prefixes ending with a character of the second argument. This can be used to derive a filename moved locally from elsewhere.

(truncate-up-to "/usr/local/lib/slib/batch.scm" "/")
⇒ "batch.scm"
Function: string-join joiner string1 …

Returns a new string consisting of all the strings string1 … in order appended together with the string joiner between each adjacent pair.

Function: must-be-first list1 list2

Returns a new list consisting of the elements of list2 ordered so that if some elements of list1 are equal? to elements of list2, then those elements will appear first and in the order of list1.

Function: must-be-last list1 list2

Returns a new list consisting of the elements of list1 ordered so that if some elements of list2 are equal? to elements of list1, then those elements will appear last and in the order of list2.

Function: os->batch-dialect osname

Returns its best guess for the batch-dialect to be used for the operating-system named osname. os->batch-dialect uses the tables added to database by batch:initialize!.

Here is an example of the use of most of batch’s procedures:

(require 'databases)
(require 'parameters)
(require 'batch)
(require 'filename)

(define batch (create-database #f 'alist-table))
(batch:initialize! batch)

(define my-parameters
  (list (list 'batch-dialect (os->batch-dialect *operating-system*))
        (list 'operating-system *operating-system*)
        (list 'batch-port (current-output-port)))) ;gets filled in later

(batch:call-with-output-script
 my-parameters
 "my-batch"
 (lambda (batch-port)
   (adjoin-parameters! my-parameters (list 'batch-port batch-port))
   (and
    (batch:comment my-parameters
                   "================ Write file with C program.")
    (batch:rename-file my-parameters "hello.c" "hello.c~")
    (batch:lines->file my-parameters "hello.c"
                       "#include <stdio.h>"
                       "int main(int argc, char **argv)"
                       "{"
                       "  printf(\"hello world\\n\");"
                       "  return 0;"
                       "}" )
    (batch:command my-parameters "cc" "-c" "hello.c")
    (batch:command my-parameters "cc" "-o" "hello"
                  (replace-suffix "hello.c" ".c" ".o"))
    (batch:command my-parameters "hello")
    (batch:delete-file my-parameters "hello")
    (batch:delete-file my-parameters "hello.c")
    (batch:delete-file my-parameters "hello.o")
    (batch:delete-file my-parameters "my-batch")
    )))

Produces the file my-batch:

#! /bin/sh
# "my-batch" script created by SLIB/batch Sun Oct 31 18:24:10 1999
# ================ Write file with C program.
mv -f hello.c hello.c~
rm -f hello.c
echo '#include <stdio.h>'>>hello.c
echo 'int main(int argc, char **argv)'>>hello.c
echo '{'>>hello.c
echo '  printf("hello world\n");'>>hello.c
echo '  return 0;'>>hello.c
echo '}'>>hello.c
cc -c hello.c
cc -o hello hello.o
hello
rm -f hello
rm -f hello.c
rm -f hello.o
rm -f my-batch

When run, my-batch prints:

bash$ my-batch
mv: hello.c: No such file or directory
hello world

Next: , Previous: , Up: Textual Conversion Packages   [Contents][Index]