diff options
| -rw-r--r-- | getopts_long.sh | 701 | ||||
| -rwxr-xr-x | lists.sh | 66 |
2 files changed, 767 insertions, 0 deletions
diff --git a/getopts_long.sh b/getopts_long.sh new file mode 100644 index 0000000..ce2f7f0 --- /dev/null +++ b/getopts_long.sh | |||
| @@ -0,0 +1,701 @@ | |||
| 1 | #! /bin/echo Usage:. | ||
| 2 | # | ||
| 3 | # getopts_long -- POSIX shell getopts with GNU-style long option support | ||
| 4 | # | ||
| 5 | # Copyright 2005-2009 Stephane Chazelas <stephane_chazelas@yahoo.fr> | ||
| 6 | # | ||
| 7 | # Permission to use, copy, modify, distribute, and sell this software and | ||
| 8 | # its documentation for any purpose is hereby granted without fee, provided | ||
| 9 | # that the above copyright notice appear in all copies and that both that | ||
| 10 | # copyright notice and this permission notice appear in supporting | ||
| 11 | # documentation. No representations are made about the suitability of this | ||
| 12 | # software for any purpose. It is provided "as is" without express or | ||
| 13 | # implied warranty. | ||
| 14 | |||
| 15 | # History: | ||
| 16 | # 2005 ?? - 1.0 | ||
| 17 | # first version | ||
| 18 | # 2009-08-12 - 1.1 | ||
| 19 | # thanks to ujqm8360@netwtc.net for helping fix a few bugs: | ||
| 20 | # - -ab x where -b accepts arguments wasn't handled properly. Also, | ||
| 21 | # $OPTLIND wasn't set properly (at least not the same way as most | ||
| 22 | # getopts implementation do). | ||
| 23 | # - The handling of ambiguous long options was incorrect. | ||
| 24 | |||
| 25 | getopts_long() { | ||
| 26 | # args: shortopts, var, [name, type]*, "", "$@" | ||
| 27 | # | ||
| 28 | # getopts_long parses command line arguments. It works like the | ||
| 29 | # getopts shell built-in command except that it also recognises long | ||
| 30 | # options a la GNU. | ||
| 31 | # | ||
| 32 | # You must provide getopts_long with the list of supported single | ||
| 33 | # letter options in the same format as getopts', followed by the | ||
| 34 | # variable name you want getopts_long to return the current processed | ||
| 35 | # option in, followed by the list of long option names (without the | ||
| 36 | # leading "--") and types (0 or no_argument, 1 or required_argument, | ||
| 37 | # 2 or optional_argument). The end of the long option specification | ||
| 38 | # is indicated by an empty argument. Then follows the list of | ||
| 39 | # arguments to be parsed. | ||
| 40 | # | ||
| 41 | # The $OPTLIND variable must be set to 1 before the first call of the | ||
| 42 | # getopts_long function to process a given list of arguments. | ||
| 43 | # | ||
| 44 | # getopts_long returns the value of the current option in the variable | ||
| 45 | # whose name is provided as its second argument (be careful to avoid | ||
| 46 | # variables that have a special signification to getopts_long or the | ||
| 47 | # shell or any other tool you may call from your script). If the | ||
| 48 | # current option is a single letter option, then it is returned | ||
| 49 | # without the leading "-". If it's a long option (possibly | ||
| 50 | # abbreviated), then the full name of the option (without the leading | ||
| 51 | # "--") is returned. If the option has an argument, then it is stored | ||
| 52 | # in the $OPTLARG variable. If the current option is not recognised, | ||
| 53 | # or if it is provided with an argument while it is not expecting one | ||
| 54 | # (as in --opt=value) or if it is not provided with an argument while | ||
| 55 | # it is expecting one, or if the option is so abbreviated that it is | ||
| 56 | # impossible to identify the option uniquely, then: | ||
| 57 | # - if the short option specifications begin with ":", getopts_long | ||
| 58 | # returns ":" in the output variable and $OPTLARG contains the | ||
| 59 | # faulty option name (in full except in the case of the ambiguous | ||
| 60 | # or bad option) and $OPTLERR contains the error message. | ||
| 61 | # - if not, then getopts_long behaves the same as above except that | ||
| 62 | # it returns "?" instead of ":", leaves $OPTLARG unset and | ||
| 63 | # displays the error message on stderr. | ||
| 64 | # | ||
| 65 | # The exit status of getopts_long is 0 unless the end of options is | ||
| 66 | # reached or an error is encountered in the syntax of the getopts_long | ||
| 67 | # call. | ||
| 68 | # | ||
| 69 | # After getopts_long has finished processing the options, $OPTLIND | ||
| 70 | # contains the index of the first non-option argument or $# + 1 if | ||
| 71 | # there's no non-option argument. | ||
| 72 | # | ||
| 73 | # The "=" character is not allowed in a long option name. Any other | ||
| 74 | # character is. "-" and ":" are not allowed as short option names. Any | ||
| 75 | # other character is. If a short option appears more than once in the | ||
| 76 | # specification, the one with the greatest number of ":"s following it | ||
| 77 | # is retained. If a long option name is provided more than once, only | ||
| 78 | # the first one is taken into account. Note that if you have both a -a | ||
| 79 | # and --a option, there's no way to differentiate them. Beside the | ||
| 80 | # $OPTLIND, $OPTLARG, and $OPTLERR, getopts_long uses the $OPTLPENDING | ||
| 81 | # variable to hold the remaining options to be processed for arguments | ||
| 82 | # with several one-letter options. That variable shouldn't be used | ||
| 83 | # anywhere else in your script. Those 4 variables are the only ones | ||
| 84 | # getopts_long may modify. | ||
| 85 | # | ||
| 86 | # Dependency: only POSIX utilities are called by that function. They | ||
| 87 | # are "set", "unset", "shift", "break", "return", "eval", "command", | ||
| 88 | # ":", "printf" and "[". Those are generally built in the POSIX | ||
| 89 | # shells. Only "printf" has been known not to be in some old versions | ||
| 90 | # of bash, zsh or ash based shells. | ||
| 91 | # | ||
| 92 | # Differences with the POSIX getopts: | ||
| 93 | # - if an error is detected during the parsing of command line | ||
| 94 | # arguments, the error message is stored in the $OPTLERR variable | ||
| 95 | # and if the first character of optstring is ':', ':' is returned in | ||
| 96 | # any case. | ||
| 97 | # - in the single-letter option specification, if a letter is | ||
| 98 | # followed by 2 colons ("::"), then the option can have an optional | ||
| 99 | # argument as in GNU getopt(3). In that case, the argument must | ||
| 100 | # directly follow the option as in -oarg (not -o arg). | ||
| 101 | # - there must be an empty argument to mark the end of the option | ||
| 102 | # specification. | ||
| 103 | # - long options starting with "--" are supported. | ||
| 104 | # | ||
| 105 | # Differences with GNU getopt_long(3): | ||
| 106 | # - getopts_long doesn't allow options to be interspersed with other | ||
| 107 | # arguments (as if POSIXLY_CORRECT was set for GNU getopt_long(3)) | ||
| 108 | # - there's no linkage of any sort between the short and long | ||
| 109 | # options. The caller is responsible of that (see example below). | ||
| 110 | # | ||
| 111 | # Compatibility: | ||
| 112 | # getopts_long code is (hopefully) POSIX.2/SUSv3 compliant. It won't | ||
| 113 | # work with the Bourne/SystemV shell. Use /usr/xpg4/bin/sh or ksh or | ||
| 114 | # bash on Solaris. | ||
| 115 | # It has been tested successfully with: | ||
| 116 | # - bash 3.0 (patch level 16) on Cygwin | ||
| 117 | # - zsh 4.2.4 on Solaris 2.7 | ||
| 118 | # - /usr/xpg4/bin/sh (same as /usr/bin/ksh) (ksh88i) on Solaris 2.7 | ||
| 119 | # - /usr/dt/bin/dtksh (ksh93d) on Solaris 2.7 | ||
| 120 | # - /usr/bin/ksh (pdksh 5.2.14) on Linux | ||
| 121 | # - zsh 3.0.6 on Solaris 2.8 | ||
| 122 | # - bash 2.0.3 on Solaris 2.8 | ||
| 123 | # - dash 0.5.2 on Linux | ||
| 124 | # - bash 2.05b (patch level 0) on Linux | ||
| 125 | # - ksh93p and ksh93q on Linux (ksh93t+ crashes) | ||
| 126 | # | ||
| 127 | # It is known to fail with those non-POSIX compliant shells: | ||
| 128 | # - /bin/sh on Solaris | ||
| 129 | # - /usr/bin/sh on Cygwin | ||
| 130 | # - bash 1.x | ||
| 131 | # | ||
| 132 | # Bugs: | ||
| 133 | # please report them to <stephane_chazelas@yahoo.fr> | ||
| 134 | # | ||
| 135 | # Example: | ||
| 136 | # | ||
| 137 | # verbose=false opt_bar=false bar=default_bar foo=default_foo | ||
| 138 | # opt_s=false opt_long=false | ||
| 139 | # OPTLIND=1 | ||
| 140 | # while getopts_long :sf:b::vh opt \ | ||
| 141 | # long 0 \ | ||
| 142 | # foo required_argument \ | ||
| 143 | # bar 2 \ | ||
| 144 | # verbose no_argument \ | ||
| 145 | # help 0 "" "$@" | ||
| 146 | # do | ||
| 147 | # case "$opt" in | ||
| 148 | # s) opt_s=true;; | ||
| 149 | # long) opt_long=true;; | ||
| 150 | # v|verbose) verbose=true;; | ||
| 151 | # h|help) usage; exit 0;; | ||
| 152 | # f|foo) foo=$OPTLARG;; | ||
| 153 | # b|bar) bar=${OPTLARG-$bar};; | ||
| 154 | # :) printf >&2 '%s: %s\n' "${0##*/}" "$OPTLERR" | ||
| 155 | # usage | ||
| 156 | # exit 1;; | ||
| 157 | # esac | ||
| 158 | # done | ||
| 159 | # shift "$(($OPTLIND - 1))" | ||
| 160 | # # process the remaining arguments | ||
| 161 | |||
| 162 | [ -n "${ZSH_VERSION+z}" ] && emulate -L sh | ||
| 163 | |||
| 164 | unset OPTLERR OPTLARG || : | ||
| 165 | |||
| 166 | case "$OPTLIND" in | ||
| 167 | "" | 0 | *[!0-9]*) | ||
| 168 | # First time in the loop. Initialise the parameters. | ||
| 169 | OPTLIND=1 | ||
| 170 | OPTLPENDING= | ||
| 171 | ;; | ||
| 172 | esac | ||
| 173 | |||
| 174 | if [ "$#" -lt 2 ]; then | ||
| 175 | printf >&2 'getopts_long: not enough arguments\n' | ||
| 176 | return 1 | ||
| 177 | fi | ||
| 178 | |||
| 179 | # validate variable name. Need to fix locale for character ranges. | ||
| 180 | LC_ALL=C command eval ' | ||
| 181 | case "$2" in | ||
| 182 | *[!a-zA-Z_0-9]*|""|[0-9]*) | ||
| 183 | printf >&2 "getopts_long: invalid variable name: \`%s'\''\n" "$2" | ||
| 184 | return 1 | ||
| 185 | ;; | ||
| 186 | esac' | ||
| 187 | |||
| 188 | # validate short option specification | ||
| 189 | case "$1" in | ||
| 190 | ::*|*:::*|*-*) | ||
| 191 | printf >&2 "getopts_long: invalid option specification: \`%s'\n" "$1" | ||
| 192 | return 1 | ||
| 193 | ;; | ||
| 194 | esac | ||
| 195 | |||
| 196 | # validate long option specifications | ||
| 197 | |||
| 198 | # POSIX shells only have $1, $2... as local variables, hence the | ||
| 199 | # extensive use of "set" in that function. | ||
| 200 | |||
| 201 | set 4 "$@" | ||
| 202 | while :; do | ||
| 203 | if | ||
| 204 | [ "$1" -gt "$#" ] || { | ||
| 205 | eval 'set -- "${'"$1"'}" "$@"' | ||
| 206 | [ -n "$1" ] || break | ||
| 207 | [ "$(($2 + 2))" -gt "$#" ] | ||
| 208 | } | ||
| 209 | then | ||
| 210 | printf >&2 "getopts_long: long option specifications must end in an empty argument\n" | ||
| 211 | return 1 | ||
| 212 | fi | ||
| 213 | eval 'set -- "${'"$(($2 + 2))"'}" "$@"' | ||
| 214 | # $1 = type, $2 = name, $3 = $@ | ||
| 215 | case "$2" in | ||
| 216 | *=*) | ||
| 217 | printf >&2 "getopts_long: invalid long option name: \`%s'\n" "$2" | ||
| 218 | return 1 | ||
| 219 | ;; | ||
| 220 | esac | ||
| 221 | case "$1" in | ||
| 222 | 0 | no_argument) ;; | ||
| 223 | 1 | required_argument) ;; | ||
| 224 | 2 | optional_argument) ;; | ||
| 225 | *) | ||
| 226 | printf >&2 "getopts_long: invalid long option type: \`%s'\n" "$1" | ||
| 227 | return 1 | ||
| 228 | ;; | ||
| 229 | esac | ||
| 230 | eval "shift 3; set $(($3 + 2))"' "$@"' | ||
| 231 | done | ||
| 232 | shift | ||
| 233 | |||
| 234 | eval "shift; set $(($1 + $OPTLIND))"' "$@"' | ||
| 235 | |||
| 236 | # unless there are pending short options to be processed (in | ||
| 237 | # $OPTLPENDING), the current option is now in ${$1} | ||
| 238 | |||
| 239 | if [ -z "$OPTLPENDING" ]; then | ||
| 240 | [ "$1" -le "$#" ] || return 1 | ||
| 241 | eval 'set -- "${'"$1"'}" "$@"' | ||
| 242 | |||
| 243 | case "$1" in | ||
| 244 | --) | ||
| 245 | OPTLIND=$(($OPTLIND + 1)) | ||
| 246 | return 1 | ||
| 247 | ;; | ||
| 248 | --*) | ||
| 249 | OPTLIND=$(($OPTLIND + 1)) | ||
| 250 | ;; | ||
| 251 | -?*) | ||
| 252 | OPTLPENDING="${1#-}" | ||
| 253 | shift | ||
| 254 | ;; | ||
| 255 | *) | ||
| 256 | return 1 | ||
| 257 | ;; | ||
| 258 | esac | ||
| 259 | fi | ||
| 260 | |||
| 261 | if [ -n "$OPTLPENDING" ]; then | ||
| 262 | # WA for zsh and bash 2.03 bugs: | ||
| 263 | OPTLARG=${OPTLPENDING%"${OPTLPENDING#?}"} | ||
| 264 | set -- "$OPTLARG" "$@" | ||
| 265 | OPTLPENDING="${OPTLPENDING#?}" | ||
| 266 | unset OPTLARG | ||
| 267 | |||
| 268 | # $1 = current option = ${$2+1}, $3 = $@ | ||
| 269 | |||
| 270 | [ -n "$OPTLPENDING" ] || | ||
| 271 | OPTLIND=$(($OPTLIND + 1)) | ||
| 272 | |||
| 273 | case "$1" in | ||
| 274 | [-:]) | ||
| 275 | OPTLERR="bad option: \`-$1'" | ||
| 276 | case "$3" in | ||
| 277 | :*) | ||
| 278 | eval "$4=:" | ||
| 279 | OPTLARG="$1" | ||
| 280 | ;; | ||
| 281 | *) | ||
| 282 | printf >&2 '%s\n' "$OPTLERR" | ||
| 283 | eval "$4='?'" | ||
| 284 | ;; | ||
| 285 | esac | ||
| 286 | ;; | ||
| 287 | |||
| 288 | *) | ||
| 289 | case "$3" in | ||
| 290 | *"$1"::*) # optional argument | ||
| 291 | eval "$4=\"\$1\"" | ||
| 292 | if [ -n "$OPTLPENDING" ]; then | ||
| 293 | # take the argument from $OPTLPENDING if any | ||
| 294 | OPTLARG="$OPTLPENDING" | ||
| 295 | OPTLPENDING= | ||
| 296 | OPTLIND=$(($OPTLIND + 1)) | ||
| 297 | fi | ||
| 298 | ;; | ||
| 299 | |||
| 300 | *"$1":*) # required argument | ||
| 301 | if [ -n "$OPTLPENDING" ]; then | ||
| 302 | # take the argument from $OPTLPENDING if any | ||
| 303 | OPTLARG="$OPTLPENDING" | ||
| 304 | eval "$4=\"\$1\"" | ||
| 305 | OPTLPENDING= | ||
| 306 | OPTLIND=$(($OPTLIND + 1)) | ||
| 307 | else | ||
| 308 | # take the argument from the next argument | ||
| 309 | if [ "$(($2 + 2))" -gt "$#" ]; then | ||
| 310 | OPTLERR="option \`-$1' requires an argument" | ||
| 311 | case "$3" in | ||
| 312 | :*) | ||
| 313 | eval "$4=:" | ||
| 314 | OPTLARG="$1" | ||
| 315 | ;; | ||
| 316 | *) | ||
| 317 | printf >&2 '%s\n' "$OPTLERR" | ||
| 318 | eval "$4='?'" | ||
| 319 | ;; | ||
| 320 | esac | ||
| 321 | else | ||
| 322 | OPTLIND=$(($OPTLIND + 1)) | ||
| 323 | eval "OPTLARG=\"\${$(($2 + 2))}\"" | ||
| 324 | eval "$4=\"\$1\"" | ||
| 325 | fi | ||
| 326 | fi | ||
| 327 | ;; | ||
| 328 | |||
| 329 | *"$1"*) # no argument | ||
| 330 | eval "$4=\"\$1\"" | ||
| 331 | ;; | ||
| 332 | *) | ||
| 333 | OPTLERR="bad option: \`-$1'" | ||
| 334 | case "$3" in | ||
| 335 | :*) | ||
| 336 | eval "$4=:" | ||
| 337 | OPTLARG="$1" | ||
| 338 | ;; | ||
| 339 | *) | ||
| 340 | printf >&2 '%s\n' "$OPTLERR" | ||
| 341 | eval "$4='?'" | ||
| 342 | ;; | ||
| 343 | esac | ||
| 344 | ;; | ||
| 345 | esac | ||
| 346 | ;; | ||
| 347 | esac | ||
| 348 | else # long option | ||
| 349 | |||
| 350 | # remove the leading "--" | ||
| 351 | OPTLPENDING="$1" | ||
| 352 | shift | ||
| 353 | set 6 "${OPTLPENDING#--}" "$@" | ||
| 354 | OPTLPENDING= | ||
| 355 | |||
| 356 | while | ||
| 357 | eval 'set -- "${'"$1"'}" "$@"' | ||
| 358 | [ -n "$1" ] | ||
| 359 | do | ||
| 360 | # $1 = option name = ${$2+1}, $3 => given option = ${$4+3}, $5 = $@ | ||
| 361 | |||
| 362 | case "${3%%=*}" in | ||
| 363 | "$1") | ||
| 364 | OPTLPENDING=EXACT | ||
| 365 | break;; | ||
| 366 | esac | ||
| 367 | |||
| 368 | # try to see if the current option can be seen as an abbreviation. | ||
| 369 | case "$1" in | ||
| 370 | "${3%%=*}"*) | ||
| 371 | if [ -n "$OPTLPENDING" ]; then | ||
| 372 | [ "$OPTLPENDING" = AMBIGUOUS ] || eval '[ "${'"$(($OPTLPENDING + 1))"'}" = "$1" ]' || | ||
| 373 | OPTLPENDING=AMBIGUOUS | ||
| 374 | # there was another different option matching the current | ||
| 375 | # option. The eval thing is in case one option is provided | ||
| 376 | # twice in the specifications which is OK as per the | ||
| 377 | # documentation above | ||
| 378 | else | ||
| 379 | OPTLPENDING="$2" | ||
| 380 | fi | ||
| 381 | ;; | ||
| 382 | esac | ||
| 383 | eval "shift 2; set $(($2 + 2)) "'"$@"' | ||
| 384 | done | ||
| 385 | |||
| 386 | case "$OPTLPENDING" in | ||
| 387 | AMBIGUOUS) | ||
| 388 | OPTLERR="option \`--${3%%=*}' is ambiguous" | ||
| 389 | case "$5" in | ||
| 390 | :*) | ||
| 391 | eval "$6=:" | ||
| 392 | OPTLARG="${3%%=*}" | ||
| 393 | ;; | ||
| 394 | *) | ||
| 395 | printf >&2 '%s\n' "$OPTLERR" | ||
| 396 | eval "$6='?'" | ||
| 397 | ;; | ||
| 398 | esac | ||
| 399 | OPTLPENDING= | ||
| 400 | return 0 | ||
| 401 | ;; | ||
| 402 | EXACT) | ||
| 403 | eval 'set "${'"$(($2 + 2))"'}" "$@"' | ||
| 404 | ;; | ||
| 405 | "") | ||
| 406 | OPTLERR="bad option: \`--${3%%=*}'" | ||
| 407 | case "$5" in | ||
| 408 | :*) | ||
| 409 | eval "$6=:" | ||
| 410 | OPTLARG="${3%%=*}" | ||
| 411 | ;; | ||
| 412 | *) | ||
| 413 | printf >&2 '%s\n' "$OPTLERR" | ||
| 414 | eval "$6='?'" | ||
| 415 | ;; | ||
| 416 | esac | ||
| 417 | OPTLPENDING= | ||
| 418 | return 0 | ||
| 419 | ;; | ||
| 420 | *) | ||
| 421 | # we've got an abbreviated long option. | ||
| 422 | shift | ||
| 423 | eval 'set "${'"$(($OPTLPENDING + 1))"'}" "${'"$OPTLPENDING"'}" "$@"' | ||
| 424 | ;; | ||
| 425 | esac | ||
| 426 | |||
| 427 | OPTLPENDING= | ||
| 428 | |||
| 429 | # $1 = option type, $2 = option name, $3 unused, | ||
| 430 | # $4 = given option = ${$5+4}, $6 = $@ | ||
| 431 | |||
| 432 | case "$4" in | ||
| 433 | *=*) | ||
| 434 | case "$1" in | ||
| 435 | 1 | required_argument | 2 | optional_argument) | ||
| 436 | eval "$7=\"\$2\"" | ||
| 437 | OPTLARG="${4#*=}" | ||
| 438 | ;; | ||
| 439 | *) | ||
| 440 | OPTLERR="option \`--$2' doesn't allow an argument" | ||
| 441 | case "$6" in | ||
| 442 | :*) | ||
| 443 | eval "$7=:" | ||
| 444 | OPTLARG="$2" | ||
| 445 | ;; | ||
| 446 | *) | ||
| 447 | printf >&2 '%s\n' "$OPTLERR" | ||
| 448 | eval "$7='?'" | ||
| 449 | ;; | ||
| 450 | esac | ||
| 451 | ;; | ||
| 452 | esac | ||
| 453 | ;; | ||
| 454 | |||
| 455 | *) | ||
| 456 | case "$1" in | ||
| 457 | 1 | required_argument) | ||
| 458 | if [ "$(($5 + 5))" -gt "$#" ]; then | ||
| 459 | OPTLERR="option \`--$2' requires an argument" | ||
| 460 | case "$6" in | ||
| 461 | :*) | ||
| 462 | eval "$7=:" | ||
| 463 | OPTLARG="$2" | ||
| 464 | ;; | ||
| 465 | *) | ||
| 466 | printf >&2 '%s\n' "$OPTLERR" | ||
| 467 | eval "$7='?'" | ||
| 468 | ;; | ||
| 469 | esac | ||
| 470 | else | ||
| 471 | OPTLIND=$(($OPTLIND + 1)) | ||
| 472 | eval "OPTLARG=\"\${$(($5 + 5))}\"" | ||
| 473 | eval "$7=\"\$2\"" | ||
| 474 | fi | ||
| 475 | ;; | ||
| 476 | *) | ||
| 477 | # optional argument (but obviously not provided) or no | ||
| 478 | # argument | ||
| 479 | eval "$7=\"\$2\"" | ||
| 480 | ;; | ||
| 481 | esac | ||
| 482 | ;; | ||
| 483 | esac | ||
| 484 | fi | ||
| 485 | return 0 | ||
| 486 | } | ||
| 487 | |||
| 488 | # testing code | ||
| 489 | if [ -n "$test_getopts_long" ]; then | ||
| 490 | test_getopts_long() { | ||
| 491 | expected="$1" had= | ||
| 492 | shift | ||
| 493 | OPTLIND=1 | ||
| 494 | |||
| 495 | while err="$(set +x;getopts_long "$@" 2>&1 > /dev/null)" | ||
| 496 | getopts_long "$@" 2> /dev/null; do | ||
| 497 | eval "opt=\"\$$2\"" | ||
| 498 | had="$had|$opt@${OPTLARG-unset}@${OPTLIND-unset}@${OPTLERR-unset}@$err" | ||
| 499 | done | ||
| 500 | had="$had|${OPTLIND-unset}|$err" | ||
| 501 | |||
| 502 | if [ "$had" = "$expected" ]; then | ||
| 503 | echo PASS | ||
| 504 | else | ||
| 505 | echo FAIL | ||
| 506 | printf 'Expected: %s\n Got: %s\n' "$expected" "$had" | ||
| 507 | fi | ||
| 508 | } | ||
| 509 | while IFS= read -r c && IFS= read -r e; do | ||
| 510 | printf '+ %-72s ' "$c" | ||
| 511 | #set -x | ||
| 512 | eval "test_getopts_long \"\$e\" $c" | ||
| 513 | done << \EOF | ||
| 514 | : a | ||
| 515 | |1|getopts_long: long option specifications must end in an empty argument | ||
| 516 | :a opt "" -a | ||
| 517 | |a@unset@2@unset@|2| | ||
| 518 | :a opt "" -a b | ||
| 519 | |a@unset@2@unset@|2| | ||
| 520 | :a opt "" -a -a | ||
| 521 | |a@unset@2@unset@|a@unset@3@unset@|3| | ||
| 522 | :a opt "" -ab | ||
| 523 | |a@unset@1@unset@|:@b@2@bad option: `-b'@|2| | ||
| 524 | :a: opt "" -ab | ||
| 525 | |a@b@2@unset@|2| | ||
| 526 | :a: opt "" -a b | ||
| 527 | |a@b@3@unset@|3| | ||
| 528 | :a: opt "" -a -a | ||
| 529 | |a@-a@3@unset@|3| | ||
| 530 | :a: opt "" -a | ||
| 531 | |:@a@2@option `-a' requires an argument@|2| | ||
| 532 | :a:: opt "" -a | ||
| 533 | |a@unset@2@unset@|2| | ||
| 534 | :a:: opt "" -ab | ||
| 535 | |a@b@2@unset@|2| | ||
| 536 | :a:: opt "" -a b | ||
| 537 | |a@unset@2@unset@|2| | ||
| 538 | :ab: opt "" -ab c | ||
| 539 | |a@unset@1@unset@|b@c@3@unset@|3| | ||
| 540 | :a:: opt "" -a -a | ||
| 541 | |a@unset@2@unset@|a@unset@3@unset@|3| | ||
| 542 | :a:: opt "" -:a: | ||
| 543 | |:@:@1@bad option: `-:'@|a@:@2@unset@|2| | ||
| 544 | := opt "" | ||
| 545 | |1| | ||
| 546 | :: opt "" | ||
| 547 | |1|getopts_long: invalid option specification: `::' | ||
| 548 | : opt "" | ||
| 549 | |1| | ||
| 550 | :a:a opt "" -a | ||
| 551 | |:@a@2@option `-a' requires an argument@|2| | ||
| 552 | :a::a opt "" -a | ||
| 553 | |a@unset@2@unset@|2| | ||
| 554 | :ab:c:: opt "" -abc -cba -bac | ||
| 555 | |a@unset@1@unset@|b@c@2@unset@|c@ba@3@unset@|b@ac@4@unset@|4| | ||
| 556 | : opt abc 0 "" --abc | ||
| 557 | |abc@unset@2@unset@|2| | ||
| 558 | : opt abc no_argument "" --abc | ||
| 559 | |abc@unset@2@unset@|2| | ||
| 560 | : opt abc no_argument "" --abc=foo | ||
| 561 | |:@abc@2@option `--abc' doesn't allow an argument@|2| | ||
| 562 | : opt abc no_argument "" --abc foo | ||
| 563 | |abc@unset@2@unset@|2| | ||
| 564 | : opt abc 1 "" --abc=foo | ||
| 565 | |abc@foo@2@unset@|2| | ||
| 566 | : opt abc required_argument "" --abc foo | ||
| 567 | |abc@foo@3@unset@|3| | ||
| 568 | : opt abc required_argument "" --abc= | ||
| 569 | |abc@@2@unset@|2| | ||
| 570 | : opt abc required_argument "" --abc | ||
| 571 | |:@abc@2@option `--abc' requires an argument@|2| | ||
| 572 | : opt abc 2 "" --abc | ||
| 573 | |abc@unset@2@unset@|2| | ||
| 574 | : opt abc optional_argument "" --abc= | ||
| 575 | |abc@@2@unset@|2| | ||
| 576 | : opt abc optional_argument "" --abc=foo | ||
| 577 | |abc@foo@2@unset@|2| | ||
| 578 | : opt abc optional_argument "" --abc --abc | ||
| 579 | |abc@unset@2@unset@|abc@unset@3@unset@|3| | ||
| 580 | : opt abc 0 abcd 0 "" --abc | ||
| 581 | |abc@unset@2@unset@|2| | ||
| 582 | : opt abc 0 abd 0 "" --ab | ||
| 583 | |:@ab@2@option `--ab' is ambiguous@|2| | ||
| 584 | : opt abc 0 abcd 0 "" --ab | ||
| 585 | |:@ab@2@option `--ab' is ambiguous@|2| | ||
| 586 | : opt abc 0 abc 1 "" --ab | ||
| 587 | |abc@unset@2@unset@|2| | ||
| 588 | : opt abc 0 abc 1 "" --abc | ||
| 589 | |abc@unset@2@unset@|2| | ||
| 590 | : opt abc 0 abc 1 "" --ab | ||
| 591 | |abc@unset@2@unset@|2| | ||
| 592 | : opt abc 0 acd 0 "" --ab | ||
| 593 | |abc@unset@2@unset@|2| | ||
| 594 | :abc:d:e::f:: opt ab 0 ac 1 bc 2 cd 1 cde 2 "" -abcdef -a -f -c --a --a= --b=foo -fg | ||
| 595 | |a@unset@1@unset@|b@unset@1@unset@|c@def@2@unset@|a@unset@3@unset@|f@unset@4@unset@|c@--a@6@unset@|:@a@7@option `--a' is ambiguous@|bc@foo@8@unset@|f@g@9@unset@|9| | ||
| 596 | a opt "" -a | ||
| 597 | |a@unset@2@unset@|2| | ||
| 598 | a opt "" -a b | ||
| 599 | |a@unset@2@unset@|2| | ||
| 600 | a opt "" -a -a | ||
| 601 | |a@unset@2@unset@|a@unset@3@unset@|3| | ||
| 602 | a opt "" -ab | ||
| 603 | |a@unset@1@unset@|?@unset@2@bad option: `-b'@bad option: `-b'|2| | ||
| 604 | a: opt "" -ab | ||
| 605 | |a@b@2@unset@|2| | ||
| 606 | a: opt "" -a b | ||
| 607 | |a@b@3@unset@|3| | ||
| 608 | a: opt "" -a -a | ||
| 609 | |a@-a@3@unset@|3| | ||
| 610 | a: opt "" -a | ||
| 611 | |?@unset@2@option `-a' requires an argument@option `-a' requires an argument|2| | ||
| 612 | a:: opt "" -a | ||
| 613 | |a@unset@2@unset@|2| | ||
| 614 | a:: opt "" -ab | ||
| 615 | |a@b@2@unset@|2| | ||
| 616 | a:: opt "" -a b | ||
| 617 | |a@unset@2@unset@|2| | ||
| 618 | a:: opt "" -a -a | ||
| 619 | |a@unset@2@unset@|a@unset@3@unset@|3| | ||
| 620 | a:: opt "" -:a: | ||
| 621 | |?@unset@1@bad option: `-:'@bad option: `-:'|a@:@2@unset@|2| | ||
| 622 | = opt "" | ||
| 623 | |1| | ||
| 624 | : opt "" | ||
| 625 | |1| | ||
| 626 | '' opt "" | ||
| 627 | |1| | ||
| 628 | a:a opt "" -a | ||
| 629 | |?@unset@2@option `-a' requires an argument@option `-a' requires an argument|2| | ||
| 630 | a::a opt "" -a | ||
| 631 | |a@unset@2@unset@|2| | ||
| 632 | ab:c:: opt "" -abc -cba -bac | ||
| 633 | |a@unset@1@unset@|b@c@2@unset@|c@ba@3@unset@|b@ac@4@unset@|4| | ||
| 634 | '' opt abc 0 "" --abc | ||
| 635 | |abc@unset@2@unset@|2| | ||
| 636 | '' opt abc no_argument "" --abc | ||
| 637 | |abc@unset@2@unset@|2| | ||
| 638 | '' opt abc no_argument "" --abc=foo | ||
| 639 | |?@unset@2@option `--abc' doesn't allow an argument@option `--abc' doesn't allow an argument|2| | ||
| 640 | '' opt abc no_argument "" --abc foo | ||
| 641 | |abc@unset@2@unset@|2| | ||
| 642 | '' opt abc 1 "" --abc=foo | ||
| 643 | |abc@foo@2@unset@|2| | ||
| 644 | '' opt abc required_argument "" --abc foo | ||
| 645 | |abc@foo@3@unset@|3| | ||
| 646 | '' opt abc required_argument "" --abc= | ||
| 647 | |abc@@2@unset@|2| | ||
| 648 | '' opt abc required_argument "" --abc | ||
| 649 | |?@unset@2@option `--abc' requires an argument@option `--abc' requires an argument|2| | ||
| 650 | '' opt abc 2 "" --abc | ||
| 651 | |abc@unset@2@unset@|2| | ||
| 652 | '' opt abc optional_argument "" --abc= | ||
| 653 | |abc@@2@unset@|2| | ||
| 654 | '' opt abc optional_argument "" --abc=foo | ||
| 655 | |abc@foo@2@unset@|2| | ||
| 656 | '' opt abc optional_argument "" --abc --abc | ||
| 657 | |abc@unset@2@unset@|abc@unset@3@unset@|3| | ||
| 658 | '' opt abc 0 abcd 0 "" --abc | ||
| 659 | |abc@unset@2@unset@|2| | ||
| 660 | '' opt abc 0 abd 0 "" --ab | ||
| 661 | |?@unset@2@option `--ab' is ambiguous@option `--ab' is ambiguous|2| | ||
| 662 | '' opt abc 0 abcd 0 "" --ab | ||
| 663 | |?@unset@2@option `--ab' is ambiguous@option `--ab' is ambiguous|2| | ||
| 664 | '' opt abc 0 abc 1 "" --ab | ||
| 665 | |abc@unset@2@unset@|2| | ||
| 666 | '' opt abc 0 abc 1 "" --abc | ||
| 667 | |abc@unset@2@unset@|2| | ||
| 668 | '' opt abc 0 abc 1 "" --ab | ||
| 669 | |abc@unset@2@unset@|2| | ||
| 670 | '' opt abc 0 acd 0 "" --ab | ||
| 671 | |abc@unset@2@unset@|2| | ||
| 672 | abc:d:e::f:: opt ab 0 ac 1 bc 2 cd 1 cde 2 "" -abcdef -a -f -c --a --a= --b=foo -fg | ||
| 673 | |a@unset@1@unset@|b@unset@1@unset@|c@def@2@unset@|a@unset@3@unset@|f@unset@4@unset@|c@--a@6@unset@|?@unset@7@option `--a' is ambiguous@option `--a' is ambiguous|bc@foo@8@unset@|f@g@9@unset@|9| | ||
| 674 | : '' '' a | ||
| 675 | |1|getopts_long: invalid variable name: `' | ||
| 676 | : 1a '' | ||
| 677 | |1|getopts_long: invalid variable name: `1a' | ||
| 678 | - a | ||
| 679 | |1|getopts_long: invalid option specification: `-' | ||
| 680 | :a::a:abcd o ab 1 abc 1 abd 1 abe 1 abc 2 '' -aa --ab 1 --abc | ||
| 681 | |a@a@2@unset@|ab@1@4@unset@|:@abc@5@option `--abc' requires an argument@|5| | ||
| 682 | : | ||
| 683 | |1|getopts_long: not enough arguments | ||
| 684 | '\[$' o -- 0 ' ' 1 '#' required_argument '' '-\\\[$' --\ =a --\#=\$\$ | ||
| 685 | |\@unset@1@unset@|\@unset@1@unset@|\@unset@1@unset@|[@unset@1@unset@|$@unset@2@unset@| @a@3@unset@|#@$$@4@unset@|4| | ||
| 686 | : o a 1 b 2 c | ||
| 687 | |1|getopts_long: long option specifications must end in an empty argument | ||
| 688 | : o a 1 b 2 | ||
| 689 | |1|getopts_long: long option specifications must end in an empty argument | ||
| 690 | : o a 1 b 2 c 3 '' --c | ||
| 691 | |1|getopts_long: invalid long option type: `3' | ||
| 692 | ": " o " " 1 '' "- " "-- =1" | ||
| 693 | | @unset@1@unset@| @unset@2@unset@| @1@3@unset@|3| | ||
| 694 | : o a 1 '' --c | ||
| 695 | |:@c@2@bad option: `--c'@|2| | ||
| 696 | : o a 1 '' --c=foo | ||
| 697 | |:@c@2@bad option: `--c'@|2| | ||
| 698 | : o ab 1 ac 1 ad 1 a 1 '' --a=1 | ||
| 699 | |a@1@2@unset@|2| | ||
| 700 | EOF | ||
| 701 | fi | ||
diff --git a/lists.sh b/lists.sh new file mode 100755 index 0000000..c07870a --- /dev/null +++ b/lists.sh | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | |||
| 3 | shopt -s extglob nullglob | ||
| 4 | |||
| 5 | . ./getopts_long.sh | ||
| 6 | |||
| 7 | find_lists() { | ||
| 8 | LISTS=() | ||
| 9 | while read -r -d $'\0'; do | ||
| 10 | local list=$(realpath --relative-to=. "$REPLY") | ||
| 11 | LISTS+=("$list") | ||
| 12 | done < <(find lists -maxdepth 1 -mindepth 1 -type d -not -name '.*' -print0 | sort -z) | ||
| 13 | } | ||
| 14 | |||
| 15 | find_posts() { | ||
| 16 | local list="$1" | ||
| 17 | shift 1 | ||
| 18 | |||
| 19 | POSTS=() | ||
| 20 | for p in "$list"/!(*[!0-9]*); do | ||
| 21 | POSTS+=( "$p" ) | ||
| 22 | done | ||
| 23 | } | ||
| 24 | |||
| 25 | print_lists() { | ||
| 26 | find_lists | ||
| 27 | for x in "${LISTS[@]}"; do | ||
| 28 | printf "%s: %s\n" "$x" "$(<$x/title)" | ||
| 29 | done | ||
| 30 | } | ||
| 31 | |||
| 32 | print_posts() { | ||
| 33 | find_posts "$1" | ||
| 34 | for p in "${POSTS[@]}"; do | ||
| 35 | local post=$(readlink "$p") | ||
| 36 | printf "%s: %s\n" "${p##*/}" "${post##*/}" | ||
| 37 | done | ||
| 38 | } | ||
| 39 | |||
| 40 | add_list() { | ||
| 41 | local list="$1" | ||
| 42 | mkdir lists/"$list" | ||
| 43 | shift 1 | ||
| 44 | echo "$@" >lists/"$list"/title | ||
| 45 | } | ||
| 46 | |||
| 47 | while getopts_long ":la:" opt \ | ||
| 48 | posts required_argument \ | ||
| 49 | "" "$@" | ||
| 50 | do | ||
| 51 | case $opt in | ||
| 52 | l) | ||
| 53 | print_lists | ||
| 54 | exit 0;; | ||
| 55 | a) | ||
| 56 | shift "$(($OPTLIND - 1))" | ||
| 57 | add_list "$OPTLARG" "$@" | ||
| 58 | exit 0;; | ||
| 59 | posts) | ||
| 60 | print_posts "$OPTLARG" | ||
| 61 | ;; | ||
| 62 | :) | ||
| 63 | printf >&2 '%s: %s\n' "${0##*/}" "$OPTLERR" | ||
| 64 | exit 1;; | ||
| 65 | esac | ||
| 66 | done | ||
