############################################################################
#
# File: options.icn
#
# Subject: Procedure to get command-line options
#
# Authors: Robert J. Alexander and Gregg M. Townsend
#
# Date: May 5, 2000
# Nov 20 2019 Don Ward Add --options-with-dashes
#
############################################################################
#
# This file is in the public domain.
#
############################################################################
#
# options(arg, optstring,errproc) removes command options from the
# argument list of an Icon main procedure, returning a table of
# option values.
#
############################################################################
#
# options(arg,optstring,errproc) -- Get command line options.
#
# This procedure separates and interprets command options included in
# the main program argument list. Option names and values are removed
# from the argument list and returned in a table.
#
# -----------------------------------------------------------------------
# [Pre Nov 2019 version description: see additional comments at the end]
#
# On the command line, options are introduced by a "-" character. An
# option name is either a single printable character, as in "-n" or "-?",
# or a string of letters, numbers, and underscores, as in "-geometry".
# Valueless single-character options may appear in combination, for
# example as "-qtv".
#
# Some options require values. Generally, the option name is one
# argument and the value appears as the next argument, for example
# "-F file.txt". However, with a single-character argument name
# (as in that example), the value may be concatenated: "-Ffile.txt"
# is accepted as equivalent.
#
# Options may be freely interspersed with non-option arguments.
# An argument of "-" is treated as a non-option. The special argument
# "--" terminates option processing. Non-option arguments are returned
# in the original argument list for interpretation by the caller.
#
# An argument of the form @filename (a "@" immediately followed
# by a file name) causes options() to replace that argument with
# arguments retrieved from the file "filename". Each line of the file
# is taken as a separate argument, exactly as it appears in the file.
# Arguments beginning with - are processed as options, and those
# starting with @ are processed as nested argument files. An argument
# of "--" causes all remaining arguments IN THAT FILE ONLY to be
# treated as non-options (including @filename arguments).
#
# The parameters of options(arg,optstring,errproc) are:
#
# arg the argument list as passed to the main procedure.
#
# optstring a string specifying the allowable options. This is
# a concatenation, with optional spaces between, of
# one or more option specs of the form
# -name%
# where
# - introduces the option
# name is either a string of alphanumerics
# (any of a-z, A-Z, 0-9, and _)
# or any single printable character
# % is one of the following flag characters:
# ! No value is required or allowed
# : A string value is required
# + An integer value is required
# . A real value is required
#
# The leading "-" may be omitted for a single-character
# option. The "!" flag may be omitted except when
# needed to terminate a multi-character name.
# Thus, the following optstrings are equivalent:
# "-n+ -t -v -q -F: -geometry: -silent"
# "n+tvqF:-geometry:-silent"
# "-silent!n+tvqF:-geometry:"
#
# If "optstring" is omitted any single letter is
# assumed to be valid and require no data.
#
# errproc a procedure which will be called if an error is
# is detected in the command line options. The
# procedure is called with one argument: a string
# describing the error that occurred. After errproc()
# is called, options() immediately returns the outcome
# of errproc(), without processing further arguments.
# Already processed arguments will have been removed
# from "arg". If "errproc" is omitted, stop() is
# called if an error is detected.
#
# A table is returned containing the options that were specified.
# The keys are the specified option names. The assigned values are the
# data values following the options converted to the specified type.
# A value of 1 is stored for options that accept no values.
# The table's default value is &null.
#
#
# Upon return, the option arguments are removed from arg, leaving
# only the non-option arguments.
#
# ---------------------------------------------------------------------------
# [Nov 2019 enhancements]
#
# Options may begin with "--" and, if they do, may contain "-" characters.
# So options like --dry-run will be parsed correctly and will result in an
# entry in the options table with a key of "-dry-run" (note the leading
# minus in the key).
#
# To remove the leading minus from such keys, make the first character
# of the option string an equals character: then, a supplied argument of
# either --opt or -opt would result in a key of "opt" in the table. When the
# first character is an equals character, the procedure ensures that only
# one of "--opt" or "-opt" may be specified in the option string. The only
# way to allow (for example) both --run and -run as options is to miss out
# the leading equals and to treat option["-run"] and option["run"]
# separately in the calling program.
# e.g.
# "-bish-bash-bosh--bash" is ok.
# "=-bish-bash-bosh--bash" results in a "duplicate option --bash" error.
#
# "=-bish-bash-bosh--bish-bash-bosh" is also ok because, in this case,
# the options are
# -bish -bash -bosh and --bish-bash-bosh
# Because the -- options can swallow succeeding minus characters, it's
# probably better to use spaces to separate arguments, even when not
# needed. In the preceding example, it's much clearer to write
# "= -bish -bash -bosh --bish-bash-bosh"
#
# This version will also accept parameters with an equal sign in them. e.g.
# instead of
# progname -time 45
# you can write
# progname -time=45
# A similar extension is available in the @ file. You can now write
# -time 45
# or
# -time = 45
# instead of two lines
# -time
# 45
# as previously.
#
############################################################################
procedure options(arg,optstring,errproc)
local f,fList,fileArg,fn,ignore,optname,opttable,opttype,p,x,option,optcs
local minusoptcs, stripm
#
# Initialize.
#
/optstring := string(&letters++"_")
/errproc := stop
option := table()
fList := []
opttable := table()
optcs := &letters ++ &digits ++ '_'
minusoptcs := optcs ++ '-'
#
# Scan the option specification string.
#
optstring ? {
stripm := ="="
until pos(0) do {
tab(many(' '))
if ="--" then {
if any(optcs) then {
optname := ( "-" || tab(many(minusoptcs)) | break)
if \stripm & \opttable[optname[2:0]] then {
return errproc("duplicate option -" || optname)
}
}
} else if ="-" then {
if any(optcs) then {
optname := (tab(many(optcs)) | break)
if \stripm & \opttable["-" || optname] then {
return errproc("duplicate option -" || optname)
}
} else { optname := move(1) | break }
} else { optname := move(1) | break }
tab(many(' '))
opttype := tab(any('!:+.')) | "!"
opttable[optname] := opttype
}
}
#
# Iterate over program invocation argument words.
#
while x := get(arg) do {
if /x then ignore := &null # if end of args from file, stop ignoring
else x ? {
if pos(-2) & ="--"
then { ignore := 1 } # ignore following args if --
else if ="-" & not pos(0) & /ignore then {
tab(0) ? until pos(0) do {
if opttype := \opttable[optname := ((pos(1),(tab(upto(' \t=')|0) )) | move(1))]
then {
option[optname] :=
if any(':+.',opttype) then {
tab(many(' \t')); tab(any('=')); tab(many(' \t'));
p := "" ~== tab(0) | get(arg) |
return errproc("No parameter following -" || optname)
case opttype of {
":": p
"+": integer(p) |
return errproc("-" || optname ||
" needs numeric parameter")
".": real(p) |
return errproc("-" || optname ||
" needs numeric parameter")
}
}
else 1
}
else return errproc("Unrecognized option: -" || optname)
}
} else if ="@" & not pos(0) & /ignore then {
#
# The argument begins with the character "@", fetch option
# words from lines of a text file.
#
f := open(fn := tab(0)) | return errproc("Can't open " || fn)
fileArg := []
while put(fileArg,read(f))
close(f)
push(arg) # push null to signal end of args from file
while push(arg,pull(fileArg))
} else put(fList,x)
}
}
while push(arg,pull(fList)) # restore unprocessed arguments
if \stripm then { # replace option["-opt"] with option["opt"]
every x := key(option) do {
if "-" == x[1] then {
option[x[2:0]] := option[x]
push(fList,x) # reuse fList for options to be removed
}
}
every delete(option, !fList)
}
return option
end
This page produced by UniDoc on 2021/04/15 @ 23:59:43.