1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
#!/bin/bash
#
# parseopts.sh - getopt_long-like parser
#
# Copyright (c) 2012-2019 Pacman Development Team <pacman-dev@archlinux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# A getopt_long-like parser which portably supports longopts and
# shortopts with some GNU extensions. For both short and long opts,
# options requiring an argument should be suffixed with a colon, and
# options with optional arguments should be suffixed with a question
# mark. After the first argument containing the short opts, any number
# of valid long opts may be be passed. The end of the options delimiter
# must then be added, followed by the user arguments to the calling
# program.
#
# Options with optional arguments will be returned as "--longopt=optarg"
# for longopts, or "-o=optarg" for shortopts. This isn't actually a valid
# way to pass an optional argument with a shortopt on the command line,
# but is done by parseopts to enable the caller script to split the option
# and its optarg easily.
#
# Recommended Usage:
# OPT_SHORT='fb:zq?'
# OPT_LONG=('foo' 'bar:' 'baz' 'qux?')
# if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then
# exit 1
# fi
# set -- "${OPTRET[@]}"
# Returns:
# 0: parse success
# 1: parse failure (error message supplied)
parseopts() {
local opt= optarg= i= shortopts=$1
local -a longopts=() unused_argv=()
shift
while [[ $1 && $1 != '--' ]]; do
longopts+=("$1")
shift
done
shift
longoptmatch() {
local o longmatch=()
for o in "${longopts[@]}"; do
if [[ ${o%[:?]} = "$1" ]]; then
longmatch=("$o")
break
fi
[[ ${o%[:?]} = "$1"* ]] && longmatch+=("$o")
done
case ${#longmatch[*]} in
1)
# success, override with opt and return arg req (0 == none, 1 == required, 2 == optional)
opt=${longmatch%[:?]}
case $longmatch in
*:) return 1 ;;
*\?) return 2 ;;
*) return 0 ;;
esac
;;
0)
# fail, no match found
return 255 ;;
*)
# fail, ambiguous match
printf "${0##*/}: $(gettext "option '%s' is ambiguous; possibilities:")" "--$1"
printf " '%s'" "${longmatch[@]%[:?]}"
printf '\n'
return 254 ;;
esac >&2
}
while (( $# )); do
case $1 in
--) # explicit end of options
shift
break
;;
-[!-]*) # short option
for (( i = 1; i < ${#1}; i++ )); do
opt=${1:i:1}
case $shortopts in
# option requires optarg
*$opt:*)
# if we're not at the end of the option chunk, the rest is the optarg
if (( i < ${#1} - 1 )); then
OPTRET+=("-$opt" "${1:i+1}")
break
# if we're at the end, grab the the next positional, if it exists
elif (( i == ${#1} - 1 && $# > 1 )); then
OPTRET+=("-$opt" "$2")
shift
break
# parse failure
else
printf "${0##*/}: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2
OPTRET=(--)
return 1
fi
;;
# option's optarg is optional
*$opt\?*)
# if we're not at the end of the option chunk, the rest is the optarg
if (( i < ${#1} - 1 )); then
OPTRET+=("-$opt=${1:i+1}")
break
# option has no optarg
else
OPTRET+=("-$opt")
fi
;;
# option has no optarg
*$opt*)
OPTRET+=("-$opt")
;;
# option doesn't exist
*)
printf "${0##*/}: $(gettext "invalid option") -- '%s'\n" "$opt" >&2
OPTRET=(--)
return 1
;;
esac
done
;;
--?*=*|--?*) # long option
IFS='=' read -r opt optarg <<< "${1#--}"
longoptmatch "$opt"
case $? in
0)
# parse failure
if [[ $1 = *=* ]]; then
printf "${0##*/}: $(gettext "option '%s' does not allow an argument")\n" "--$opt" >&2
OPTRET=(--)
return 1
# --longopt
else
OPTRET+=("--$opt")
fi
;;
1)
# --longopt=optarg
if [[ $1 = *=* ]]; then
OPTRET+=("--$opt" "$optarg")
# --longopt optarg
elif (( $# > 1 )); then
OPTRET+=("--$opt" "$2" )
shift
# parse failure
else
printf "${0##*/}: $(gettext "option '%s' requires an argument")\n" "--$opt" >&2
OPTRET=(--)
return 1
fi
;;
2)
# --longopt=optarg
if [[ $1 = *=* ]]; then
OPTRET+=("--$opt=$optarg")
# --longopt
else
OPTRET+=("--$opt")
fi
;;
254)
# ambiguous option -- error was reported for us by longoptmatch()
OPTRET=(--)
return 1
;;
255)
# parse failure
printf "${0##*/}: $(gettext "invalid option") '--%s'\n" "$opt" >&2
OPTRET=(--)
return 1
;;
esac
;;
*) # non-option arg encountered, add it as a parameter
unused_argv+=("$1")
;;
esac
shift
done
# add end-of-opt terminator and any leftover positional parameters
OPTRET+=('--' "${unused_argv[@]}" "$@")
unset longoptmatch
return 0
}
|