29
29
#
30
30
# Value-accepting argument definers allow these additional options to pre-filter
31
31
# a value, before it gets set or passed to a `--call`:
32
- # * `--filter=<name>` -- Calls the named function passing it a single argument
33
- # value; the function must output a replacement value. If the call fails, the
34
- # argument is rejected. Note: The filter function runs in a subshell, and as
35
- # such it cannot be used to affect the global environment of the main script.
32
+ # * `--filter=<name>` or `filter={code}` -- Calls the named function passing it
33
+ # a single argument value, or runs the indicated code snippet. The function
34
+ # or snippet must output a replacement value. If the call fails, the argument
35
+ # is rejected. Note: The filter runs in a subshell, and as such it cannot be
36
+ # used to affect the global environment of the main script.
36
37
# * `--filter=/<regex>/` -- Matches each argument value against the regex. If
37
38
# the regex doesn't match, the argument is rejected.
38
- # * `--enum=<spec>` -- Matches each argument value against a set of valid names.
39
- # `<spec>` must be a space-separated list of names, e.g. `--enum='yes no
40
- # maybe'`.
39
+ # * `--enum[] =<spec>` -- Matches each argument value against a set of valid
40
+ # names. `<spec>` must be a non-empty list of values, in the usual multi-value
41
+ # form accepted by this system, e.g. `--enum[]='yes no " maybe so" '`.
41
42
#
42
43
# Some argument-definers also accept these options:
43
44
# * `--default=<value>` -- Specifies a default value for an argument or option
@@ -151,7 +152,7 @@ function opt-multi {
151
152
local optRequired=0
152
153
local optVar=' '
153
154
local args=(" $@ " )
154
- _argproc_janky-args call enum filter required var \
155
+ _argproc_janky-args call enum[] filter required var \
155
156
|| return 1
156
157
157
158
local specName=' '
@@ -218,7 +219,7 @@ function opt-value {
218
219
local optRequired=0
219
220
local optVar=' '
220
221
local args=(" $@ " )
221
- _argproc_janky-args call default enum filter required var \
222
+ _argproc_janky-args call default enum[] filter required var \
222
223
|| return 1
223
224
224
225
local specName=' '
@@ -252,7 +253,7 @@ function positional-arg {
252
253
local optRequired=0
253
254
local optVar=' '
254
255
local args=(" $@ " )
255
- _argproc_janky-args call default enum filter required var \
256
+ _argproc_janky-args call default enum[] filter required var \
256
257
|| return 1
257
258
258
259
local specName=' '
@@ -361,7 +362,7 @@ function rest-arg {
361
362
local optFilter=' '
362
363
local optVar=' '
363
364
local args=(" $@ " )
364
- _argproc_janky-args call enum filter var \
365
+ _argproc_janky-args call enum[] filter var \
365
366
|| return 1
366
367
367
368
local specName=' '
@@ -658,6 +659,31 @@ function _argproc_error-coda {
658
659
fi
659
660
}
660
661
662
+ # Helper (called by code produced by `_argproc_handler-body`) which performs
663
+ # a filter call. Upon success, prints all the filtered values.
664
+ function _argproc_filter-call {
665
+ local desc=" $1 "
666
+ local filter=" $2 "
667
+ shift 2
668
+
669
+ if [[ ${filter} =~ ^\{ (.* )\} $ ]]; then
670
+ # Kinda gross, but this makes it easy to call the filter code block.
671
+ eval " function _argproc_filter-call:inner {
672
+ ${BASH_REMATCH[1]}
673
+ }"
674
+ filter=' _argproc_filter-call:inner'
675
+ fi
676
+
677
+ local arg result
678
+ for arg in " $@ " ; do
679
+ if ! result=(" $( " ${filter} " " ${arg} " ) " ); then
680
+ error-msg " Invalid value for ${desc} : ${arg} "
681
+ return 1
682
+ fi
683
+ vals -- " ${result} "
684
+ done
685
+ }
686
+
661
687
# Produces an argument handler body, from the given components.
662
688
function _argproc_handler-body {
663
689
local specName=" $1 "
@@ -675,16 +701,20 @@ function _argproc_handler-body {
675
701
" ${desc} " " ${filter} "
676
702
) " )
677
703
elif [[ ${filter} != ' ' ]]; then
678
- # Add a loop to call the filter function on each argument.
679
- result+=(
680
- " $( printf '
681
- local _argproc_value _argproc_args=()
682
- for _argproc_value in "$@"; do
683
- _argproc_args+=("$(%s "${_argproc_value}")") || return 1
684
- done
685
- set -- "${_argproc_args[@]}"' \
686
- " ${filter} " ) "
687
- )
704
+ # Add a call to perform the filtering.
705
+ local desc=" $( _argproc_arg-description " ${specName} " ) "
706
+ result+=(" $( printf '
707
+ local _argproc_args
708
+ _argproc_args="$(_argproc_filter-call %q %q "$@")" \
709
+ && set-array-from-vals _argproc_args "${_argproc_args}" \
710
+ || {
711
+ local error="$?"
712
+ error-msg --suppress-cmd
713
+ return "${error}"
714
+ }
715
+ set -- "${_argproc_args[@]}"' \
716
+ " ${desc} " " ${filter} "
717
+ ) " )
688
718
fi
689
719
690
720
if [[ ${callFunc} =~ ^\{ (.* )\} $ ]]; then
@@ -733,14 +763,19 @@ function _argproc_janky-args {
733
763
local gotDefault=0
734
764
local a
735
765
766
+ # TEMP: Remove spec mod once use sites are migrated.
767
+ if [[ ${argSpecs} =~ ' enum[] ' ]]; then
768
+ argSpecs+=' enum '
769
+ fi
770
+
736
771
for a in " ${args[@]} " ; do
737
772
if (( optsDone )) ; then
738
773
args+=(" ${a} " )
739
774
continue
740
775
fi
741
776
742
777
if [[ ${a} =~ ^--. ]]; then
743
- if ! [[ ${a} =~ ^--([a-z][-a-z]+)(=.* )? $ ]]; then
778
+ if ! [[ ${a} =~ ^--([a-z][-a-z]+\[ ? \] ? )(=.* )? $ ]]; then
744
779
error-msg --file-line=2 " Invalid option syntax: ${a} "
745
780
_argproc_declarationError=1
746
781
return 1
@@ -767,25 +802,14 @@ function _argproc_janky-args {
767
802
&& optDefault=" ${BASH_REMATCH[1]} " \
768
803
|| argError=1
769
804
;;
770
- enum)
771
- if [[ ${value} =~ ^= (' ' * ([-_:.a-zA-Z0-9]+' ' * )+)$ ]]; then
772
- # Re-form as a filter expression.
773
- value=" ${BASH_REMATCH[1]} "
774
- optFilter=' '
775
- while [[ ${value} =~ ^' ' * ([^ ]+)' ' * (.* )$ ]]; do
776
- optFilter+=" |${BASH_REMATCH[1]} "
777
- value=" ${BASH_REMATCH[2]} "
778
- done
779
- # `:1` to drop the initial `|`.
780
- optFilter=" /^(${optFilter: 1} )\$ /"
781
- # "Escape" `.` so it's not treated as regex syntax.
782
- optFilter=" ${optFilter// ./ [.]} "
783
- else
805
+ # TEMP: Remove plain `enum` once use sites are migrated.
806
+ enum|enum[])
807
+ if ! _argproc_parse-enum " ${value# =} " ; then
784
808
argError=1
785
809
fi
786
810
;;
787
811
filter)
788
- [[ ${value} =~ ^= (/.* /| [_a-zA-Z][-_:a-zA-Z0-9]* )$ ]] \
812
+ [[ ${value} =~ ^= (/.* /| \{ . * \} | [_a-zA-Z][-_:a-zA-Z0-9]* )$ ]] \
789
813
&& optFilter=" ${BASH_REMATCH[1]} " \
790
814
|| argError=1
791
815
;;
@@ -858,6 +882,32 @@ function _argproc_janky-args {
858
882
fi
859
883
}
860
884
885
+ # Parses a single enumeration-set value. Upon success sets a `optFilter`
886
+ # (presumed local in the calling scope) to "return" a filter expression that
887
+ # matches the specified enumeration.
888
+ function _argproc_parse-enum {
889
+ local value=" $1 "
890
+ local values
891
+
892
+ set-array-from-vals values " ${value} " \
893
+ || return " $? "
894
+
895
+ if (( ${# values[@]} == 0 )) ; then
896
+ # Error: Must have at least one value.
897
+ return 1
898
+ fi
899
+
900
+ optFilter=" $(
901
+ printf ' { [[ "$1" =~ ^('
902
+ local or=' ' print
903
+ for value in " ${values[@]} " ; do
904
+ printf ' %s%s' " ${or} " " $( vals --dollar -- " ${value} " ) "
905
+ or=' |'
906
+ done
907
+ printf $' )$ ]] && printf \' %%s\' "$1" }'
908
+ ) "
909
+ }
910
+
861
911
# Parses a single argument / option spec. `--short` to accept a <short>
862
912
# (short-option) character. `--value` to accept a value. `--value-eq` to accept
863
913
# a value and leave the `=` in the result (to distinguish unset and
@@ -1022,12 +1072,12 @@ function _argproc_statements-from-args {
1022
1072
;;
1023
1073
' []=' )
1024
1074
# Multi-value option. Parse the value into elements.
1025
- if eval 2> /dev/null " values=( ${value} ) " ; then
1075
+ if set-array-from-vals values " ${value} " ; then
1026
1076
_argproc_statements+=(
1027
1077
" ${handler} $( _argproc_quote " ${values[@]} " ) " )
1028
1078
else
1029
1079
error-msg " Invalid multi-value syntax for option --${name} :"
1030
- error-msg " ${value} "
1080
+ error-msg " $( vals -- " $ {value}" ) "
1031
1081
argError=1
1032
1082
fi
1033
1083
;;
0 commit comments