xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/getopt.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Parser for command line options.
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerThis module helps scripts to parse the command line arguments in
4*cda5da8dSAndroid Build Coastguard Workersys.argv.  It supports the same conventions as the Unix getopt()
5*cda5da8dSAndroid Build Coastguard Workerfunction (including the special meanings of arguments of the form `-'
6*cda5da8dSAndroid Build Coastguard Workerand `--').  Long options similar to those supported by GNU software
7*cda5da8dSAndroid Build Coastguard Workermay be used as well via an optional third argument.  This module
8*cda5da8dSAndroid Build Coastguard Workerprovides two functions and an exception:
9*cda5da8dSAndroid Build Coastguard Worker
10*cda5da8dSAndroid Build Coastguard Workergetopt() -- Parse command line options
11*cda5da8dSAndroid Build Coastguard Workergnu_getopt() -- Like getopt(), but allow option and non-option arguments
12*cda5da8dSAndroid Build Coastguard Workerto be intermixed.
13*cda5da8dSAndroid Build Coastguard WorkerGetoptError -- exception (class) raised with 'opt' attribute, which is the
14*cda5da8dSAndroid Build Coastguard Workeroption involved with the exception.
15*cda5da8dSAndroid Build Coastguard Worker"""
16*cda5da8dSAndroid Build Coastguard Worker
17*cda5da8dSAndroid Build Coastguard Worker# Long option support added by Lars Wirzenius <[email protected]>.
18*cda5da8dSAndroid Build Coastguard Worker#
19*cda5da8dSAndroid Build Coastguard Worker# Gerrit Holl <[email protected]> moved the string-based exceptions
20*cda5da8dSAndroid Build Coastguard Worker# to class-based exceptions.
21*cda5da8dSAndroid Build Coastguard Worker#
22*cda5da8dSAndroid Build Coastguard Worker# Peter Åstrand <[email protected]> added gnu_getopt().
23*cda5da8dSAndroid Build Coastguard Worker#
24*cda5da8dSAndroid Build Coastguard Worker# TODO for gnu_getopt():
25*cda5da8dSAndroid Build Coastguard Worker#
26*cda5da8dSAndroid Build Coastguard Worker# - GNU getopt_long_only mechanism
27*cda5da8dSAndroid Build Coastguard Worker# - allow the caller to specify ordering
28*cda5da8dSAndroid Build Coastguard Worker# - RETURN_IN_ORDER option
29*cda5da8dSAndroid Build Coastguard Worker# - GNU extension with '-' as first character of option string
30*cda5da8dSAndroid Build Coastguard Worker# - optional arguments, specified by double colons
31*cda5da8dSAndroid Build Coastguard Worker# - an option string with a W followed by semicolon should
32*cda5da8dSAndroid Build Coastguard Worker#   treat "-W foo" as "--foo"
33*cda5da8dSAndroid Build Coastguard Worker
34*cda5da8dSAndroid Build Coastguard Worker__all__ = ["GetoptError","error","getopt","gnu_getopt"]
35*cda5da8dSAndroid Build Coastguard Worker
36*cda5da8dSAndroid Build Coastguard Workerimport os
37*cda5da8dSAndroid Build Coastguard Workertry:
38*cda5da8dSAndroid Build Coastguard Worker    from gettext import gettext as _
39*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:
40*cda5da8dSAndroid Build Coastguard Worker    # Bootstrapping Python: gettext's dependencies not built yet
41*cda5da8dSAndroid Build Coastguard Worker    def _(s): return s
42*cda5da8dSAndroid Build Coastguard Worker
43*cda5da8dSAndroid Build Coastguard Workerclass GetoptError(Exception):
44*cda5da8dSAndroid Build Coastguard Worker    opt = ''
45*cda5da8dSAndroid Build Coastguard Worker    msg = ''
46*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, msg, opt=''):
47*cda5da8dSAndroid Build Coastguard Worker        self.msg = msg
48*cda5da8dSAndroid Build Coastguard Worker        self.opt = opt
49*cda5da8dSAndroid Build Coastguard Worker        Exception.__init__(self, msg, opt)
50*cda5da8dSAndroid Build Coastguard Worker
51*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
52*cda5da8dSAndroid Build Coastguard Worker        return self.msg
53*cda5da8dSAndroid Build Coastguard Worker
54*cda5da8dSAndroid Build Coastguard Workererror = GetoptError # backward compatibility
55*cda5da8dSAndroid Build Coastguard Worker
56*cda5da8dSAndroid Build Coastguard Workerdef getopt(args, shortopts, longopts = []):
57*cda5da8dSAndroid Build Coastguard Worker    """getopt(args, options[, long_options]) -> opts, args
58*cda5da8dSAndroid Build Coastguard Worker
59*cda5da8dSAndroid Build Coastguard Worker    Parses command line options and parameter list.  args is the
60*cda5da8dSAndroid Build Coastguard Worker    argument list to be parsed, without the leading reference to the
61*cda5da8dSAndroid Build Coastguard Worker    running program.  Typically, this means "sys.argv[1:]".  shortopts
62*cda5da8dSAndroid Build Coastguard Worker    is the string of option letters that the script wants to
63*cda5da8dSAndroid Build Coastguard Worker    recognize, with options that require an argument followed by a
64*cda5da8dSAndroid Build Coastguard Worker    colon (i.e., the same format that Unix getopt() uses).  If
65*cda5da8dSAndroid Build Coastguard Worker    specified, longopts is a list of strings with the names of the
66*cda5da8dSAndroid Build Coastguard Worker    long options which should be supported.  The leading '--'
67*cda5da8dSAndroid Build Coastguard Worker    characters should not be included in the option name.  Options
68*cda5da8dSAndroid Build Coastguard Worker    which require an argument should be followed by an equal sign
69*cda5da8dSAndroid Build Coastguard Worker    ('=').
70*cda5da8dSAndroid Build Coastguard Worker
71*cda5da8dSAndroid Build Coastguard Worker    The return value consists of two elements: the first is a list of
72*cda5da8dSAndroid Build Coastguard Worker    (option, value) pairs; the second is the list of program arguments
73*cda5da8dSAndroid Build Coastguard Worker    left after the option list was stripped (this is a trailing slice
74*cda5da8dSAndroid Build Coastguard Worker    of the first argument).  Each option-and-value pair returned has
75*cda5da8dSAndroid Build Coastguard Worker    the option as its first element, prefixed with a hyphen (e.g.,
76*cda5da8dSAndroid Build Coastguard Worker    '-x'), and the option argument as its second element, or an empty
77*cda5da8dSAndroid Build Coastguard Worker    string if the option has no argument.  The options occur in the
78*cda5da8dSAndroid Build Coastguard Worker    list in the same order in which they were found, thus allowing
79*cda5da8dSAndroid Build Coastguard Worker    multiple occurrences.  Long and short options may be mixed.
80*cda5da8dSAndroid Build Coastguard Worker
81*cda5da8dSAndroid Build Coastguard Worker    """
82*cda5da8dSAndroid Build Coastguard Worker
83*cda5da8dSAndroid Build Coastguard Worker    opts = []
84*cda5da8dSAndroid Build Coastguard Worker    if type(longopts) == type(""):
85*cda5da8dSAndroid Build Coastguard Worker        longopts = [longopts]
86*cda5da8dSAndroid Build Coastguard Worker    else:
87*cda5da8dSAndroid Build Coastguard Worker        longopts = list(longopts)
88*cda5da8dSAndroid Build Coastguard Worker    while args and args[0].startswith('-') and args[0] != '-':
89*cda5da8dSAndroid Build Coastguard Worker        if args[0] == '--':
90*cda5da8dSAndroid Build Coastguard Worker            args = args[1:]
91*cda5da8dSAndroid Build Coastguard Worker            break
92*cda5da8dSAndroid Build Coastguard Worker        if args[0].startswith('--'):
93*cda5da8dSAndroid Build Coastguard Worker            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
94*cda5da8dSAndroid Build Coastguard Worker        else:
95*cda5da8dSAndroid Build Coastguard Worker            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
96*cda5da8dSAndroid Build Coastguard Worker
97*cda5da8dSAndroid Build Coastguard Worker    return opts, args
98*cda5da8dSAndroid Build Coastguard Worker
99*cda5da8dSAndroid Build Coastguard Workerdef gnu_getopt(args, shortopts, longopts = []):
100*cda5da8dSAndroid Build Coastguard Worker    """getopt(args, options[, long_options]) -> opts, args
101*cda5da8dSAndroid Build Coastguard Worker
102*cda5da8dSAndroid Build Coastguard Worker    This function works like getopt(), except that GNU style scanning
103*cda5da8dSAndroid Build Coastguard Worker    mode is used by default. This means that option and non-option
104*cda5da8dSAndroid Build Coastguard Worker    arguments may be intermixed. The getopt() function stops
105*cda5da8dSAndroid Build Coastguard Worker    processing options as soon as a non-option argument is
106*cda5da8dSAndroid Build Coastguard Worker    encountered.
107*cda5da8dSAndroid Build Coastguard Worker
108*cda5da8dSAndroid Build Coastguard Worker    If the first character of the option string is `+', or if the
109*cda5da8dSAndroid Build Coastguard Worker    environment variable POSIXLY_CORRECT is set, then option
110*cda5da8dSAndroid Build Coastguard Worker    processing stops as soon as a non-option argument is encountered.
111*cda5da8dSAndroid Build Coastguard Worker
112*cda5da8dSAndroid Build Coastguard Worker    """
113*cda5da8dSAndroid Build Coastguard Worker
114*cda5da8dSAndroid Build Coastguard Worker    opts = []
115*cda5da8dSAndroid Build Coastguard Worker    prog_args = []
116*cda5da8dSAndroid Build Coastguard Worker    if isinstance(longopts, str):
117*cda5da8dSAndroid Build Coastguard Worker        longopts = [longopts]
118*cda5da8dSAndroid Build Coastguard Worker    else:
119*cda5da8dSAndroid Build Coastguard Worker        longopts = list(longopts)
120*cda5da8dSAndroid Build Coastguard Worker
121*cda5da8dSAndroid Build Coastguard Worker    # Allow options after non-option arguments?
122*cda5da8dSAndroid Build Coastguard Worker    if shortopts.startswith('+'):
123*cda5da8dSAndroid Build Coastguard Worker        shortopts = shortopts[1:]
124*cda5da8dSAndroid Build Coastguard Worker        all_options_first = True
125*cda5da8dSAndroid Build Coastguard Worker    elif os.environ.get("POSIXLY_CORRECT"):
126*cda5da8dSAndroid Build Coastguard Worker        all_options_first = True
127*cda5da8dSAndroid Build Coastguard Worker    else:
128*cda5da8dSAndroid Build Coastguard Worker        all_options_first = False
129*cda5da8dSAndroid Build Coastguard Worker
130*cda5da8dSAndroid Build Coastguard Worker    while args:
131*cda5da8dSAndroid Build Coastguard Worker        if args[0] == '--':
132*cda5da8dSAndroid Build Coastguard Worker            prog_args += args[1:]
133*cda5da8dSAndroid Build Coastguard Worker            break
134*cda5da8dSAndroid Build Coastguard Worker
135*cda5da8dSAndroid Build Coastguard Worker        if args[0][:2] == '--':
136*cda5da8dSAndroid Build Coastguard Worker            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
137*cda5da8dSAndroid Build Coastguard Worker        elif args[0][:1] == '-' and args[0] != '-':
138*cda5da8dSAndroid Build Coastguard Worker            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
139*cda5da8dSAndroid Build Coastguard Worker        else:
140*cda5da8dSAndroid Build Coastguard Worker            if all_options_first:
141*cda5da8dSAndroid Build Coastguard Worker                prog_args += args
142*cda5da8dSAndroid Build Coastguard Worker                break
143*cda5da8dSAndroid Build Coastguard Worker            else:
144*cda5da8dSAndroid Build Coastguard Worker                prog_args.append(args[0])
145*cda5da8dSAndroid Build Coastguard Worker                args = args[1:]
146*cda5da8dSAndroid Build Coastguard Worker
147*cda5da8dSAndroid Build Coastguard Worker    return opts, prog_args
148*cda5da8dSAndroid Build Coastguard Worker
149*cda5da8dSAndroid Build Coastguard Workerdef do_longs(opts, opt, longopts, args):
150*cda5da8dSAndroid Build Coastguard Worker    try:
151*cda5da8dSAndroid Build Coastguard Worker        i = opt.index('=')
152*cda5da8dSAndroid Build Coastguard Worker    except ValueError:
153*cda5da8dSAndroid Build Coastguard Worker        optarg = None
154*cda5da8dSAndroid Build Coastguard Worker    else:
155*cda5da8dSAndroid Build Coastguard Worker        opt, optarg = opt[:i], opt[i+1:]
156*cda5da8dSAndroid Build Coastguard Worker
157*cda5da8dSAndroid Build Coastguard Worker    has_arg, opt = long_has_args(opt, longopts)
158*cda5da8dSAndroid Build Coastguard Worker    if has_arg:
159*cda5da8dSAndroid Build Coastguard Worker        if optarg is None:
160*cda5da8dSAndroid Build Coastguard Worker            if not args:
161*cda5da8dSAndroid Build Coastguard Worker                raise GetoptError(_('option --%s requires argument') % opt, opt)
162*cda5da8dSAndroid Build Coastguard Worker            optarg, args = args[0], args[1:]
163*cda5da8dSAndroid Build Coastguard Worker    elif optarg is not None:
164*cda5da8dSAndroid Build Coastguard Worker        raise GetoptError(_('option --%s must not have an argument') % opt, opt)
165*cda5da8dSAndroid Build Coastguard Worker    opts.append(('--' + opt, optarg or ''))
166*cda5da8dSAndroid Build Coastguard Worker    return opts, args
167*cda5da8dSAndroid Build Coastguard Worker
168*cda5da8dSAndroid Build Coastguard Worker# Return:
169*cda5da8dSAndroid Build Coastguard Worker#   has_arg?
170*cda5da8dSAndroid Build Coastguard Worker#   full option name
171*cda5da8dSAndroid Build Coastguard Workerdef long_has_args(opt, longopts):
172*cda5da8dSAndroid Build Coastguard Worker    possibilities = [o for o in longopts if o.startswith(opt)]
173*cda5da8dSAndroid Build Coastguard Worker    if not possibilities:
174*cda5da8dSAndroid Build Coastguard Worker        raise GetoptError(_('option --%s not recognized') % opt, opt)
175*cda5da8dSAndroid Build Coastguard Worker    # Is there an exact match?
176*cda5da8dSAndroid Build Coastguard Worker    if opt in possibilities:
177*cda5da8dSAndroid Build Coastguard Worker        return False, opt
178*cda5da8dSAndroid Build Coastguard Worker    elif opt + '=' in possibilities:
179*cda5da8dSAndroid Build Coastguard Worker        return True, opt
180*cda5da8dSAndroid Build Coastguard Worker    # No exact match, so better be unique.
181*cda5da8dSAndroid Build Coastguard Worker    if len(possibilities) > 1:
182*cda5da8dSAndroid Build Coastguard Worker        # XXX since possibilities contains all valid continuations, might be
183*cda5da8dSAndroid Build Coastguard Worker        # nice to work them into the error msg
184*cda5da8dSAndroid Build Coastguard Worker        raise GetoptError(_('option --%s not a unique prefix') % opt, opt)
185*cda5da8dSAndroid Build Coastguard Worker    assert len(possibilities) == 1
186*cda5da8dSAndroid Build Coastguard Worker    unique_match = possibilities[0]
187*cda5da8dSAndroid Build Coastguard Worker    has_arg = unique_match.endswith('=')
188*cda5da8dSAndroid Build Coastguard Worker    if has_arg:
189*cda5da8dSAndroid Build Coastguard Worker        unique_match = unique_match[:-1]
190*cda5da8dSAndroid Build Coastguard Worker    return has_arg, unique_match
191*cda5da8dSAndroid Build Coastguard Worker
192*cda5da8dSAndroid Build Coastguard Workerdef do_shorts(opts, optstring, shortopts, args):
193*cda5da8dSAndroid Build Coastguard Worker    while optstring != '':
194*cda5da8dSAndroid Build Coastguard Worker        opt, optstring = optstring[0], optstring[1:]
195*cda5da8dSAndroid Build Coastguard Worker        if short_has_arg(opt, shortopts):
196*cda5da8dSAndroid Build Coastguard Worker            if optstring == '':
197*cda5da8dSAndroid Build Coastguard Worker                if not args:
198*cda5da8dSAndroid Build Coastguard Worker                    raise GetoptError(_('option -%s requires argument') % opt,
199*cda5da8dSAndroid Build Coastguard Worker                                      opt)
200*cda5da8dSAndroid Build Coastguard Worker                optstring, args = args[0], args[1:]
201*cda5da8dSAndroid Build Coastguard Worker            optarg, optstring = optstring, ''
202*cda5da8dSAndroid Build Coastguard Worker        else:
203*cda5da8dSAndroid Build Coastguard Worker            optarg = ''
204*cda5da8dSAndroid Build Coastguard Worker        opts.append(('-' + opt, optarg))
205*cda5da8dSAndroid Build Coastguard Worker    return opts, args
206*cda5da8dSAndroid Build Coastguard Worker
207*cda5da8dSAndroid Build Coastguard Workerdef short_has_arg(opt, shortopts):
208*cda5da8dSAndroid Build Coastguard Worker    for i in range(len(shortopts)):
209*cda5da8dSAndroid Build Coastguard Worker        if opt == shortopts[i] != ':':
210*cda5da8dSAndroid Build Coastguard Worker            return shortopts.startswith(':', i+1)
211*cda5da8dSAndroid Build Coastguard Worker    raise GetoptError(_('option -%s not recognized') % opt, opt)
212*cda5da8dSAndroid Build Coastguard Worker
213*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__':
214*cda5da8dSAndroid Build Coastguard Worker    import sys
215*cda5da8dSAndroid Build Coastguard Worker    print(getopt(sys.argv[1:], "a:b", ["alpha=", "beta"]))
216