source: tol/linux/shflags @ e8b0203

1941-docker-image-to-testdevel
Last change on this file since e8b0203 was 6ae58bb, checked in by Jorge Pérez <josp.jorge@…>, 15 years ago

script to compile and install tol in a similar way as uptol for windows

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@4396 be4846ae-f051-483a-831f-b64e7d08527f

  • Property mode set to 100644
File size: 30.4 KB
Line 
1# $Id: shflags 133 2009-05-10 18:04:51Z kate.ward@forestent.com $
2# vim:et:ft=sh:sts=2:sw=2
3#
4# Copyright 2008 Kate Ward. All Rights Reserved.
5# Released under the LGPL (GNU Lesser General Public License)
6#
7# shFlags -- Advanced command-line flag library for Unix shell scripts.
8# http://code.google.com/p/shflags/
9#
10# Author: kate.ward@forestent.com (Kate Ward)
11#
12# This module implements something like the google-gflags library available
13# from http://code.google.com/p/google-gflags/.
14#
15# FLAG TYPES: This is a list of the DEFINE_*'s that you can do.  All flags take
16# a name, default value, help-string, and optional 'short' name (one-letter
17# name).  Some flags have other arguments, which are described with the flag.
18#
19# DEFINE_string: takes any input, and intreprets it as a string.
20#
21# DEFINE_boolean: typically does not take any argument: say --myflag to set
22#   FLAGS_myflag to true, or --nomyflag to set FLAGS_myflag to false.
23#   Alternately, you can say
24#     --myflag=true  or --myflag=t or --myflag=0  or
25#     --myflag=false or --myflag=f or --myflag=1
26#   Passing an option has the same affect as passing the option once.
27#
28# DEFINE_float: takes an input and intreprets it as a floating point number. As
29#   shell does not support floats per-se, the input is merely validated as
30#   being a valid floating point value.
31#
32# DEFINE_integer: takes an input and intreprets it as an integer.
33#
34# SPECIAL FLAGS: There are a few flags that have special meaning:
35#   --help (or -?)  prints a list of all the flags in a human-readable fashion
36#   --flagfile=foo  read flags from foo.  (not implemented yet)
37#   --              as in getopt(), terminates flag-processing
38#
39# EXAMPLE USAGE:
40#
41#   -- begin hello.sh --
42#   #! /bin/sh
43#   . ./shflags
44#   DEFINE_string name 'world' "somebody's name" n
45#   FLAGS "$@" || exit $?
46#   eval set -- "${FLAGS_ARGV}"
47#   echo "Hello, ${FLAGS_name}."
48#   -- end hello.sh --
49#
50#   $ ./hello.sh -n Kate
51#   Hello, Kate.
52#
53# NOTE: Not all systems include a getopt version that supports long flags. On
54# these systems, only short flags are recognized.
55
56#==============================================================================
57# shFlags
58#
59# Shared attributes:
60#   flags_error: last error message
61#   flags_return: last return value
62#
63#   __flags_longNames: list of long names for all flags
64#   __flags_shortNames: list of short names for all flags
65#   __flags_boolNames: list of boolean flag names
66#
67#   __flags_opts: options parsed by getopt
68#
69# Per-flag attributes:
70#   FLAGS_<flag_name>: contains value of flag named 'flag_name'
71#   __flags_<flag_name>_default: the default flag value
72#   __flags_<flag_name>_help: the flag help string
73#   __flags_<flag_name>_short: the flag short name
74#   __flags_<flag_name>_type: the flag type
75#
76# Notes:
77# - lists of strings are space separated, and a null value is the '~' char.
78
79# return if FLAGS already loaded
80[ -n "${FLAGS_VERSION:-}" ] && return 0
81FLAGS_VERSION='1.0.3'
82
83# return values
84FLAGS_TRUE=0
85FLAGS_FALSE=1
86FLAGS_ERROR=2
87
88# reserved flag names
89FLAGS_RESERVED='ARGC ARGV ERROR FALSE HELP PARENT RESERVED TRUE VERSION'
90
91_flags_debug() { echo "flags:DEBUG $@" >&2; }
92_flags_warn() { echo "flags:WARN $@" >&2; }
93_flags_error() { echo "flags:ERROR $@" >&2; }
94_flags_fatal() { echo "flags:FATAL $@" >&2; }
95
96# specific shell checks
97if [ -n "${ZSH_VERSION:-}" ]; then
98  setopt |grep "^shwordsplit$" >/dev/null
99  if [ $? -ne ${FLAGS_TRUE} ]; then
100    _flags_fatal 'zsh shwordsplit option is required for proper zsh operation'
101    exit ${FLAGS_ERROR}
102  fi
103  if [ -z "${FLAGS_PARENT:-}" ]; then
104    _flags_fatal "zsh does not pass \$0 through properly. please declare' \
105\"FLAGS_PARENT=\$0\" before calling shFlags"
106    exit ${FLAGS_ERROR}
107  fi
108fi
109
110#
111# constants
112#
113
114# getopt version
115__FLAGS_GETOPT_VERS_STD=0
116__FLAGS_GETOPT_VERS_ENH=1
117__FLAGS_GETOPT_VERS_BSD=2
118
119getopt >/dev/null 2>&1
120case $? in
121  0) __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD} ;;  # bsd getopt
122  2)
123    # TODO(kward): look into '-T' option to test the internal getopt() version
124    if [ "`getopt --version`" = '-- ' ]; then
125      __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD}
126    else
127      __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_ENH}
128    fi
129    ;;
130  *)
131    _flags_fatal 'unable to determine getopt version'
132    exit ${FLAGS_ERROR}
133    ;;
134esac
135
136# getopt optstring lengths
137__FLAGS_OPTSTR_SHORT=0
138__FLAGS_OPTSTR_LONG=1
139
140__FLAGS_NULL='~'
141
142# flag info strings
143__FLAGS_INFO_DEFAULT='default'
144__FLAGS_INFO_HELP='help'
145__FLAGS_INFO_SHORT='short'
146__FLAGS_INFO_TYPE='type'
147
148# flag lengths
149__FLAGS_LEN_SHORT=0
150__FLAGS_LEN_LONG=1
151
152# flag types
153__FLAGS_TYPE_NONE=0
154__FLAGS_TYPE_BOOLEAN=1
155__FLAGS_TYPE_FLOAT=2
156__FLAGS_TYPE_INTEGER=3
157__FLAGS_TYPE_STRING=4
158
159# set the constants readonly
160__flags_constants=`set |awk -F= '/^FLAGS_/ || /^__FLAGS_/ {print $1}'`
161for __flags_const in ${__flags_constants}; do
162  # skip certain flags
163  case ${__flags_const} in
164    FLAGS_HELP) continue ;;
165    FLAGS_PARENT) continue ;;
166  esac
167  # set flag readonly
168  if [ -z "${ZSH_VERSION:-}" ]; then
169    readonly ${__flags_const}
170  else  # handle zsh
171    case ${ZSH_VERSION} in
172      [123].*) readonly ${__flags_const} ;;
173      *) readonly -g ${__flags_const} ;;  # declare readonly constants globally
174    esac
175  fi
176done
177unset __flags_const __flags_constants
178
179#
180# internal variables
181#
182
183__flags_boolNames=' '  # space separated list of boolean flag names
184__flags_longNames=' '  # space separated list of long flag names
185__flags_shortNames=' '  # space separated list of short flag names
186
187__flags_columns=''  # screen width in columns
188__flags_opts=''  # temporary storage for parsed getopt flags
189
190#------------------------------------------------------------------------------
191# private functions
192#
193
194# Define a flag.
195#
196# Calling this function will define the following info variables for the
197# specified flag:
198#   FLAGS_flagname - the name for this flag (based upon the long flag name)
199#   __flags_<flag_name>_default - the default value
200#   __flags_flagname_help - the help string
201#   __flags_flagname_short - the single letter alias
202#   __flags_flagname_type - the type of flag (one of __FLAGS_TYPE_*)
203#
204# Args:
205#   _flags__type: integer: internal type of flag (__FLAGS_TYPE_*)
206#   _flags__name: string: long flag name
207#   _flags__default: default flag value
208#   _flags__help: string: help string
209#   _flags__short: string: (optional) short flag name
210# Returns:
211#   integer: success of operation, or error
212_flags_define()
213{
214  if [ $# -lt 4 ]; then
215    flags_error='DEFINE error: too few arguments'
216    flags_return=${FLAGS_ERROR}
217    _flags_error "${flags_error}"
218    return ${flags_return}
219  fi
220
221  _flags_type_=$1
222  _flags_name_=$2
223  _flags_default_=$3
224  _flags_help_=$4
225  _flags_short_=${5:-${__FLAGS_NULL}}
226
227  _flags_return_=${FLAGS_TRUE}
228
229  # TODO(kward): check for validity of the flag name (e.g. dashes)
230
231  # check whether the flag name is reserved
232  echo " ${FLAGS_RESERVED} " |grep " ${_flags_name_} " >/dev/null
233  if [ $? -eq 0 ]; then
234    flags_error="flag name (${_flags_name_}) is reserved"
235    _flags_return_=${FLAGS_ERROR}
236  fi
237
238  # require short option for getopt that don't support long options
239  if [ ${_flags_return_} -eq ${FLAGS_TRUE} \
240      -a ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} \
241      -a "${_flags_short_}" = "${__FLAGS_NULL}" ]
242  then
243    flags_error="short flag required for (${_flags_name_}) on this platform"
244    _flags_return_=${FLAGS_ERROR}
245  fi
246
247  # check for existing long name definition
248  if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
249    if _flags_itemInList "${_flags_name_}" \
250        ${__flags_longNames} ${__flags_boolNames}
251    then
252      flags_error="flag name ([no]${_flags_name_}) already defined"
253      _flags_warn "${flags_error}"
254      _flags_return_=${FLAGS_FALSE}
255    fi
256  fi
257
258  # check for existing short name definition
259  if [ ${_flags_return_} -eq ${FLAGS_TRUE} \
260      -a "${_flags_short_}" != "${__FLAGS_NULL}" ]
261  then
262    if _flags_itemInList "${_flags_short_}" ${__flags_shortNames}; then
263      flags_error="flag short name (${_flags_short_}) already defined"
264      _flags_warn "${flags_error}"
265      _flags_return_=${FLAGS_FALSE}
266    fi
267  fi
268
269  # handle default value. note, on several occasions the 'if' portion of an
270  # if/then/else contains just a ':' which does nothing. a binary reversal via
271  # '!' is not done because it does not work on all shells.
272  if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
273    case ${_flags_type_} in
274      ${__FLAGS_TYPE_BOOLEAN})
275        if _flags_validateBoolean "${_flags_default_}"; then
276          case ${_flags_default_} in
277            true|t|0) _flags_default_=${FLAGS_TRUE} ;;
278            false|f|1) _flags_default_=${FLAGS_FALSE} ;;
279          esac
280        else
281          flags_error="invalid default flag value '${_flags_default_}'"
282          _flags_return_=${FLAGS_ERROR}
283        fi
284        ;;
285
286      ${__FLAGS_TYPE_FLOAT})
287        if _flags_validateFloat "${_flags_default_}"; then
288          :
289        else
290          flags_error="invalid default flag value '${_flags_default_}'"
291          _flags_return_=${FLAGS_ERROR}
292        fi
293        ;;
294
295      ${__FLAGS_TYPE_INTEGER})
296        if _flags_validateInteger "${_flags_default_}"; then
297          :
298        else
299          flags_error="invalid default flag value '${_flags_default_}'"
300          _flags_return_=${FLAGS_ERROR}
301        fi
302        ;;
303
304      ${__FLAGS_TYPE_STRING}) ;;  # everything in shell is a valid string
305
306      *)
307        flags_error="unrecognized flag type '${_flags_type_}'"
308        _flags_return_=${FLAGS_ERROR}
309        ;;
310    esac
311  fi
312
313  if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
314    # store flag information
315    eval "FLAGS_${_flags_name_}='${_flags_default_}'"
316    eval "__flags_${_flags_name_}_${__FLAGS_INFO_TYPE}=${_flags_type_}"
317    eval "__flags_${_flags_name_}_${__FLAGS_INFO_DEFAULT}=\
318\"${_flags_default_}\""
319    eval "__flags_${_flags_name_}_${__FLAGS_INFO_HELP}=\"${_flags_help_}\""
320    eval "__flags_${_flags_name_}_${__FLAGS_INFO_SHORT}='${_flags_short_}'"
321
322    # append flag name(s) to list of names
323    __flags_longNames="${__flags_longNames}${_flags_name_} "
324    __flags_shortNames="${__flags_shortNames}${_flags_short_} "
325    [ ${_flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} ] && \
326        __flags_boolNames="${__flags_boolNames}no${_flags_name_} "
327  fi
328
329  flags_return=${_flags_return_}
330  unset _flags_default_ _flags_help_ _flags_name_ _flags_return_ _flags_short_ \
331      _flags_type_
332  [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}"
333  return ${flags_return}
334}
335
336# Return valid getopt options using currently defined list of long options.
337#
338# This function builds a proper getopt option string for short (and long)
339# options, using the current list of long options for reference.
340#
341# Args:
342#   _flags_optStr: integer: option string type (__FLAGS_OPTSTR_*)
343# Output:
344#   string: generated option string for getopt
345# Returns:
346#   boolean: success of operation (always returns True)
347_flags_genOptStr()
348{
349  _flags_optStrType_=$1
350
351  _flags_opts_=''
352
353  for _flags_flag_ in ${__flags_longNames}; do
354    _flags_type_=`_flags_getFlagInfo ${_flags_flag_} ${__FLAGS_INFO_TYPE}`
355    case ${_flags_optStrType_} in
356      ${__FLAGS_OPTSTR_SHORT})
357        _flags_shortName_=`_flags_getFlagInfo \
358            ${_flags_flag_} ${__FLAGS_INFO_SHORT}`
359        if [ "${_flags_shortName_}" != "${__FLAGS_NULL}" ]; then
360          _flags_opts_="${_flags_opts_}${_flags_shortName_}"
361          # getopt needs a trailing ':' to indicate a required argument
362          [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \
363              _flags_opts_="${_flags_opts_}:"
364        fi
365        ;;
366
367      ${__FLAGS_OPTSTR_LONG})
368        _flags_opts_="${_flags_opts_:+${_flags_opts_},}${_flags_flag_}"
369        # getopt needs a trailing ':' to indicate a required argument
370        [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \
371            _flags_opts_="${_flags_opts_}:"
372        ;;
373    esac
374  done
375
376  echo "${_flags_opts_}"
377  unset _flags_flag_ _flags_opts_ _flags_optStrType_ _flags_shortName_ \
378      _flags_type_
379  return ${FLAGS_TRUE}
380}
381
382# Returns flag details based on a flag name and flag info.
383#
384# Args:
385#   string: long flag name
386#   string: flag info (see the _flags_define function for valid info types)
387# Output:
388#   string: value of dereferenced flag variable
389# Returns:
390#   integer: one of FLAGS_{TRUE|FALSE|ERROR}
391_flags_getFlagInfo()
392{
393  _flags_name_=$1
394  _flags_info_=$2
395
396  _flags_nameVar_="__flags_${_flags_name_}_${_flags_info_}"
397  _flags_strToEval_="_flags_value_=\"\${${_flags_nameVar_}:-}\""
398  eval "${_flags_strToEval_}"
399  if [ -n "${_flags_value_}" ]; then
400    flags_return=${FLAGS_TRUE}
401  else
402    # see if the _flags_name_ variable is a string as strings can be empty...
403    # note: the DRY principle would say to have this function call itself for
404    # the next three lines, but doing so results in an infinite loop as an
405    # invalid _flags_name_ will also not have the associated _type variable.
406    # Because it doesn't (it will evaluate to an empty string) the logic will
407    # try to find the _type variable of the _type variable, and so on. Not so
408    # good ;-)
409    _flags_typeVar_="__flags_${_flags_name_}_${__FLAGS_INFO_TYPE}"
410    _flags_strToEval_="_flags_type_=\"\${${_flags_typeVar_}:-}\""
411    eval "${_flags_strToEval_}"
412    if [ "${_flags_type_}" = "${__FLAGS_TYPE_STRING}" ]; then
413      flags_return=${FLAGS_TRUE}
414    else
415      flags_return=${FLAGS_ERROR}
416      flags_error="invalid flag name (${_flags_nameVar_})"
417    fi
418  fi
419
420  echo "${_flags_value_}"
421  unset _flags_info_ _flags_name_ _flags_strToEval_ _flags_type_ _flags_value_ \
422      _flags_nameVar_ _flags_typeVar_
423  [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}"
424  return ${flags_return}
425}
426
427# check for presense of item in a list. passed a string (e.g. 'abc'), this
428# function will determine if the string is present in the list of strings (e.g.
429# ' foo bar abc ').
430#
431# Args:
432#   _flags__str: string: string to search for in a list of strings
433#   unnamed: list: list of strings
434# Returns:
435#   boolean: true if item is in the list
436_flags_itemInList()
437{
438  _flags_str_=$1
439  shift
440
441  echo " ${*:-} " |grep " ${_flags_str_} " >/dev/null
442  if [ $? -eq 0 ]; then
443    flags_return=${FLAGS_TRUE}
444  else
445    flags_return=${FLAGS_FALSE}
446  fi
447
448  unset _flags_str_
449  return ${flags_return}
450}
451
452# Returns the width of the current screen.
453#
454# Output:
455#   integer: width in columns of the current screen.
456_flags_columns()
457{
458  if [ -z "${__flags_columns}" ]; then
459    # determine the value and store it
460    if eval stty size >/dev/null 2>&1; then
461      # stty size worked :-)
462      set -- `stty size`
463      __flags_columns=$2
464    elif eval tput cols >/dev/null 2>&1; then
465      set -- `tput cols`
466      __flags_columns=$1
467    else
468      __flags_columns=80  # default terminal width
469    fi
470  fi
471  echo ${__flags_columns}
472}
473
474# Validate a boolean.
475#
476# Args:
477#   _flags__bool: boolean: value to validate
478# Returns:
479#   bool: true if the value is a valid boolean
480_flags_validateBoolean()
481{
482  _flags_bool_=$1
483
484  flags_return=${FLAGS_TRUE}
485  case "${_flags_bool_}" in
486    true|t|0) ;;
487    false|f|1) ;;
488    *) flags_return=${FLAGS_FALSE} ;;
489  esac
490
491  unset _flags_bool_
492  return ${flags_return}
493}
494
495# Validate a float.
496#
497# Args:
498#   _flags__float: float: value to validate
499# Returns:
500#   bool: true if the value is a valid float
501_flags_validateFloat()
502{
503  _flags_float_=$1
504
505  if _flags_validateInteger ${_flags_float_}; then
506    flags_return=${FLAGS_TRUE}
507  else
508    flags_return=${FLAGS_TRUE}
509    case ${_flags_float_} in
510      -*)  # negative floats
511        _flags_test_=`expr "${_flags_float_}" : '\(-[0-9][0-9]*\.[0-9][0-9]*\)'`
512        ;;
513      *)  # positive floats
514        _flags_test_=`expr "${_flags_float_}" : '\([0-9][0-9]*\.[0-9][0-9]*\)'`
515        ;;
516    esac
517    [ "${_flags_test_}" != "${_flags_float_}" ] && flags_return=${FLAGS_FALSE}
518  fi
519
520  unset _flags_float_ _flags_test_
521  return ${flags_return}
522}
523
524# Validate an integer.
525#
526# Args:
527#   _flags__integer: interger: value to validate
528# Returns:
529#   bool: true if the value is a valid integer
530_flags_validateInteger()
531{
532  _flags_int_=$1
533
534  flags_return=${FLAGS_TRUE}
535  case ${_flags_int_} in
536    -*)  # negative ints
537      _flags_test_=`expr "${_flags_int_}" : '\(-[0-9][0-9]*\)'`
538      ;;
539    *)  # positive ints
540      _flags_test_=`expr "${_flags_int_}" : '\([0-9][0-9]*\)'`
541      ;;
542  esac
543  [ "${_flags_test_}" != "${_flags_int_}" ] && flags_return=${FLAGS_FALSE}
544
545  unset _flags_int_ _flags_test_
546  return ${flags_return}
547}
548
549# Parse command-line options using the standard getopt.
550#
551# Note: the flag options are passed around in the global __flags_opts so that
552# the formatting is not lost due to shell parsing and such.
553#
554# Args:
555#   @: varies: command-line options to parse
556# Returns:
557#   integer: a FLAGS success condition
558_flags_getoptStandard()
559{
560  flags_return=${FLAGS_TRUE}
561  _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}`
562
563  # check for spaces in passed options
564  for _flags_opt_ in "$@"; do
565    # note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06
566    _flags_match_=`echo "x${_flags_opt_}x" |sed 's/ //g'`
567    if [ "${_flags_match_}" != "x${_flags_opt_}x" ]; then
568      flags_error='the available getopt does not support spaces in options'
569      flags_return=${FLAGS_ERROR}
570      break
571    fi
572  done
573
574  if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then
575    __flags_opts=`getopt ${_flags_shortOpts_} $@ 2>&1`
576    _flags_rtrn_=$?
577    if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then
578      _flags_warn "${__flags_opts}"
579      flags_error='unable to parse provided options with getopt.'
580      flags_return=${FLAGS_ERROR}
581    fi
582  fi
583
584  unset _flags_match_ _flags_opt_ _flags_rtrn_ _flags_shortOpts_
585  return ${flags_return}
586}
587
588# Parse command-line options using the enhanced getopt.
589#
590# Note: the flag options are passed around in the global __flags_opts so that
591# the formatting is not lost due to shell parsing and such.
592#
593# Args:
594#   @: varies: command-line options to parse
595# Returns:
596#   integer: a FLAGS success condition
597_flags_getoptEnhanced()
598{
599  flags_return=${FLAGS_TRUE}
600  _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}`
601  _flags_boolOpts_=`echo "${__flags_boolNames}" \
602      |sed 's/^ *//;s/ *$//;s/ /,/g'`
603  _flags_longOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_LONG}`
604
605  __flags_opts=`getopt \
606      -o ${_flags_shortOpts_} \
607      -l "${_flags_longOpts_},${_flags_boolOpts_}" \
608      -- "$@" 2>&1`
609  _flags_rtrn_=$?
610  if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then
611    _flags_warn "${__flags_opts}"
612    flags_error='unable to parse provided options with getopt.'
613    flags_return=${FLAGS_ERROR}
614  fi
615
616  unset _flags_boolOpts_ _flags_longOpts_ _flags_rtrn_ _flags_shortOpts_
617  return ${flags_return}
618}
619
620# Dynamically parse a getopt result and set appropriate variables.
621#
622# This function does the actual conversion of getopt output and runs it through
623# the standard case structure for parsing. The case structure is actually quite
624# dynamic to support any number of flags.
625#
626# Args:
627#   argc: int: original command-line argument count
628#   @: varies: output from getopt parsing
629# Returns:
630#   integer: a FLAGS success condition
631_flags_parseGetopt()
632{
633  _flags_argc_=$1
634  shift
635
636  flags_return=${FLAGS_TRUE}
637
638  if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then
639    set -- $@
640  else
641    # note the quotes around the `$@' -- they are essential!
642    eval set -- "$@"
643  fi
644
645  # provide user with number of arguments to shift by later
646  # NOTE: the FLAGS_ARGC variable is obsolete as of 1.0.3 because it does not
647  # properly give user access to non-flag arguments mixed in between flag
648  # arguments. Its usage was replaced by FLAGS_ARGV, and it is being kept only
649  # for backwards compatibility reasons.
650  FLAGS_ARGC=`expr $# - 1 - ${_flags_argc_}`
651
652  # handle options. note options with values must do an additional shift
653  while true; do
654    _flags_opt_=$1
655    _flags_arg_=${2:-}
656    _flags_type_=${__FLAGS_TYPE_NONE}
657    _flags_name_=''
658
659    # determine long flag name
660    case "${_flags_opt_}" in
661      --) shift; break ;;  # discontinue option parsing
662
663      --*)  # long option
664        _flags_opt_=`expr "${_flags_opt_}" : '--\(.*\)'`
665        _flags_len_=${__FLAGS_LEN_LONG}
666        if _flags_itemInList "${_flags_opt_}" ${__flags_longNames}; then
667          _flags_name_=${_flags_opt_}
668        else
669          # check for negated long boolean version
670          if _flags_itemInList "${_flags_opt_}" ${__flags_boolNames}; then
671            _flags_name_=`expr "${_flags_opt_}" : 'no\(.*\)'`
672            _flags_type_=${__FLAGS_TYPE_BOOLEAN}
673            _flags_arg_=${__FLAGS_NULL}
674          fi
675        fi
676        ;;
677
678      -*)  # short option
679        _flags_opt_=`expr "${_flags_opt_}" : '-\(.*\)'`
680        _flags_len_=${__FLAGS_LEN_SHORT}
681        if _flags_itemInList "${_flags_opt_}" ${__flags_shortNames}; then
682          # yes. match short name to long name. note purposeful off-by-one
683          # (too high) with awk calculations.
684          _flags_pos_=`echo "${__flags_shortNames}" \
685              |awk 'BEGIN{RS=" ";rn=0}$0==e{rn=NR}END{print rn}' \
686                  e=${_flags_opt_}`
687          _flags_name_=`echo "${__flags_longNames}" \
688              |awk 'BEGIN{RS=" "}rn==NR{print $0}' rn="${_flags_pos_}"`
689        fi
690        ;;
691    esac
692
693    # die if the flag was unrecognized
694    if [ -z "${_flags_name_}" ]; then
695      flags_error="unrecognized option (${_flags_opt_})"
696      flags_return=${FLAGS_ERROR}
697      break
698    fi
699
700    # set new flag value
701    [ ${_flags_type_} -eq ${__FLAGS_TYPE_NONE} ] && \
702        _flags_type_=`_flags_getFlagInfo \
703            "${_flags_name_}" ${__FLAGS_INFO_TYPE}`
704    case ${_flags_type_} in
705      ${__FLAGS_TYPE_BOOLEAN})
706        if [ ${_flags_len_} -eq ${__FLAGS_LEN_LONG} ]; then
707          if [ "${_flags_arg_}" != "${__FLAGS_NULL}" ]; then
708            eval "FLAGS_${_flags_name_}=${FLAGS_TRUE}"
709          else
710            eval "FLAGS_${_flags_name_}=${FLAGS_FALSE}"
711          fi
712        else
713          _flags_strToEval_="_flags_val_=\
714\${__flags_${_flags_name_}_${__FLAGS_INFO_DEFAULT}}"
715          eval "${_flags_strToEval_}"
716          if [ ${_flags_val_} -eq ${FLAGS_FALSE} ]; then
717            eval "FLAGS_${_flags_name_}=${FLAGS_TRUE}"
718          else
719            eval "FLAGS_${_flags_name_}=${FLAGS_FALSE}"
720          fi
721        fi
722        ;;
723
724      ${__FLAGS_TYPE_FLOAT})
725        if _flags_validateFloat "${_flags_arg_}"; then
726          eval "FLAGS_${_flags_name_}='${_flags_arg_}'"
727        else
728          flags_error="invalid float value (${_flags_arg_})"
729          flags_return=${FLAGS_ERROR}
730          break
731        fi
732        ;;
733
734      ${__FLAGS_TYPE_INTEGER})
735        if _flags_validateInteger "${_flags_arg_}"; then
736          eval "FLAGS_${_flags_name_}='${_flags_arg_}'"
737        else
738          flags_error="invalid integer value (${_flags_arg_})"
739          flags_return=${FLAGS_ERROR}
740          break
741        fi
742        ;;
743
744      ${__FLAGS_TYPE_STRING})
745        eval "FLAGS_${_flags_name_}='${_flags_arg_}'"
746        ;;
747    esac
748
749    # handle special case help flag
750    if [ "${_flags_name_}" = 'help' ]; then
751      if [ ${FLAGS_help} -eq ${FLAGS_TRUE} ]; then
752        flags_help
753        flags_error='help requested'
754        flags_return=${FLAGS_FALSE}
755        break
756      fi
757    fi
758
759    # shift the option and non-boolean arguements out.
760    shift
761    [ ${_flags_type_} != ${__FLAGS_TYPE_BOOLEAN} ] && shift
762  done
763
764  # give user back non-flag arguments
765  FLAGS_ARGV=''
766  while [ $# -gt 0 ]; do
767    FLAGS_ARGV="${FLAGS_ARGV:+${FLAGS_ARGV} }'$1'"
768    shift
769  done
770
771  unset _flags_arg_ _flags_len_ _flags_name_ _flags_opt_ _flags_pos_ \
772      _flags_strToEval_ _flags_type_ _flags_val_
773  return ${flags_return}
774}
775
776#------------------------------------------------------------------------------
777# public functions
778#
779
780# A basic boolean flag. Boolean flags do not take any arguments, and their
781# value is either 1 (false) or 0 (true). For long flags, the false value is
782# specified on the command line by prepending the word 'no'. With short flags,
783# the presense of the flag toggles the current value between true and false.
784# Specifying a short boolean flag twice on the command results in returning the
785# value back to the default value.
786#
787# A default value is required for boolean flags.
788#
789# For example, lets say a Boolean flag was created whose long name was 'update'
790# and whose short name was 'x', and the default value was 'false'. This flag
791# could be explicitly set to 'true' with '--update' or by '-x', and it could be
792# explicitly set to 'false' with '--noupdate'.
793DEFINE_boolean() { _flags_define ${__FLAGS_TYPE_BOOLEAN} "$@"; }
794
795# Other basic flags.
796DEFINE_float()   { _flags_define ${__FLAGS_TYPE_FLOAT} "$@"; }
797DEFINE_integer() { _flags_define ${__FLAGS_TYPE_INTEGER} "$@"; }
798DEFINE_string()  { _flags_define ${__FLAGS_TYPE_STRING} "$@"; }
799
800# Parse the flags.
801#
802# Args:
803#   unnamed: list: command-line flags to parse
804# Returns:
805#   integer: success of operation, or error
806FLAGS()
807{
808  # define a standard 'help' flag if one isn't already defined
809  [ -z "${__flags_help_type:-}" ] && \
810      DEFINE_boolean 'help' false 'show this help' 'h'
811
812  # parse options
813  if [ $# -gt 0 ]; then
814    if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then
815      _flags_getoptStandard "$@"
816    else
817      _flags_getoptEnhanced "$@"
818    fi
819    flags_return=$?
820  else
821    # nothing passed; won't bother running getopt
822    __flags_opts='--'
823    flags_return=${FLAGS_TRUE}
824  fi
825
826  if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then
827    _flags_parseGetopt $# "${__flags_opts}"
828    flags_return=$?
829  fi
830
831  [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_fatal "${flags_error}"
832  return ${flags_return}
833}
834
835# This is a helper function for determining the `getopt` version for platforms
836# where the detection isn't working. It simply outputs debug information that
837# can be included in a bug report.
838#
839# Args:
840#   none
841# Output:
842#   debug info that can be included in a bug report
843# Returns:
844#   nothing
845flags_getoptInfo()
846{
847  # platform info
848  _flags_debug "uname -a: `uname -a`"
849  _flags_debug "PATH: ${PATH}"
850
851  # shell info
852  if [ -n "${BASH_VERSION:-}" ]; then
853    _flags_debug 'shell: bash'
854    _flags_debug "BASH_VERSION: ${BASH_VERSION}"
855  elif [ -n "${ZSH_VERSION:-}" ]; then
856    _flags_debug 'shell: zsh'
857    _flags_debug "ZSH_VERSION: ${ZSH_VERSION}"
858  fi
859
860  # getopt info
861  getopt >/dev/null
862  _flags_getoptReturn=$?
863  _flags_debug "getopt return: ${_flags_getoptReturn}"
864  _flags_debug "getopt --version: `getopt --version 2>&1`"
865
866  unset _flags_getoptReturn
867}
868
869# Returns whether the detected getopt version is the enhanced version.
870#
871# Args:
872#   none
873# Output:
874#   none
875# Returns:
876#   bool: true if getopt is the enhanced version
877flags_getoptIsEnh()
878{
879  test ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_ENH}
880}
881
882# Returns whether the detected getopt version is the standard version.
883#
884# Args:
885#   none
886# Returns:
887#   bool: true if getopt is the standard version
888flags_getoptIsStd()
889{
890  test ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_STD}
891}
892
893# This is effectively a 'usage()' function. It prints usage information and
894# exits the program with ${FLAGS_FALSE} if it is ever found in the command line
895# arguments. Note this function can be overridden so other apps can define
896# their own --help flag, replacing this one, if they want.
897#
898# Args:
899#   none
900# Returns:
901#   integer: success of operation (always returns true)
902flags_help()
903{
904  if [ -n "${FLAGS_HELP:-}" ]; then
905    echo "${FLAGS_HELP}" >&2
906  else
907    echo "USAGE: ${FLAGS_PARENT:-$0} [flags] args" >&2
908  fi
909  if [ -n "${__flags_longNames}" ]; then
910    echo 'flags:' >&2
911    for flags_name_ in ${__flags_longNames}; do
912      flags_flagStr_=''
913      flags_boolStr_=''
914
915      flags_default_=`_flags_getFlagInfo \
916          "${flags_name_}" ${__FLAGS_INFO_DEFAULT}`
917      flags_help_=`_flags_getFlagInfo \
918          "${flags_name_}" ${__FLAGS_INFO_HELP}`
919      flags_short_=`_flags_getFlagInfo \
920          "${flags_name_}" ${__FLAGS_INFO_SHORT}`
921      flags_type_=`_flags_getFlagInfo \
922          "${flags_name_}" ${__FLAGS_INFO_TYPE}`
923
924      [ "${flags_short_}" != "${__FLAGS_NULL}" ] \
925          && flags_flagStr_="-${flags_short_}"
926
927      if [ ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_ENH} ]; then
928        [ "${flags_short_}" != "${__FLAGS_NULL}" ] \
929            && flags_flagStr_="${flags_flagStr_},"
930        [ ${flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} ] \
931            && flags_boolStr_='[no]'
932        flags_flagStr_="${flags_flagStr_}--${flags_boolStr_}${flags_name_}:"
933      fi
934
935      case ${flags_type_} in
936        ${__FLAGS_TYPE_BOOLEAN})
937          if [ ${flags_default_} -eq ${FLAGS_TRUE} ]; then
938            flags_defaultStr_='true'
939          else
940            flags_defaultStr_='false'
941          fi
942          ;;
943        ${__FLAGS_TYPE_FLOAT}|${__FLAGS_TYPE_INTEGER})
944          flags_defaultStr_=${flags_default_} ;;
945        ${__FLAGS_TYPE_STRING}) flags_defaultStr_="'${flags_default_}'" ;;
946      esac
947      flags_defaultStr_="(default: ${flags_defaultStr_})"
948
949      flags_helpStr_="  ${flags_flagStr_}  ${flags_help_} ${flags_defaultStr_}"
950      flags_helpStrLen_=`expr "${flags_helpStr_}" : '.*'`
951      flags_columns_=`_flags_columns`
952      if [ ${flags_helpStrLen_} -lt ${flags_columns_} ]; then
953        echo "${flags_helpStr_}" >&2
954      else
955        echo "  ${flags_flagStr_}  ${flags_help_}" >&2
956        # note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06
957        # because it doesn't like empty strings when used in this manner.
958        flags_emptyStr_="`echo \"x${flags_flagStr_}x\" \
959            |awk '{printf "%"length($0)-2"s", ""}'`"
960        flags_helpStr_="  ${flags_emptyStr_}  ${flags_defaultStr_}"
961        flags_helpStrLen_=`expr "${flags_helpStr_}" : '.*'`
962        if [ ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_STD} \
963            -o ${flags_helpStrLen_} -lt ${flags_columns_} ]; then
964          # indented to match help string
965          echo "${flags_helpStr_}" >&2
966        else
967          # indented four from left to allow for longer defaults as long flag
968          # names might be used too, making things too long
969          echo "    ${flags_defaultStr_}" >&2
970        fi
971      fi
972    done
973  fi
974
975  unset flags_boolStr_ flags_default_ flags_defaultStr_ flags_emptyStr_ \
976      flags_flagStr_ flags_help_ flags_helpStr flags_helpStrLen flags_name_ \
977      flags_columns_ flags_short_ flags_type_
978  return ${FLAGS_TRUE}
979}
980
981# Reset shflags back to an uninitialized state.
982#
983# Args:
984#   none
985# Returns:
986#   nothing
987flags_reset()
988{
989  for flags_name_ in ${__flags_longNames}; do
990    flags_strToEval_="unset FLAGS_${flags_name_}"
991    for flags_type_ in \
992        ${__FLAGS_INFO_DEFAULT} \
993        ${__FLAGS_INFO_HELP} \
994        ${__FLAGS_INFO_SHORT} \
995        ${__FLAGS_INFO_TYPE}
996    do
997      flags_strToEval_=\
998"${flags_strToEval_} __flags_${flags_name_}_${flags_type_}"
999    done
1000    eval ${flags_strToEval_}
1001  done
1002
1003  # reset internal variables
1004  __flags_boolNames=' '
1005  __flags_longNames=' '
1006  __flags_shortNames=' '
1007
1008  unset flags_name_ flags_type_ flags_strToEval_
1009}
Note: See TracBrowser for help on using the repository browser.