summaryrefslogtreecommitdiff
path: root/scripts/library/parseopts.sh
blob: 11589ce3160e0c2a9c72a9f233d43e66acde269d (plain)
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
# getopt-like parser
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)
                                opt=${longmatch%:}
                                if [[ $longmatch = *: ]]; then
                                        return 1
                                else
                                        return 0
                                fi ;;
                        0)
                                # fail, no match found
                                return 255 ;;
                        *)
                                # fail, ambiguous match
                                printf "@SCRIPTNAME@: $(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}

                                        # option doesn't exist
                                        if [[ $shortopts != *$opt* ]]; then
                                                printf "@SCRIPTNAME@: $(gettext "invalid option") -- '%s'\n" "$opt" >&2
                                                OPTRET=(--)
                                                return 1
                                        fi

                                        OPTRET+=("-$opt")
                                        # option requires optarg
                                        if [[ $shortopts = *$opt:* ]]; then
                                                # if we're not at the end of the option chunk, the rest is the optarg
                                                if (( i < ${#1} - 1 )); then
                                                        OPTRET+=("${1:i+1}")
                                                        break
                                                # if we're at the end, grab the the next positional, if it exists
                                                elif (( i == ${#1} - 1 )) && [[ $2 ]]; then
                                                        OPTRET+=("$2")
                                                        shift
                                                        break
                                                # parse failure
                                                else
                                                        printf "@SCRIPTNAME@: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2
                                                        OPTRET=(--)
                                                        return 1
                                                fi
                                        fi
                                done
                                ;;
                        --?*=*|--?*) # long option
                                IFS='=' read -r opt optarg <<< "${1#--}"
                                longoptmatch "$opt"
                                case $? in
                                        0)
                                                # parse failure
                                                if [[ $optarg ]]; then
                                                        printf "@SCRIPTNAME@: $(gettext "option '%s' does not allow an argument")\n" "--$opt" >&2
                                                        OPTRET=(--)
                                                        return 1
                                                # --longopt
                                                else
                                                        OPTRET+=("--$opt")
                                                        shift
                                                        continue 2
                                                fi
                                                ;;
                                        1)
                                                # --longopt=optarg
                                                if [[ $optarg ]]; then
                                                        OPTRET+=("--$opt" "$optarg")
                                                        shift
                                                # --longopt optarg
                                                elif [[ $2 ]]; then
                                                        OPTRET+=("--$opt" "$2" )
                                                        shift 2
                                                # parse failure
                                                else
                                                        printf "@SCRIPTNAME@: $(gettext "option '%s' requires an argument")\n" "--$opt" >&2
                                                        OPTRET=(--)
                                                        return 1
                                                fi
                                                continue 2
                                                ;;
                                        254)
                                                # ambiguous option -- error was reported for us by longoptmatch()
                                                OPTRET=(--)
                                                return 1
                                                ;;
                                        255)
                                                # parse failure
                                                printf "@SCRIPTNAME@: $(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
}