scripts/library: introduce parseopts
[pacman-ng.git] / scripts / library / parseopts.sh
blob11589ce3160e0c2a9c72a9f233d43e66acde269d
1 # getopt-like parser
2 parseopts() {
3 local opt= optarg= i= shortopts=$1
4 local -a longopts=() unused_argv=()
6 shift
7 while [[ $1 && $1 != '--' ]]; do
8 longopts+=("$1")
9 shift
10 done
11 shift
13 longoptmatch() {
14 local o longmatch=()
15 for o in "${longopts[@]}"; do
16 if [[ ${o%:} = "$1" ]]; then
17 longmatch=("$o")
18 break
20 [[ ${o%:} = "$1"* ]] && longmatch+=("$o")
21 done
23 case ${#longmatch[*]} in
25 # success, override with opt and return arg req (0 == none, 1 == required)
26 opt=${longmatch%:}
27 if [[ $longmatch = *: ]]; then
28 return 1
29 else
30 return 0
31 fi ;;
33 # fail, no match found
34 return 255 ;;
36 # fail, ambiguous match
37 printf "@SCRIPTNAME@: $(gettext "option '%s' is ambiguous; possibilities:")" "--$1"
38 printf " '%s'" "${longmatch[@]%:}"
39 printf '\n'
40 return 254 ;;
41 esac >&2
44 while (( $# )); do
45 case $1 in
46 --) # explicit end of options
47 shift
48 break
50 -[!-]*) # short option
51 for (( i = 1; i < ${#1}; i++ )); do
52 opt=${1:i:1}
54 # option doesn't exist
55 if [[ $shortopts != *$opt* ]]; then
56 printf "@SCRIPTNAME@: $(gettext "invalid option") -- '%s'\n" "$opt" >&2
57 OPTRET=(--)
58 return 1
61 OPTRET+=("-$opt")
62 # option requires optarg
63 if [[ $shortopts = *$opt:* ]]; then
64 # if we're not at the end of the option chunk, the rest is the optarg
65 if (( i < ${#1} - 1 )); then
66 OPTRET+=("${1:i+1}")
67 break
68 # if we're at the end, grab the the next positional, if it exists
69 elif (( i == ${#1} - 1 )) && [[ $2 ]]; then
70 OPTRET+=("$2")
71 shift
72 break
73 # parse failure
74 else
75 printf "@SCRIPTNAME@: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2
76 OPTRET=(--)
77 return 1
80 done
82 --?*=*|--?*) # long option
83 IFS='=' read -r opt optarg <<< "${1#--}"
84 longoptmatch "$opt"
85 case $? in
87 # parse failure
88 if [[ $optarg ]]; then
89 printf "@SCRIPTNAME@: $(gettext "option '%s' does not allow an argument")\n" "--$opt" >&2
90 OPTRET=(--)
91 return 1
92 # --longopt
93 else
94 OPTRET+=("--$opt")
95 shift
96 continue 2
100 # --longopt=optarg
101 if [[ $optarg ]]; then
102 OPTRET+=("--$opt" "$optarg")
103 shift
104 # --longopt optarg
105 elif [[ $2 ]]; then
106 OPTRET+=("--$opt" "$2" )
107 shift 2
108 # parse failure
109 else
110 printf "@SCRIPTNAME@: $(gettext "option '%s' requires an argument")\n" "--$opt" >&2
111 OPTRET=(--)
112 return 1
114 continue 2
116 254)
117 # ambiguous option -- error was reported for us by longoptmatch()
118 OPTRET=(--)
119 return 1
121 255)
122 # parse failure
123 printf "@SCRIPTNAME@: $(gettext "invalid option") '--%s'\n" "$opt" >&2
124 OPTRET=(--)
125 return 1
127 esac
129 *) # non-option arg encountered, add it as a parameter
130 unused_argv+=("$1")
132 esac
133 shift
134 done
136 # add end-of-opt terminator and any leftover positional parameters
137 OPTRET+=('--' "${unused_argv[@]}" "$@")
138 unset longoptmatch
140 return 0