Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
New options & arguments processing system for ZSH
- X-seq: zsh-users 4131
- From: martin.ebourne@xxxxxxxxxxxx
- To: zsh-users@xxxxxxxxxx
- Subject: New options & arguments processing system for ZSH
- Date: Fri, 17 Aug 2001 14:59:46 +0100
- Expiry-date: Thu, 15 Nov 2001 -1:-1:-1 +0000
- Mailing-list: contact zsh-users-help@xxxxxxxxxx; run by ezmlm
Hi,
I've been working on a fully featured system for processing options &
arguments in Zsh scripts/functions for a while now, and I think it's ready
for other people to try out (mostly because I've just finished the
documentation!).
The main features are ease of use, processing and validation of both
options and normal command arguments, and fully automatic completion when
using the new Zsh completion system!
For now I'd like any comments people have, especially bug reports and
praise (yeah right!).
It should work on any version of Zsh 4, and probably recent 3's as well. An
example called 'yes' is also included for your perusal.
All you need to do is put the enclosed parse_opts and _parse_opts files
(and yes for the example) on your fpath and
autoload -U parse_opts _parse_opts yes
compdef -n _parse_opts yes
If other people find it useful then then I'd like this to be added to the
Zsh distribution sometime, though not until it's had a decent amount of
external testing. Send me those bug reports!
Documentation is all included at the top of the parse_opts function. 'bugs'
in that also of interest.
I've sent this to zsh-users because it may be useful to anyone. I don't
expect to post it here in future though. (zsh-workers probably.)
Cheers,
Martin.
PS. If anyone wants the equivalent Perl module version then let me know.
----------BEGIN parse_opts
# ZSH function file
# Provide options parsing and help output
# (C) 2001 Martin Ebourne
#
# $Id: parse_opts,v 1.24 2001/08/17 13:32:02 mebourne Exp $
### Documentation
## Introduction
#
# This module provides a complete option and argument parsing system,
# configured entirely from a help string supplied by the user. The help string
# is free-format text and describes options/arguments in the usual way; into
# this is then embedded special descriptions which this module decodes to
# allow it to process options and arguments on the command line.
#
# Some of the main features of this system are:
#
# - Easy to use, and configure
# - Fully supports both options and normal command line arguments
# - Option values and arguments may be type checked
# - There is an extensible user defined type system, based on inheritence
# - A range of default types is provided
# - Completion for Zsh is handled fully automatically
# - Automatic error reporting and handling of help displaying
# - Both standard Unix single character options (which may be grouped,
# eg. `-a -l' or `-al'), and GNU style long options (eg. --long-list) are
# supported
# - Long options with values are supported with two different formats for the
# user's convenience. eg. both `--directory=<dir>' and `--directory <dir>'
# are valid
# - There is a compatible equivalent module for Perl
#
# Please see the example section at the end of this help text to see what the
# system is like in use.
## Use from a shell script
#
# In order to use parse_opts from a shell script, a construct such as the
# following will generally be used:
#
# local -A opts
# parse_opts - opts -- "$argv[@]" <<'EOF' || return 1
# <help text goes here>
# EOF
#
# Here the first argument tells parse_opts that the help text will be passed
# in as stdin (done as a `here' doc in this case), the second argument gives
# the name of the associative array which the results should be stored in, and
# the command line arguments are all provided after the `--'.
#
# In the case that parse_opts finishes successfully, the decoded options are
# in the opts array, otherwise the script will exit. Note that in the case of
# a non-0 return code it may not necessarily be an error - maybe the user has
# requested help. In either case an error message or whatever will already
# have been printed so nothing further needs to be done.
## Syntax
#
# parse_opts [<config> [<results> [<argv>]]] [--] [<arguments> ...]
#
# config - This is the help text used to configure the system. If missing or
# `-' then this will instead be read from stdin to parse_opts (which
# is not normally stdin to the caller)
# results - If provided then this is the name of the default associative array
# to store the results in, though it may be overridden later on a per
# parameter basis. On an unsuccessful exit, this will be untouched
# argv - This is the name of the array to find the command line arguments in.
# On a successful exit the array will have been emptied due to all
# the arguments being used. On an successful exit it will be
# untouched
# `--' - This is required if the arguments are to be given directly
# arguments - As an alternative to providing the argv parameter, the arguments
# may be supplied directly on the command line, after the `--'
#
# The return code is 0 if the arguments have been successfully decoded and the
# script should proceed as normal, or non-0 for all other circumstances. In
# the latter case the script should exit straight away.
#
# NB. When specifying the results and argv parameters, these are done by name,
# and hence should not include the `$'. Note also that argv cannot actually be
# `argv' since that will clash with the one inside parse_opts.
## Configuration help text
#
# The idea behind the configuration help text is that you write the free-form
# help exactly as you wish to see it, and then add in extra lines with
# embedded specifications in them. These lines are filtered out when the help
# is displayed so you end up with what you wanted. Meanwhile, in addition to
# processing the embedded specifications, parse_opts also decodes the help
# itself to extract descriptions for each of the options. These are used when
# automatically generating the Zsh completion.
#
# Just to get the idea, a very simple help text could look like this:
#
# Description:
# View the latest of a set of files, given part of the filename
#
# Usage:
# latest [options] <filename-stem>
#
# Options:
# -h, --help Provide this help
# # --help | -h
#
# Arguments:
# <filename-stem> Start of filename of set of files to view.
# May contain wildcards
# # filename-stem : ? file
#
# For a slightly more elaborate example, see the `Example' section below.
#
# The basic rules for the help text are as follows:
#
# 1. All embedded specifications and commands are introduced with a `#' as the
# first thing on the line. No non-whitespace characters may proceed it,
# else the `#' will be treated as a normal character.
#
# 2. All option/argument syntax descriptions (ie. the bit for the user to
# see), or other titles/general description, should generally not have, or
# be completely before, any tabs on the line. This does not matter in the
# case where the text preceeds another option, but otherwise it may end up
# becoming part of an option decription as sent to the completion system.
#
# 3. All description text relating to an option/argument must have at least
# one tab preceeding it.
#
# 4. Comments (ie. free text not included in the help output) are introduced
# as for the embedded specifications, but with `##' instead.
#
# 5. The presence or otherwise of whitespace withing an embedded description
# line (as described below) is generally crucial. The amount will however
# not matter.
#
# 6. Long embedded specifications may be continued on another line by escaping
# the newline with a `\'
## Embedded specification syntax
#
# The currently recognised types of specification line are introduced as
# follows:
#
# `#' - These are option/argument lines. There are minor differences
# between option and argument lines, but they are otherwise the
# same
# `#type' - These are type definition lines
# `##' - As already mentioned, this is a comment line
## Option/argument definition lines
#
# The option/argument description lines have four parts, of which only one is
# mandatory.
#
# `# ' [ <tag-part> ( ` = ' | ` += ' ) ] <option-part>
# [ ` : ' <type-part> ] [ ` * ' <settings-part> ]
#
# Or as an abbreviated form:
#
# # tag-part = option-part : type-part * settings-part
# # tag-part += option-part : type-part * settings-part
#
# eg.
# # --help | -h
# # --delimiter | -d : text
# # columns += [1,*] column-number : integer
#
# The <option-part> is the only mandatory section, and the other sections are
# only present if their corresponding separator is present. Briefly,
#
# tag-part - This determines where the decoded result is stored
# option-part - This gives the name of the option or argument, with aliases
# type-part - This gives the type of value with the option or for the
# argument, used for validation checking and completion
# settings-part - This gives special settings for the option/argument, and is
# rarely used
#
# These are now described in detail.
# Tag part:
#
# This determines where the decoded result is stored.
#
# One of:
# `[' <key-name> `]'
# or
# <variable-name>
#
# eg.
# [name]
# an_array
# associative_array
# associative_array[name]
#
# <key-name> is the name of the hash key the value will be stored in - using
# the default `return values' associative array provided to parse_opts.
#
# The alternative of variable-name stores the value directly in the given
# variable. The variable is expected to be local to the calling program, and
# the exact behaviour depends on its type.
#
# If <tag-part> is not provided, then it will default to using <key-name> of
# the first option or argument name in the <option-part> list. Option names
# will have any leading `-' stripped off them for this purpose.
#
# The way the tag is accessed also depends on the separator used before the
# <option-part>. There are two possibilities - either `=' which overwrites any
# value, or `+=' which appends to any current value. Note that this is
# completely independent of the option/argument frequency, detailed later.
#
# The possible options when storing decoded values are:
#
# Type of storage Overwrite Append
#
# undefined (ie. variable doesn't exist) Type 1 Type 2
# index into default result array Type 1 Type 2
# scalar Type 1 Type 2
# array Type 3 Type 4
# associative array Type 1 Type 2
#
# Type 1 - The old value is replaced each time a new value is stored
# Type 2 - Each new value is appended as a string to the previous value, with
# a space as separator if required
# Type 3 - Any old entries are removed, leaving just the new entry
# Type 4 - The new entry is appended onto the end of the array
# Option part:
#
# This gives the name of the option or argument, with aliases.
#
# [ `[' <frequency> `] ' ] <option-name> [ ` | ' <option-name> ... ]
#
# eg.
# --help | -h
# [*] --directory | -d
# [0,1] filename
#
# <option-name> gives the name of the option, as it will appear on the command
# line. eg. `-h' or `--help'. Multiple <option-name>s may be given in order to
# supply aliases of the same option. Single letter options begin with `-', and
# may be grouped. Other options are long options and begin with `--'.
#
# If there is no `-' at the start of the option name then this is taken to be
# an argument definition. Obviously this doesn't appear on the command line,
# but it will be used when reporting any errors. For this reason it is
# recommended that it matches the argument name as specified in the help. Note
# that aliases for arguments are not allowed (there would be no point).
#
# <option-name> may consist of `-', `_', and alphanumeric characters only.
#
# <frequency> is of the form:
# <count>
# or
# `*'
# or
# <min-count> `,' <max-count>
# or
# <min-count> `,*'
# or
# `*,' <max-count>
#
# Where <count>, <min-count>, and <max-count> are all positive integers. These
# give the allowable range of the number of occurrences of the option or
# argument. If only <count> is provided, then <min-count> and <max-count> will
# both have this value.
#
# If a `*' is present then it means `any value' for that count. For an option
# this would mean it could appear any number of times. For an argument it
# means that any spare arguments on the command line will be used up for this
# argument. Only one argument will normally have a frequency including `*',
# otherwise there will be a clash as to what gets the remaining arguments. In
# the event of such a clash (which may also happen if there is more than one
# min/max range specified), the early arguments will all use up to their
# maximum before moving on to processing the next one. Note that enough
# arguments will always be left to meet the requirements of the minimum count
# of any following arguments, so it is only `spare' ones which get treated in
# this way.
#
# Typical frequencies for options are:
#
# [0,1] - The default. Optional - may only occur once or not at all.
# [*] - Repeatable. May appear any number of times.
# [1,1] - Mandatory. NOT YET CHECKED
# NOTE: Min/max values are currently not fully checked for options
#
# Typical frequencies for arguments are:
#
# [1] - The default. Single mandatory argument.
# [0,1] - Optional.
# [*] - Any number of arguments, or none
# [1,*] - Any number of arguments, but at least one
# Type part:
#
# This gives the type of value with the option or for the argument, used for
# validation checking and completion.
#
# [ `? ' ] <type-name> [ `=' <value> ]
#
# eg.
# string
# ? filename
# constant=true
# values="(value1 value2)"
# values=${(k)functions}
#
# `?' indicates that validation should not be performed on the option
# value/argument. The type is still used for options to determine if they take
# a value, and for completion with both options and arguments.
#
# <type-name> is one of:
#
# switch - (2) Simple switch. Result will be 1. Default for options
# constant - (1) (2) Constant to be assigned. Result will be <value> given
# string - Takes a parameter with no validation. Default for arguments
# values - (1) Takes a parameter validated against the supplied <value>
# interpreted as some kind of list of values. See below for a more
# detailed explanation
# pattern - (1) Takes a parameter validated against the supplied <value>
# which will be interpreted as a shell glob pattern (with the
# extended_glob option set)
# exec - (1) Takes a parameter validated by executing the <value>
# supplied as a command. The command will be provided with an extra
# parameter on the end, which will be the word requiring
# validation, and should return success/failure in the usual way
# OR - Any pre-defined type (none of which take a <value>). For a
# complete list of these, see the _parse_opts_setupdefinitions
# function below, which is where they are defined
# OR - Any user defined type (none of which take a <value>)
#
# (1) These types take a value, which must be supplied. Others do not and one
# must not be supplied to those. The value will be subject to normal shell
# quote removal.
# (2) These are for use with options only. They clearly have no useful meaning
# for arguments.
#
# values type:
#
# There are three modes of operation for the values type. Remember that quote
# removal will happen on the <value> before any of the following.
#
# If the <value> is enclosed in `(' ... `)' then it is taken as a literal list
# of valid values. These will be handled according to normal shell word
# splitting and quote removal rules.
# eg.
# "(avalue another_value 'with space')"
#
# If the <value> starts with `$' then it will be taken as a variable
# substitution which is expected to return an array of results. If there is an
# `@' expansion flag present then the expansion will automatically take place
# in double quotes in order that it will have the desired effect.
# eg.
# $array
# ${(k)associative_array}
# ${(@)=string}
#
# In all other cases the value is assumed to be the name of some kind of shell
# variable, and the behaviour will depend on this variable's type:
#
# scalar - Treated as a string and handled according to normal shell
# word splitting and quote removal rules
# array - The array values are used directly
# associative array - The keys are used for validation. In this case the
# values are also used for the completion - each value is
# treated as the description for its key when passed to
# the completion code
#
# eg.
# string
# associative_descriptions
# Settings part:
#
# This gives special settings for the option/argument, and is rarely used.
#
# <setting-name> [ `=' <value> ] [ ` ' ... ]
#
# eg.
# complete.hidden
# excludes="-L --no-list"
#
# <setting-name> is the identifier of whatever needs to be set. The value is
# subject to quote removal, and is then assigned directly to the setting after
# any of the other assignments have been made. If the value is not given, then
# `1' will be assigned.
#
# Current identifiers in use are:
#
# NOTE: These identifiers subject to change for now
#
# tag - (1) This is as for the tag section described above
# frequency - (1) This is the frequency from the option section described
# above, with the surrounding brackets removed
# type - (1) This is as the the type section described above
# help - (2) This is the description for this option extracted from the
# help
# aliases - (2) This is a list of aliases of this option - ie. the other
# <option-name>s provided in the same specification. It is empty
# for arguments
# excludes - (2) This is a list of options which are not allowed if this one
# is present. For a non-repeatable option, this is usually the same
# as aliases. It is currently unused for arguments
# complete.hidden - If true then the option is not supplied for completion
#
# (1) This should not normally be set, since it just duplicates information
# already provided
# (2) This is generated automatically, but it may on rare occasions be useful
# to override it manually with a different value
## Type definition lines
#
# `#type ' <type-name> ` ' <base-part> [ [ ` ' <description> ] ` ' <action> ]
# [ ` * ' <settings-part> ]
#
# eg.
# #type integer pattern=([-+]|)<-> "signed integer number" " "
# #type function values=${(k)functions} "shell function" _functions
#
# <type-name> is the identifier for this type to be assigned to. This is what
# appears as <type-name> in option/argument type sections, or as a base type
# to another user defined type.
#
# <type-name> may consist of `-', `_', and alphanumeric characters only.
#
# <base-part> is of the form:
# <base-type-name> [ `=' <value> ]
#
# <base-type-name> and <value> are handled exactly the same as for the type
# section in the option/argument description. The base type is what this user
# defined type is an alias for. It may be either a built in type, or another
# user defined type (in which case <value> must not be supplied).
#
# <description> is the description that the option value or argument will get
# when supplied to the completion code, and is used in exactly the same way as
# the description provided to the _arguments completion function.
#
# <action> is the action that the option value or argument will have when
# supplied to the completion code, and is used in exactly the same way as the
# action provided to the _arguments completion function.
#
# Both <description> and <action> are optional, and if not supplied or if
# empty then the respective field from the base type will be used,
# recursively. In order to override the value in a base type with nothing, a
# single space should be used.
#
# <settings-part> is identical to that of options/arguments, except for the
# valid identifiers, which are:
#
# NOTE: These identifiers subject to change for now
#
# base - (1) This is as for the base-part described above
# complete.description - (1) This is as for the description described above
# complete.action - (1) This is as for the action described above
# complete.restrict - This controls the way the `words' array is handled by
# _arguments when it calls the action. See the
# completion documentation for details. It may have any
# of the following values:
# none - Default. Used as is, corresponds to `*:'
# normal - Modified to only include the arguments on
# the command line, corresponds to `*::'
# match - Modified to only include the matched
# arguments, corresponds to `*:::'
# NB. When using a type with restrict set other than to
# `none', the frequency of the argument should be of
# the form [*] or [<n>,*]
# NOTE: Currently not valid on options
#
# (1) This should not normally be set, since it just duplicates information
# already provided
## Enabling completion with Zsh
#
# In order to enable completion with Zsh, all that is required is to register
# the _parse_opts function as the completer for your command.
#
# eg.
# compdef -n _parse_opts yes
#
# _parse_opts will need to be defined or autoloaded beforehand.
#
# Now you should find full completion available for options, option values,
# and arguments. Note that not all types are completable. You may register
# user defined types to provide any unusual completion actions, but for the
# `switch', `constant', and `values' types sensible completions will be
# generated automatically. No completion will be offered by default for values
# with the `string', `pattern', or `exec' types. All pre-defined types already
# have completions where appropriate.
#
# If you have problems with an automatic completion you can see the _arguments
# call generated by simply running your command directly with the
# --zsh-completion option. This will then enable you to find out where the
# problem is. Currently there's no checking for sanity in the option
# specifications so it is quite possible to generate invalid calls.
## Example:
#
# Here is an example of a small Zsh script/function which makes effective use
# of parse_opts. It implements a command similar to 'yes' available on some
# Unix systems.
#
# local -A opts
# parse_opts - opts -- "$argv[@]" <<'EOF' || return 1
# Description:
# Repeatedly print a string
#
# Usage:
# yes [options] [<text> ...]
#
# Options:
# -e, --escape Interpret escape characters as for echo (default unless
# the BSD_ECHO option is set)
# # [echoopt] += --escape | -e : constant=-e
# -E, --no-escape Prevent interpretation of escape characters
# # [echoopt] += --no-escape | -E : constant=-E
# -h, --help Provide this help
# # --help | -h
# -l <count>, --lines=<count>
# Output only count times
# # --lines | -l : integer
# -n, --no-newline Suppress output of automatic newline
# # [echoopt] += --no-newline | -n : constant=-n
# -s <seconds>, --sleep=<seconds>
# Pause for number of seconds between each echo
# # --sleep | -s : integer
#
# Arguments:
# [<text> ...] Optional text to print. Defaults to 'yes'
# # [text] += [*] text
# EOF
#
# # Output as required
# while (( ${opts[lines]:-1} ))
# do
# echo $=opts[echoopt] ${opts[text]:-yes}
# (( opts[sleep] )) && sleep $opts[sleep]
# (( opts[lines] && opts[lines]-- ))
# done
### Configuration
# To do:
# excludes properly - options & arguments
# frequency checking on options
# optional values for options - or more fully, frequency for same
# interface to extend excludes
# type.complete.restrict for options
# settings identifier names. decide on them properly
# Calls _parse_opts_parsehelp in order to create the built-in types & other
# default definitions
_parse_opts_setupdefinitions() {
_parse_opts_parsehelp '
## These are extensions of the basic built in types
#type switchoff constant=0 "" ""
#type integer pattern=([-+]|)<-> "signed integer number" " "
#type posinteger pattern=<-> "positive integer number" " "
#type text string text " "
## These are file-like things validated with test
#type directory exec="test -d" directory _directories
#type file exec="test -f" file _files
## These are lists from the shell
#type function values=${(k)functions} "shell function" _functions
#type option exec=_parse_opts_validate_option "zsh option" _options
#type parameter values=${(k)parameters} parameter _parameters
## Complex type for sub commands - ie. where a real command is passed as an
## argument. Note that this type may currently only be used for argument
## specifications, and only with a frequency of [*], [1,*], etc
#type fullcommand string "" " _normal" \
* complete.restrict=match
# --help
# --zsh-completion * complete.hidden
'
}
### Helper functions for default user types
# Returns 0 for valid option, 1 for otherwise. Can't use values= due to the
# '_' and case handling in option names
_parse_opts_validate_option() {
local option="$argv[1]"
(( $+options[$option] ))
}
### Implementation
# NB. In some of the below functions (marked) locals must begin with _po_.
# This is to prevent name clashes when options refer back to variables in the
# calling program's name space
# Print an associative array with multi-field keys ('.' delimited), as if it
# were a real nested structure. Currently only handles one level of 'nesting'
# though. Used for debugging
_parse_opts_showdata() {
local var="$argv[1]"
# Make a copy of the array because otherwise we have trouble later
local -A data
data=("${(@Pkv)var}")
echo "$var = {"
# Collect the first field from the keys, uniqued
local -aU stems
stems=(${(k)data%%.*})
# Process each of the first fields
local -a keys
for stem in ${(o)stems}
do
echo " $stem = {"
# Get a list of matching full keys
keys=(${${(Mk)data:#$stem.*}#$stem.})
# Work out the width of field padding required
integer length=${#${(O)keys//?/?}[1]}
# Print each of the key/value pairs
local key=""
for key in ${(o)keys}
do
echo " ${(r:length:::::)key} = '$data[$stem.$key]'"
done
echo " }"
done
echo "}"
}
# This helper function handles the parsing of option definitions for
# _parse_opts_parsehelp (uses locals directly)
_parse_opts_parseoption() {
# Extract the special parameters if there are any
local -a parameters
if (( paraminfo[(I)\*] ))
then
parameters=($paraminfo[paraminfo[(I)\*]+1,-1])
paraminfo=($paraminfo[1,paraminfo[(I)\*]-1])
fi
# Extract the type information if there is any
local type=""
if (( paraminfo[(I):] ))
then
type=$paraminfo[paraminfo[(I):]+1,-1]
paraminfo=($paraminfo[1,paraminfo[(I):]-1])
fi
# Extract the tag information if there is any. Append (ie. where '+='
# instead of '=') is recorded by prefixing a '+' onto the stored tag
local tag=""
if (( paraminfo[(I)(+|)=] ))
then
tag=$paraminfo[1,paraminfo[(I)(+|)=]-1]
if [[ $paraminfo[(r)(+|)=] == += ]]
then
tag="+$tag"
fi
paraminfo=($paraminfo[paraminfo[(I)(+|)=]+1,-1])
fi
# Handle the optional frequency definition
# (default is [1] for arguments, [0,1] for options)
local frequency=""
if [[ $paraminfo[1] == \[*\] ]]
then
frequency=${${paraminfo[1]#\[}%\]}
shift paraminfo
fi
# Options is remainder less the '|'
paraminfo=(${paraminfo:#\|})
# Check we've got something left
if (( !$#paraminfo ))
then
echo "Missing option/argument name in definition: '" $=line "'" 1>&2
return 1
fi
# If tag not provided then default to first option name with any leading '-'s removed
if [[ -z $tag ]]
then
tag="[${paraminfo[1]##-#}]"
fi
# Assign all the details for the current options/argument
local option=""
for option in $paraminfo
do
local aliases="" excludes=""
# Check for invalid characters in option name
if [[ $option != [-_[:alnum:]]## ]]
then
echo "Invalid name for option/argument: '$option'" 1>&2
return 1
fi
# Establish defaults for missing values
if [[ $option == -* ]]
then
[[ -z $frequency ]] && frequency="0,1"
[[ -z $type ]] && type="switch"
# Aliases, excluding this option
aliases=${paraminfo:#$option}
# Excludes. Only if the max frequency of this option is 1, same as aliases
integer min_frequency=0 max_frequency=0
_parse_opts_minmaxfrequency min_frequency max_frequency $frequency || return
if (( max_frequency == 1 ))
then
excludes=$aliases
fi
else
[[ -z $frequency ]] && frequency="1"
[[ -z $type ]] && type="string"
# Calculate the minimum number of arguments we need for the command to
# be able to run. We need this later to know how many arguments are
# spare when we have a choice on how many to use
integer min_frequency=0 max_frequency=0
_parse_opts_minmaxfrequency min_frequency max_frequency $frequency || return
(( _po_minargs += min_frequency ))
fi
# Store the main data for this option
_po_params[$option.tag]=$tag
_po_params[$option.frequency]=$frequency
_po_params[$option.type]=$type
_po_params[$option.help]=$helpline
_po_params[$option.aliases]=$aliases
_po_params[$option.excludes]=$excludes
# Store option/argument name in appropriate array
if [[ $option == -* ]]
then
_po_options[$option]=1
else
_po_arguments=($_po_arguments $option)
fi
# Store the special parameters
local parameter="" value=""
for parameter in "$parameters[@]"
do
if [[ $parameter == *=* ]]
then
value=${(Q)parameter#*=}
parameter=${parameter%%\=*}
else
value=1
fi
_po_params[$option.$parameter]=$value
done
done
return 0
}
# This helper function handles the parsing of type definitions for
# _parse_opts_parsehelp (uses locals directly)
_parse_opts_parsetype() {
# Extract the special parameters if there are any
local -a parameters
if (( paraminfo[(I)\*] ))
then
parameters=($paraminfo[paraminfo[(I)\*]+1,-1])
paraminfo=($paraminfo[1,paraminfo[(I)\*]-1])
fi
local type="$paraminfo[1]" basetype="$paraminfo[2]"
local description="${(Q)paraminfo[3]}" action="${(Q)paraminfo[4]}"
# Check for invalid characters in type name
if [[ $type != [-_[:alnum:]]## ]]
then
echo "Invalid name for type: '$type'" 1>&2
return 1
fi
# Store the main data for this type
_po_types[$type.base]=$basetype
_po_types[$type.complete.description]=$description
_po_types[$type.complete.action]=$action
# Store the special parameters
local parameter="" value=""
for parameter in "$parameters[@]"
do
if [[ $parameter == *=* ]]
then
value=${(Q)parameter#*=}
parameter=${parameter%%\=*}
else
value=1
fi
_po_types[$type.$parameter]=$value
done
return 0
}
# This function takes an encoded helpstring as its only parameter and
# generates the various definition arrays
_parse_opts_parsehelp() {
local -a helptext
# Process each line in the helptext. Note double newline (ie. empty line) is
# converted to have a space so it doesn't get lost in the splitting
local line="" helpline=""
local -a paraminfo
for line in ${(@f)${argv[1]//$'\\\n'}//$'\n\n'/$'\n \n'}
do
# If it starts with a '#' then its some kind of command line
if [[ $line == [[:blank:]]#\#* ]]
then
# Decode the line into fields. Use 'z' not '=' to get proper quote handling
paraminfo=(${(z)line})
local command="$paraminfo[1]"
shift paraminfo
case $command in
\#) # An option description line
_parse_opts_parseoption || return
;;
\#type) # A type definition line
_parse_opts_parsetype || return
;;
\#\#*) # A comment
;;
*) # This is a configuration error
echo "Invalid command: '$command'" 1>&2
return 1
;;
esac
else
# Doesn't start with a # so append it to help
helptext=($helptext $line)
# If we have a line which has non-whitespace before any tabs it's an
# option or argument or something similar. So we reset our current
# helpline because the description will come after it
if [[ $line == \ #[[:graph:]]* ]]
then
helpline=""
fi
# If the line has a tab on it then it might have a description after the tab
if [[ $line == *$'\t'* ]]
then
# Extract the text after any tabs, and trim it of whitespace
line=${${line##*$'\t'[[:blank:]]#}%%[[:blank:]]}
if [[ -n $line ]]
then
# Add to current helpline
if [[ -n $helpline ]]
then
helpline="$helpline $line"
else
helpline=$line
fi
fi
fi
fi
done
# Create the help string from the array of lines. In order to get a blank
# line this far it must have a space on it
_po_helptext=(${(F)helptext})
return 0
}
# This function decodes the numeric expression within a frequency
# specification to return an integer value. The variable given to var will be
# updated with the results of processing the given expression substituting
# number for `*'
_parse_opts_frequencyvalue() {
local var="$argv[1]"
integer number="$argv[2]"
local expression="$argv[3]"
integer result=0
case $expression in
\*) # Use default number
result=$number
;;
<->) # Use fixed number
result=$expression
;;
*) # This is a configuration error
echo "Unrecognised frequency: '$expression'" 1>&2
return 1
;;
esac
eval $var=$result
return 0
}
# This function decodes a frequency expression into min and max values. The
# variables given to minvar & maxvar will be updated with the results of
# processing the given expression substituting 0 or -1 for `*'
_parse_opts_minmaxfrequency() {
local minvar="$argv[1]" maxvar="$argv[2]" expression="$argv[3]"
case $expression in
<->,*) # Separate min & max values
_parse_opts_frequencyvalue $minvar 0 ${expression%,*} || return
_parse_opts_frequencyvalue $maxvar -1 ${expression#*,} || return
;;
*,*) # Must be a number in the first field, this is invalid
echo "Unrecognised frequency expression: '$expression'" 1>&2
return 1
;;
*) # Min & max values the same
_parse_opts_frequencyvalue $minvar 0 $expression || return
_parse_opts_frequencyvalue $maxvar -1 $expression || return
;;
esac
return 0
}
# This function expands the parameter given after the 'values=' type into a
# full list of valid values, which are then placed in the array named by
# listname. If the hashname value is also given then a hash with values as
# keys and descriptions as values is also updated if available
#
# NB. Locals in this function must begin _po_
_parse_opts_expandvalues() {
local _po_param="$argv[1]" _po_list_name="$argv[2]" _po_hash_name="$argv[3]"
local -a _po_local_list _po_local_hash
case $_po_param in
\(*\)) # Literal list
_po_local_list=(${(z)${${_po_param#\(}%\)}})
;;
\$*) # Variable substitution
# We don't know if there's an @ flag late in the substitution - in which
# case it would need to be done in quotes to work properly. So try both
# with and without and pick which way gave us the most results
local -a _po_local_list_q
eval _po_local_list="($_po_param)"
eval _po_local_list_q="(\"$_po_param\")"
if (( $#_po_local_list_q > $#_po_local_list ))
then
_po_local_list=("$_po_local_list_q[@]")
fi
;;
*) # External variable name
# Behave according to the type of the external variable
case ${(Pt)_po_param} in
array*) # An array
_po_local_list=("${(@P)_po_param}")
;;
association*) # An associative array
# Collect the keys for the values
_po_local_list=("${(@kP)_po_param}")
# The values will have descriptions, so we can update the hash as well
_po_local_hash=("${(@kvP)_po_param}")
;;
*) # Some kind of scalar. Split according to shell rules, remove quotes on each value
_po_local_list=("${(@Q)${(z)_po_param}}")
;;
esac
;;
esac
# Return results
eval $_po_list_name='("$_po_local_list[@]")'
[[ -n $_po_hash_name ]] && eval $_po_hash_name='("${(@kv)_po_local_hash}")'
return 0
}
# This helper function for _parse_opts_generatecompletion decodes our type
# information into a form suitable for the completion system. It directly sets
# the _po_description, _po_action, and _po_restrict variables in the calling
# function
#
# NB. Locals in this function must begin _po_
_parse_opts_generatecompletiontype() {
local _po_option="$argv[1]"
# Extract the type without any leading '?'
local _po_type="${_po_params[$_po_option.type]#? }"
# Recursively convert user defined type into base type
while (( $+_po_types[$_po_type.base] ))
do
# Take any settings supplied from the user defined type which we haven't already got
[[ -z $_po_description ]] && _po_description=$_po_types[$_po_type.complete.description]
[[ -z $_po_action ]] && _po_action=$_po_types[$_po_type.complete.action]
[[ -z $_po_restrict ]] && _po_restrict=$_po_types[$_po_type.complete.restrict]
_po_type=$_po_types[$_po_type.base]
done
# Decode built in type to set anything not done already
local _po_param="${(Q)_po_type#*=}"
case $_po_type in
switch|constant=*)
# No parameters, leave blank
;;
string)
# Takes a parameter with no validation
[[ -z $_po_description ]] && _po_description="string"
[[ -z $_po_action ]] && _po_action="_files"
;;
values=*)
# Takes a parameter, validated by list of values
[[ -z $_po_description ]] && _po_description="value"
if [[ -z $_po_action ]]
then
local -a _po_values
local -A _po_hash
_parse_opts_expandvalues $_po_param _po_values _po_hash || return
if (( ! $#_po_hash ))
then
_po_values=(${(q)_po_values})
_po_action="($_po_values)"
else
_po_hash=("${(@qkv)_po_hash}")
_po_action="(("
local _po_key=""
for _po_key in ${(k)_po_hash}
do
_po_action="$_po_action$_po_key\:$_po_hash[$_po_key] "
done
_po_action="$_po_action))"
fi
fi
;;
pattern=*)
# Takes a parameter which is a pattern for validation
[[ -z $_po_description ]] && _po_description="value"
[[ -z $_po_action ]] && _po_action=" "
;;
exec=*)
# Takes a parameter which is a command for validation
[[ -z $_po_description ]] && _po_description="value"
[[ -z $_po_action ]] && _po_action=" "
;;
*) # This is a configuration error
echo "Invalid option type for option $_po_option: '$_po_type'" 1>&2
return 1
;;
esac
return 0
}
# This function generates the _arguments call used by the completion system.
# It is called when the special option --zsh-completion is passed on the
# command line to the calling program. The command is written to stdout only
# on success, and the completion system will then take that and execute it. On
# error, errors are written to stderr instead. If returning the completion
# information, parse_opts always exits with 1 in order to cause the calling
# program to exit, so the return code can not be used to signal errors
#
# NB. Locals in this function must begin _po_
_parse_opts_generatecompletion() {
local -a _po_specs
# Process each option to build a list of specifications for _arguments.
local _po_option=""
for _po_option in ${(k)_po_options} $_po_arguments
do
if (( ! _po_params[$_po_option.complete.hidden] ))
then
local _po_frequency="$_po_params[$_po_option.frequency]"
integer _po_min_frequency=0 _po_max_frequency=0
_parse_opts_minmaxfrequency _po_min_frequency _po_max_frequency $_po_frequency || return
# Try to determine a description/completer from the type of this option
local _po_description="" _po_action="" _po_restrict=""
_parse_opts_generatecompletiontype $_po_option || return
# Default restriction of none
if [[ -z $_po_restrict ]]
then
_po_restrict="none"
fi
local _po_spec=""
if [[ $_po_option == -* ]]
then
# Generate excludes clause if we have any
if [[ -n $_po_params[$_po_option.excludes] ]]
then
_po_spec="$_po_spec($_po_params[$_po_option.excludes])"
fi
# Add repeating option flag if max frequency is not 1
if (( _po_max_frequency != 1 ))
then
_po_spec="$_po_spec*"
fi
# Add the main option name section
_po_spec="$_po_spec$_po_option"
# If we have a long option which takes a parameter then add the '=' flag
# to say --option=value is valid
if [[ $_po_option == --* && -n $_po_action ]]
then
_po_spec="$_po_spec="
fi
# Add the description of the option if one was successfully extracted from the help text
if [[ -n $_po_params[$_po_option.help] ]]
then
_po_spec="${_po_spec}[${_po_params[$_po_option.help]%%. *}]"
fi
# Now add the description and completer for the parameter, if we have one
if [[ -n $_po_action ]]
then
_po_spec="$_po_spec:$_po_description:$_po_action"
fi
# Collect the specifications
_po_specs=($_po_specs "'$_po_spec'")
else
_po_spec="$_po_spec:$_po_description"
# Add the description of the argument if one was successfully extracted from the help text
if [[ -n $_po_params[$_po_option.help] ]]
then
_po_spec="$_po_spec - ${${_po_params[$_po_option.help]%%. *}//:/\\:}"
fi
_po_spec="$_po_spec:$_po_action"
# Collect the specifications
if (( _po_min_frequency >= 0 )) && [[ $_po_restrict == none ]]
then
repeat $_po_min_frequency
do
_po_specs=($_po_specs "'$_po_spec'")
done
if (( _po_max_frequency >= _po_min_frequency ))
then
repeat $(( _po_max_frequency - _po_min_frequency ))
do
_po_specs=($_po_specs "':$_po_spec'")
done
else
_po_specs=($_po_specs "'*$_po_spec'")
fi
else
# Handle options to restrict 'words' array for completion action
case $_po_restrict in
match)
_po_specs=($_po_specs "'*::$_po_spec'")
;;
normal)
_po_specs=($_po_specs "'*:$_po_spec'")
;;
*) # none
_po_specs=($_po_specs "'*$_po_spec'")
;;
esac
fi
fi
fi
done
# Only output the _arguments call on success
# -s - Allow option grouping for single letter options
# -w - Arguments follow single letter options, one for each relevant option
# -S - Handle -- to terminate options
# -A - No more options after first non-option argument
echo "_arguments -s -w -S -A '-*' $_po_specs"
return 0
}
# This function stores the given value against the given option/argument name,
# as defined by its related tag
#
# NB. Locals in this function must begin _po_
_parse_opts_store() {
local _po_name="$argv[1]" _po_value="$argv[2]"
local _po_tag="$_po_params[$_po_name.tag]"
# If append is enabled for this tag, it will be prefixed with `+'
integer append=0
if [[ $_po_tag == +* ]]
then
append=1
_po_tag=${_po_tag#+}
fi
case $_po_tag in
\[*\]) # Store in the default options associative array
_po_tag=${${_po_tag#\[}%\]}
if (( append && $#_po_results[$_po_tag] ))
then
_po_results[$_po_tag]="$_po_results[$_po_tag] $_po_value"
else
_po_results[$_po_tag]=$_po_value
fi
;;
*) # Store in external variable/array
# Behave according to the type of the external variable
case ${(Pt)_po_tag} in
array*)
# An array
if (( append ))
then
eval $_po_tag='("${(@P)_po_tag}" "$_po_value")'
else
eval $_po_tag='("$_po_value")'
fi
;;
association*)
# An associative array. Add option/value pair
local _po_oldvalue=""
eval _po_oldvalue="\$${_po_tag}[$_po_name]"
if (( append && $#_po_oldvalue ))
then
eval $_po_tag\[\$_po_name\]='"$_po_oldvalue $_po_value"'
else
eval $_po_tag\[\$_po_name\]='$_po_value'
fi
;;
*)
# Some kind of scalar or undefined
if (( append && ${(P)#_po_tag} ))
then
eval $_po_tag='"${(P)_po_tag} $_po_value"'
else
eval $_po_tag='$_po_value'
fi
;;
esac
esac
return 0
}
# This helper function for _parse_opts_handletype gets the next value for an
# option. It uses local variables in _parse_opts_handletype and so should not
# be called from elsewhere
_parse_opts_getnextvalue() {
if (( _po_hasgivenvalue ))
then
# Value supplied with option so use that one first
_po_value=$_po_givenvalue
_po_hasgivenvalue=0
else
# Get the next argument as the value. Note that if single letter options
# taking a parameter are grouped, then the parameters follow straight
# afterwards in the respective order
# Check there's a value to get
if (( $#_po_argv ))
then
_po_value=$_po_argv[1]
shift _po_argv
else
echo "Missing parameter to option '$name'" 1>&2
return 1
fi
fi
return 0
}
# This function handles processing of the option value or argument type. Any
# values it needs will be fetched vie _parse_opts_getnextvalue, and the
# appropriate result variable will be updated. Any validation of the values is
# also performed here
#
# NB. Locals in this function must begin _po_
_parse_opts_handletype() {
local _po_name="$argv[1]" _po_hasgivenvalue="$argv[2]" _po_givenvalue="$argv[3]"
# Check for leading '?' to disable validation
integer _po_validate=1
if [[ $_po_params[$_po_name.type] == ?\ * ]]
then
_po_validate=0
fi
# Extract the type without any leading '?'
local _po_type="${_po_params[$_po_name.type]#? }"
# Recursively convert user defined type into base type
while (( $+_po_types[$_po_type.base] ))
do
_po_type=$_po_types[$_po_type.base]
done
local _po_param="${(Q)_po_type#*=}" _po_value
integer _po_isvalid=1
case $_po_type in
switch)
# Simple switch. Mark its presence
_po_value=1
;;
constant=*)
# Constant to be assigned (after quote removal). eg. constant=value or constant="more values"
_po_value=$_po_param
;;
string)
# Takes a parameter with no validation
_parse_opts_getnextvalue || return
;;
values=*)
# Takes a parameter, validated by list of values from external array
_parse_opts_getnextvalue || return
if (( $_po_validate ))
then
local -a _po_values
_parse_opts_expandvalues $_po_param _po_values || return
if (( ! $_po_values[(I)$_po_value] ))
then
_po_isvalid=0
fi
fi
;;
pattern=*)
# Takes a parameter which is a pattern for validation
_parse_opts_getnextvalue || return
if [[ $_po_validate -ne 0 && $_po_value != $~_po_param ]]
then
_po_isvalid=0
fi
;;
exec=*)
# Takes a parameter which is a command for validation
_parse_opts_getnextvalue || return
if (( $_po_validate )) && ! eval $_po_param \$_po_value
then
_po_isvalid=0
fi
;;
*) # This is a configuration error
echo "Invalid option type for option $_po_name: '$_po_type'" 1>&2
return 1
;;
esac
if (( !_po_isvalid ))
then
echo "Invalid value for option $_po_name: '$_po_value'" 1>&2
return 1
fi
_parse_opts_store $_po_name $_po_value
}
# This function decodes any options present in the command line arguments,
# using the pre-generated option arrays from _parse_opts_parsehelp. Arguments
# are provided in _po_argv and are removed as and when processed. Decoded
# options are returned in _po_results. Returns 1 if an error prevented the
# options from being processed, else 0
#
# NB. Locals in this function must begin _po_
_parse_opts_dooptions() {
# Process each argument while it is an option
while [[ $_po_argv[1] == -* ]]
do
local _po_option="$_po_argv[1]"
shift _po_argv
case $_po_option in
--) # End of option list
# Nothing else to do - return success
return 0
;;
--*) # Long argument
# Check to see if the option exists
if (( $+_po_options[${_po_option%%\=*}] ))
then
# Check to see if value was in option, ie. --name=value
if [[ $_po_option == *=* ]]
then
# Extract value in option and pass to type processing
_parse_opts_handletype ${_po_option%%\=*} 1 ${_po_option#*=} || return
else
# Call type handling without a value
_parse_opts_handletype $_po_option 0 || return
fi
else
echo "Invalid option: $_po_option" 1>&2
return 1
fi
;;
-?*) # Short option (may be grouped)
# Extract the list of options from eg. -jkl
local _po_optiongroup="${_po_option#-}"
# Process each letter individually
while [[ -n $_po_optiongroup ]]
do
# Extract the first letter
local _po_optionletter="$_po_optiongroup[1]"
_po_optiongroup=${_po_optiongroup#?}
# Check to see if is a valid short option
if (( $+_po_options[-$_po_optionletter] ))
then
# It is so process it as the type determines
_parse_opts_handletype -$_po_optionletter 0 || return
else
echo "Invalid option: -$_po_optionletter" 1>&2
return 1
fi
done
;;
*) # Must be a lone -
echo "Missing option after -" 1>&2
return 1
;;
esac
done
return 0
}
# This function decodes any arguments present in the command line after the
# options have been removed, using the pre-generated option arrays from
# _parse_opts_parsehelp. Arguments are provided in _po_argv and are removed as
# and when processed. Decoded values are returned in _po_results. Returns 1 if
# an error prevented the arguments from being processed, else 0
#
# NB. Locals in this function must begin _po_
_parse_opts_doarguments() {
integer _po_spareargs
(( _po_spareargs = $#_po_argv - _po_minargs ))
if (( _po_spareargs < 0 ))
then
echo "Insufficient arguments" 1>&2
return 1
fi
# Process each argument specification
local _po_argument=""
for _po_argument in $_po_arguments
do
local _po_frequency="$_po_params[$_po_argument.frequency]"
# Ensure number of args to use is within required range
integer _po_min=0 _po_max=0
_parse_opts_minmaxfrequency _po_min _po_max $_po_frequency || return
# Try to grab all the spare arguments
integer _po_use
(( _po_use = _po_min + _po_spareargs ))
# Restrict this if that's too many
if (( _po_max >= 0 && _po_use > _po_max ))
then
_po_use=$_po_max
fi
# Recalculate how many arguments are spare for the next option
(( _po_spareargs -= _po_use - _po_min ))
# This shouldn't happen due to being checked above
if (( _po_use < _po_min || $#_po_argv < _po_use ))
then
echo "Argument processing error" 1>&2
return 1
fi
# Process each of them according to the type
repeat $_po_use
do
_parse_opts_handletype $_po_argument 1 $_po_argv[1] || return
shift _po_argv
done
done
# If there's any surplus arguments that's an error
if (( $#_po_argv ))
then
echo "Too many arguments" 1>&2
return 1
fi
return 0
}
# This function controls the overall processing
#
# NB. Locals in this function must begin _po_
_parse_opts_process() {
local -a _po_arguments
local -A _po_options _po_params _po_types
local _po_helptext=""
integer _po_minargs=0
# Check for arguments. If we have one and it is not '-' then it is the help
# string. Otherwise need to to get that from stdin
local _po_config=""
if [[ -n $argv[1] && $argv[1] != - ]]
then
_po_config=$argv[1]
else
_po_config=$(cat)
fi
# Parse the default definitions for types etc.
_parse_opts_setupdefinitions || return
# Parse the help string we've got to determine the type of options
_parse_opts_parsehelp $_po_config || return
# Useful for debugging
if (( _po_debug ))
then
_parse_opts_showdata _po_params
echo
_parse_opts_showdata _po_types
echo
echo "_po_options=(${(ok)_po_options})"
echo "_po_arguments=($_po_arguments)"
echo "_po_minargs=$_po_minargs"
fi
# Perform the option recognition
if _parse_opts_dooptions
then
# If success, and help was requested then give it. Note that it is up to
# the user to supply the help option before we can use it
if (( _po_results[help] ))
then
echo $_po_helptext
return 1
fi
# Entry point for automatic zsh completion. We always return 1 in order to
# get the calling program to exit. The completion system knows this is not
# an error
if (( _po_results[zsh-completion] ))
then
_parse_opts_generatecompletion
return 1
fi
# Perform the argument recognition
if ! _parse_opts_doarguments
then
# There was an error in processing. Mention the --help option
echo "Use --help for more information" 1>&2
return 1
fi
else
# There was an error in processing. Mention the --help option
echo "Use --help for more information" 1>&2
return 1
fi
return 0
}
# This is the only entry point for the parse_opts system.
# Parse the command line arguments to extract both long and short form
# options.
#
# This function is a wrapper for _parse_opts_process to handle accessing the
# arrays in the calling function's namespace
#
# NB. Locals in this function must begin _po_
parse_opts() {
# Ensure options are in a known & useful state
emulate -L zsh
setopt extended_glob
# Extract arguments passed to us
local _po_ext_results="" _po_ext_argv="" _po_config=""
[[ $argv[1] != -- ]] && _po_config=$argv[1] && shift
[[ $argv[1] != -- ]] && _po_ext_results=$argv[1] && shift
[[ $argv[1] != -- ]] && _po_ext_argv=$argv[1] && shift
[[ $argv[1] == -- ]] && shift
# Copy any current values of external arrays into our copies. This allows for
# default state in the options, etc.
local -a _po_argv
local -A _po_results
_po_argv=("$argv[@]")
[[ -n $_po_ext_argv ]] && _po_argv=("${(@P)_po_ext_argv}")
[[ -n $_po_ext_results ]] && _po_results=("${(@Pkv)_po_ext_results}")
# Do the work. Only update the external arrays if we succeeded and they exist
if _parse_opts_process $_po_config
then
[[ -n $_po_ext_argv ]] && eval $_po_ext_argv='("$_po_argv[@]")'
[[ -n $_po_ext_results ]] && eval $_po_ext_results='("${(@kv)_po_results}")'
else
return 1
fi
return 0
}
parse_opts "$argv[@]"
----------END parse_opts
----------BEGIN _parse_opts
# ZSH function file
# Automatic completer for functions which use parse_opts
# (C) 2001 Martin Ebourne
#
# See documentation with parse_opts for details on usage.
#
# $Id: _parse_opts,v 1.2 2001/08/17 13:31:44 mebourne Exp $
_parse_opts() {
local command="$words[1]"
# Return code is meaningless. We need to just check for stdout instead
local toeval="$(_call_program "$command" "$command" --zsh-completion 2>/dev/null)"
if [[ -n $toeval ]]
then
eval $toeval
else
_message "error executing '$command --zsh-completion'"
fi
}
_parse_opts "$argv[@]"
----------END _parse_opts
----------BEGIN yes
local -A opts
parse_opts - opts -- "$argv[@]" <<'EOF' || return 1
Description:
Repeatedly print a string
Usage:
yes [options] [<text> ...]
Options:
-e, --escape Interpret escape characters as for echo (default unless
the BSD_ECHO option is set)
# [echoopt] += --escape | -e : constant=-e
-E, --no-escape Prevent interpretation of escape characters
# [echoopt] += --no-escape | -E : constant=-E
-h, --help Provide this help
# --help | -h
-l <count>, --lines=<count> Output only count times
# --lines | -l : posinteger
-n, --no-newline Suppress output of automatic newline
# [echoopt] += --no-newline | -n : constant=-n
-s <seconds>, --sleep=<seconds>
Pause for number of seconds between each echo
# --sleep | -s : posinteger
Arguments:
[<text> ...] Optional text to print. Defaults to 'yes'
# [text] += [*] text
EOF
# Output as required
while (( ${opts[lines]:-1} ))
do
echo $=opts[echoopt] ${opts[text]:-yes}
(( opts[sleep] )) && sleep $opts[sleep]
(( opts[lines] && opts[lines]-- ))
done
----------END yes
This e-mail message is CONFIDENTIAL and may contain legally privileged
information. If you are not the intended recipient you should not read,
copy, distribute, disclose or otherwise use the information in this e-mail.
Please also telephone or fax us immediately and delete the message from
your system. E-mail may be susceptible to data corruption, interception
and unauthorised amendment, and we do not accept liability for any such
corruption, interception or amendment or the consequences thereof.
Messages sorted by:
Reverse Date,
Date,
Thread,
Author