maint: bump copyright year
[m4.git] / build-aux / gl / build-aux / options-parser
blobdd9867f21f7e62408327ac1a9dd811e7b528cb68
1 #! /bin/sh
3 # Set a version string for this script.
4 scriptversion=2014-01-07.03; # UTC
6 # A portable, pluggable option parser for Bourne shell.
7 # Written by Gary V. Vaughan, 2010
9 # Copyright (C) 2010-2014, 2017 Free Software Foundation, Inc.
10 # This is free software; see the source for copying conditions. There is NO
11 # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # Please report bugs or propose patches to gary@gnu.org.
29 ## ------ ##
30 ## Usage. ##
31 ## ------ ##
33 # This file is a library for parsing options in your shell scripts along
34 # with assorted other useful supporting features that you can make use
35 # of too.
37 # For the simplest scripts you might need only:
39 # #!/bin/sh
40 # . relative/path/to/funclib.sh
41 # . relative/path/to/options-parser
42 # scriptversion=1.0
43 # func_options ${1+"$@"}
44 # eval set dummy "$func_options_result"; shift
45 # ...rest of your script...
47 # In order for the '--version' option to work, you will need to have a
48 # suitably formatted comment like the one at the top of this file
49 # starting with '# Written by ' and ending with '# warranty; '.
51 # For '-h' and '--help' to work, you will also need a one line
52 # description of your script's purpose in a comment directly above the
53 # '# Written by ' line, like the one at the top of this file.
55 # The default options also support '--debug', which will turn on shell
56 # execution tracing (see the comment above debug_cmd below for another
57 # use), and '--verbose' and the func_verbose function to allow your script
58 # to display verbose messages only when your user has specified
59 # '--verbose'.
61 # After sourcing this file, you can plug processing for additional
62 # options by amending the variables from the 'Configuration' section
63 # below, and following the instructions in the 'Option parsing'
64 # section further down.
66 ## -------------- ##
67 ## Configuration. ##
68 ## -------------- ##
70 # You should override these variables in your script after sourcing this
71 # file so that they reflect the customisations you have added to the
72 # option parser.
74 # The usage line for option parsing errors and the start of '-h' and
75 # '--help' output messages. You can embed shell variables for delayed
76 # expansion at the time the message is displayed, but you will need to
77 # quote other shell meta-characters carefully to prevent them being
78 # expanded when the contents are evaled.
79 usage='$progpath [OPTION]...'
81 # Short help message in response to '-h' and '--help'. Add to this or
82 # override it after sourcing this library to reflect the full set of
83 # options your script accepts.
84 usage_message="\
85 --debug enable verbose shell tracing
86 -W, --warnings=CATEGORY
87 report the warnings falling in CATEGORY [all]
88 -v, --verbose verbosely report processing
89 --version print version information and exit
90 -h, --help print short or long help message and exit
93 # Additional text appended to 'usage_message' in response to '--help'.
94 long_help_message="
95 Warning categories include:
96 'all' show all warnings
97 'none' turn off all the warnings
98 'error' warnings are treated as fatal errors"
100 # Help message printed before fatal option parsing errors.
101 fatal_help="Try '\$progname --help' for more information."
105 ## ------------------------- ##
106 ## Hook function management. ##
107 ## ------------------------- ##
109 # This section contains functions for adding, removing, and running hooks
110 # to the main code. A hook is just a named list of of function, that can
111 # be run in order later on.
113 # func_hookable FUNC_NAME
114 # -----------------------
115 # Declare that FUNC_NAME will run hooks added with
116 # 'func_add_hook FUNC_NAME ...'.
117 func_hookable ()
119 $debug_cmd
121 func_append hookable_fns " $1"
125 # func_add_hook FUNC_NAME HOOK_FUNC
126 # ---------------------------------
127 # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must
128 # first have been declared "hookable" by a call to 'func_hookable'.
129 func_add_hook ()
131 $debug_cmd
133 case " $hookable_fns " in
134 *" $1 "*) ;;
135 *) func_fatal_error "'$1' does not accept hook functions." ;;
136 esac
138 eval func_append ${1}_hooks '" $2"'
142 # func_remove_hook FUNC_NAME HOOK_FUNC
143 # ------------------------------------
144 # Remove HOOK_FUNC from the list of functions called by FUNC_NAME.
145 func_remove_hook ()
147 $debug_cmd
149 eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`'
153 # func_run_hooks FUNC_NAME [ARG]...
154 # ---------------------------------
155 # Run all hook functions registered to FUNC_NAME.
156 # It is assumed that the list of hook functions contains nothing more
157 # than a whitespace-delimited list of legal shell function names, and
158 # no effort is wasted trying to catch shell meta-characters or preserve
159 # whitespace.
160 func_run_hooks ()
162 $debug_cmd
164 case " $hookable_fns " in
165 *" $1 "*) ;;
166 *) func_fatal_error "'$1' does not support hook funcions.n" ;;
167 esac
169 eval _G_hook_fns=\$$1_hooks; shift
171 for _G_hook in $_G_hook_fns; do
172 eval $_G_hook '"$@"'
174 # store returned options list back into positional
175 # parameters for next 'cmd' execution.
176 eval _G_hook_result=\$${_G_hook}_result
177 eval set dummy "$_G_hook_result"; shift
178 done
180 func_quote_for_eval ${1+"$@"}
181 func_run_hooks_result=$func_quote_for_eval_result
186 ## --------------- ##
187 ## Option parsing. ##
188 ## --------------- ##
190 # In order to add your own option parsing hooks, you must accept the
191 # full positional parameter list in your hook function, remove any
192 # options that you action, and then pass back the remaining unprocessed
193 # options in '<hooked_function_name>_result', escaped suitably for
194 # 'eval'. Like this:
196 # my_options_prep ()
198 # $debug_cmd
200 # # Extend the existing usage message.
201 # usage_message=$usage_message'
202 # -s, --silent don'\''t print informational messages
205 # func_quote_for_eval ${1+"$@"}
206 # my_options_prep_result=$func_quote_for_eval_result
208 # func_add_hook func_options_prep my_options_prep
211 # my_silent_option ()
213 # $debug_cmd
215 # # Note that for efficiency, we parse as many options as we can
216 # # recognise in a loop before passing the remainder back to the
217 # # caller on the first unrecognised argument we encounter.
218 # while test $# -gt 0; do
219 # opt=$1; shift
220 # case $opt in
221 # --silent|-s) opt_silent=: ;;
222 # # Separate non-argument short options:
223 # -s*) func_split_short_opt "$_G_opt"
224 # set dummy "$func_split_short_opt_name" \
225 # "-$func_split_short_opt_arg" ${1+"$@"}
226 # shift
227 # ;;
228 # *) set dummy "$_G_opt" "$*"; shift; break ;;
229 # esac
230 # done
232 # func_quote_for_eval ${1+"$@"}
233 # my_silent_option_result=$func_quote_for_eval_result
235 # func_add_hook func_parse_options my_silent_option
238 # my_option_validation ()
240 # $debug_cmd
242 # $opt_silent && $opt_verbose && func_fatal_help "\
243 # '--silent' and '--verbose' options are mutually exclusive."
245 # func_quote_for_eval ${1+"$@"}
246 # my_option_validation_result=$func_quote_for_eval_result
248 # func_add_hook func_validate_options my_option_validation
250 # You'll alse need to manually amend $usage_message to reflect the extra
251 # options you parse. It's preferable to append if you can, so that
252 # multiple option parsing hooks can be added safely.
255 # func_options [ARG]...
256 # ---------------------
257 # All the functions called inside func_options are hookable. See the
258 # individual implementations for details.
259 func_hookable func_options
260 func_options ()
262 $debug_cmd
264 func_options_prep ${1+"$@"}
265 eval func_parse_options \
266 ${func_options_prep_result+"$func_options_prep_result"}
267 eval func_validate_options \
268 ${func_parse_options_result+"$func_parse_options_result"}
270 eval func_run_hooks func_options \
271 ${func_validate_options_result+"$func_validate_options_result"}
273 # save modified positional parameters for caller
274 func_options_result=$func_run_hooks_result
278 # func_options_prep [ARG]...
279 # --------------------------
280 # All initialisations required before starting the option parse loop.
281 # Note that when calling hook functions, we pass through the list of
282 # positional parameters. If a hook function modifies that list, and
283 # needs to propogate that back to rest of this script, then the complete
284 # modified list must be put in 'func_run_hooks_result' before
285 # returning.
286 func_hookable func_options_prep
287 func_options_prep ()
289 $debug_cmd
291 # Option defaults:
292 opt_verbose=false
293 opt_warning_types=
295 func_run_hooks func_options_prep ${1+"$@"}
297 # save modified positional parameters for caller
298 func_options_prep_result=$func_run_hooks_result
302 # func_parse_options [ARG]...
303 # ---------------------------
304 # The main option parsing loop.
305 func_hookable func_parse_options
306 func_parse_options ()
308 $debug_cmd
310 func_parse_options_result=
312 # this just eases exit handling
313 while test $# -gt 0; do
314 # Defer to hook functions for initial option parsing, so they
315 # get priority in the event of reusing an option name.
316 func_run_hooks func_parse_options ${1+"$@"}
318 # Adjust func_parse_options positional parameters to match
319 eval set dummy "$func_run_hooks_result"; shift
321 # Break out of the loop if we already parsed every option.
322 test $# -gt 0 || break
324 _G_opt=$1
325 shift
326 case $_G_opt in
327 --debug|-x) debug_cmd='set -x'
328 func_echo "enabling shell trace mode"
329 $debug_cmd
332 --no-warnings|--no-warning|--no-warn)
333 set dummy --warnings none ${1+"$@"}
334 shift
337 --warnings|--warning|-W)
338 test $# = 0 && func_missing_arg $_G_opt && break
339 case " $warning_categories $1" in
340 *" $1 "*)
341 # trailing space prevents matching last $1 above
342 func_append_uniq opt_warning_types " $1"
344 *all)
345 opt_warning_types=$warning_categories
347 *none)
348 opt_warning_types=none
349 warning_func=:
351 *error)
352 opt_warning_types=$warning_categories
353 warning_func=func_fatal_error
356 func_fatal_error \
357 "unsupported warning category: '$1'"
359 esac
360 shift
363 --verbose|-v) opt_verbose=: ;;
364 --version) func_version ;;
365 -\?|-h) func_usage ;;
366 --help) func_help ;;
368 # Separate optargs to long options (plugins may need this):
369 --*=*) func_split_equals "$_G_opt"
370 set dummy "$func_split_equals_lhs" \
371 "$func_split_equals_rhs" ${1+"$@"}
372 shift
375 # Separate optargs to short options:
376 -W*)
377 func_split_short_opt "$_G_opt"
378 set dummy "$func_split_short_opt_name" \
379 "$func_split_short_opt_arg" ${1+"$@"}
380 shift
383 # Separate non-argument short options:
384 -\?*|-h*|-v*|-x*)
385 func_split_short_opt "$_G_opt"
386 set dummy "$func_split_short_opt_name" \
387 "-$func_split_short_opt_arg" ${1+"$@"}
388 shift
391 --) break ;;
392 -*) func_fatal_help "unrecognised option: '$_G_opt'" ;;
393 *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
394 esac
395 done
397 # save modified positional parameters for caller
398 func_quote_for_eval ${1+"$@"}
399 func_parse_options_result=$func_quote_for_eval_result
403 # func_validate_options [ARG]...
404 # ------------------------------
405 # Perform any sanity checks on option settings and/or unconsumed
406 # arguments.
407 func_hookable func_validate_options
408 func_validate_options ()
410 $debug_cmd
412 # Display all warnings if -W was not given.
413 test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
415 func_run_hooks func_validate_options ${1+"$@"}
417 # Bail if the options were screwed!
418 $exit_cmd $EXIT_FAILURE
420 # save modified positional parameters for caller
421 func_validate_options_result=$func_run_hooks_result
426 ## ----------------- ##
427 ## Helper functions. ##
428 ## ----------------- ##
430 # This section contains the helper functions used by the rest of the
431 # hookable option parser framework in ascii-betical order.
434 # func_fatal_help ARG...
435 # ----------------------
436 # Echo program name prefixed message to standard error, followed by
437 # a help hint, and exit.
438 func_fatal_help ()
440 $debug_cmd
442 eval \$ECHO \""Usage: $usage"\"
443 eval \$ECHO \""$fatal_help"\"
444 func_error ${1+"$@"}
445 exit $EXIT_FAILURE
449 # func_help
450 # ---------
451 # Echo long help message to standard output and exit.
452 func_help ()
454 $debug_cmd
456 func_usage_message
457 $ECHO "$long_help_message"
458 exit 0
462 # func_missing_arg ARGNAME
463 # ------------------------
464 # Echo program name prefixed message to standard error and set global
465 # exit_cmd.
466 func_missing_arg ()
468 $debug_cmd
470 func_error "Missing argument for '$1'."
471 exit_cmd=exit
475 # func_split_equals STRING
476 # ------------------------
477 # Set func_split_equals_lhs and func_split_equals_rhs shell variables after
478 # splitting STRING at the '=' sign.
479 test -z "$_G_HAVE_XSI_OPS" \
480 && (eval 'x=a/b/c;
481 test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
482 && _G_HAVE_XSI_OPS=yes
484 if test yes = "$_G_HAVE_XSI_OPS"
485 then
486 # This is an XSI compatible shell, allowing a faster implementation...
487 eval 'func_split_equals ()
489 $debug_cmd
491 func_split_equals_lhs=${1%%=*}
492 func_split_equals_rhs=${1#*=}
493 test "x$func_split_equals_lhs" = "x$1" \
494 && func_split_equals_rhs=
496 else
497 # ...otherwise fall back to using expr, which is often a shell builtin.
498 func_split_equals ()
500 $debug_cmd
502 func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'`
503 func_split_equals_rhs=
504 test "x$func_split_equals_lhs" = "x$1" \
505 || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'`
507 fi #func_split_equals
510 # func_split_short_opt SHORTOPT
511 # -----------------------------
512 # Set func_split_short_opt_name and func_split_short_opt_arg shell
513 # variables after splitting SHORTOPT after the 2nd character.
514 if test yes = "$_G_HAVE_XSI_OPS"
515 then
516 # This is an XSI compatible shell, allowing a faster implementation...
517 eval 'func_split_short_opt ()
519 $debug_cmd
521 func_split_short_opt_arg=${1#??}
522 func_split_short_opt_name=${1%"$func_split_short_opt_arg"}
524 else
525 # ...otherwise fall back to using expr, which is often a shell builtin.
526 func_split_short_opt ()
528 $debug_cmd
530 func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'`
531 func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'`
533 fi #func_split_short_opt
536 # func_usage
537 # ----------
538 # Echo short help message to standard output and exit.
539 func_usage ()
541 $debug_cmd
543 func_usage_message
544 $ECHO "Run '$progname --help |${PAGER-more}' for full usage"
545 exit 0
549 # func_usage_message
550 # ------------------
551 # Echo short help message to standard output.
552 func_usage_message ()
554 $debug_cmd
556 eval \$ECHO \""Usage: $usage"\"
557 echo
558 $SED -n 's|^# ||
559 /^Written by/{
560 x;p;x
563 /^Written by/q' < "$progpath"
564 echo
565 eval \$ECHO \""$usage_message"\"
569 # func_version
570 # ------------
571 # Echo version message to standard output and exit.
572 func_version ()
574 $debug_cmd
576 printf '%s\n' "$progname $scriptversion"
577 $SED -n '
578 /(C)/!b go
579 :more
580 /\./!{
582 s|\n# | |
583 b more
586 /^# Written by /,/# warranty; / {
587 s|^# ||
588 s|^# *$||
589 s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
592 /^# Written by / {
593 s|^# ||
596 /^warranty; /q' < "$progpath"
598 exit $?
602 # Local variables:
603 # mode: shell-script
604 # sh-indentation: 2
605 # eval: (add-hook 'before-save-hook 'time-stamp)
606 # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
607 # time-stamp-time-zone: "UTC"
608 # End: