xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/shlex.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""A lexical analyzer class for simple shell-like syntaxes."""
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard Worker# Module and documentation by Eric S. Raymond, 21 Dec 1998
4*cda5da8dSAndroid Build Coastguard Worker# Input stacking and error message cleanup added by ESR, March 2000
5*cda5da8dSAndroid Build Coastguard Worker# push_source() and pop_source() made explicit by ESR, January 2001.
6*cda5da8dSAndroid Build Coastguard Worker# Posix compliance, split(), string arguments, and
7*cda5da8dSAndroid Build Coastguard Worker# iterator interface by Gustavo Niemeyer, April 2003.
8*cda5da8dSAndroid Build Coastguard Worker# changes to tokenize more like Posix shells by Vinay Sajip, July 2016.
9*cda5da8dSAndroid Build Coastguard Worker
10*cda5da8dSAndroid Build Coastguard Workerimport os
11*cda5da8dSAndroid Build Coastguard Workerimport re
12*cda5da8dSAndroid Build Coastguard Workerimport sys
13*cda5da8dSAndroid Build Coastguard Workerfrom collections import deque
14*cda5da8dSAndroid Build Coastguard Worker
15*cda5da8dSAndroid Build Coastguard Workerfrom io import StringIO
16*cda5da8dSAndroid Build Coastguard Worker
17*cda5da8dSAndroid Build Coastguard Worker__all__ = ["shlex", "split", "quote", "join"]
18*cda5da8dSAndroid Build Coastguard Worker
19*cda5da8dSAndroid Build Coastguard Workerclass shlex:
20*cda5da8dSAndroid Build Coastguard Worker    "A lexical analyzer class for simple shell-like syntaxes."
21*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, instream=None, infile=None, posix=False,
22*cda5da8dSAndroid Build Coastguard Worker                 punctuation_chars=False):
23*cda5da8dSAndroid Build Coastguard Worker        if isinstance(instream, str):
24*cda5da8dSAndroid Build Coastguard Worker            instream = StringIO(instream)
25*cda5da8dSAndroid Build Coastguard Worker        if instream is not None:
26*cda5da8dSAndroid Build Coastguard Worker            self.instream = instream
27*cda5da8dSAndroid Build Coastguard Worker            self.infile = infile
28*cda5da8dSAndroid Build Coastguard Worker        else:
29*cda5da8dSAndroid Build Coastguard Worker            self.instream = sys.stdin
30*cda5da8dSAndroid Build Coastguard Worker            self.infile = None
31*cda5da8dSAndroid Build Coastguard Worker        self.posix = posix
32*cda5da8dSAndroid Build Coastguard Worker        if posix:
33*cda5da8dSAndroid Build Coastguard Worker            self.eof = None
34*cda5da8dSAndroid Build Coastguard Worker        else:
35*cda5da8dSAndroid Build Coastguard Worker            self.eof = ''
36*cda5da8dSAndroid Build Coastguard Worker        self.commenters = '#'
37*cda5da8dSAndroid Build Coastguard Worker        self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
38*cda5da8dSAndroid Build Coastguard Worker                          'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_')
39*cda5da8dSAndroid Build Coastguard Worker        if self.posix:
40*cda5da8dSAndroid Build Coastguard Worker            self.wordchars += ('ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
41*cda5da8dSAndroid Build Coastguard Worker                               'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
42*cda5da8dSAndroid Build Coastguard Worker        self.whitespace = ' \t\r\n'
43*cda5da8dSAndroid Build Coastguard Worker        self.whitespace_split = False
44*cda5da8dSAndroid Build Coastguard Worker        self.quotes = '\'"'
45*cda5da8dSAndroid Build Coastguard Worker        self.escape = '\\'
46*cda5da8dSAndroid Build Coastguard Worker        self.escapedquotes = '"'
47*cda5da8dSAndroid Build Coastguard Worker        self.state = ' '
48*cda5da8dSAndroid Build Coastguard Worker        self.pushback = deque()
49*cda5da8dSAndroid Build Coastguard Worker        self.lineno = 1
50*cda5da8dSAndroid Build Coastguard Worker        self.debug = 0
51*cda5da8dSAndroid Build Coastguard Worker        self.token = ''
52*cda5da8dSAndroid Build Coastguard Worker        self.filestack = deque()
53*cda5da8dSAndroid Build Coastguard Worker        self.source = None
54*cda5da8dSAndroid Build Coastguard Worker        if not punctuation_chars:
55*cda5da8dSAndroid Build Coastguard Worker            punctuation_chars = ''
56*cda5da8dSAndroid Build Coastguard Worker        elif punctuation_chars is True:
57*cda5da8dSAndroid Build Coastguard Worker            punctuation_chars = '();<>|&'
58*cda5da8dSAndroid Build Coastguard Worker        self._punctuation_chars = punctuation_chars
59*cda5da8dSAndroid Build Coastguard Worker        if punctuation_chars:
60*cda5da8dSAndroid Build Coastguard Worker            # _pushback_chars is a push back queue used by lookahead logic
61*cda5da8dSAndroid Build Coastguard Worker            self._pushback_chars = deque()
62*cda5da8dSAndroid Build Coastguard Worker            # these chars added because allowed in file names, args, wildcards
63*cda5da8dSAndroid Build Coastguard Worker            self.wordchars += '~-./*?='
64*cda5da8dSAndroid Build Coastguard Worker            #remove any punctuation chars from wordchars
65*cda5da8dSAndroid Build Coastguard Worker            t = self.wordchars.maketrans(dict.fromkeys(punctuation_chars))
66*cda5da8dSAndroid Build Coastguard Worker            self.wordchars = self.wordchars.translate(t)
67*cda5da8dSAndroid Build Coastguard Worker
68*cda5da8dSAndroid Build Coastguard Worker    @property
69*cda5da8dSAndroid Build Coastguard Worker    def punctuation_chars(self):
70*cda5da8dSAndroid Build Coastguard Worker        return self._punctuation_chars
71*cda5da8dSAndroid Build Coastguard Worker
72*cda5da8dSAndroid Build Coastguard Worker    def push_token(self, tok):
73*cda5da8dSAndroid Build Coastguard Worker        "Push a token onto the stack popped by the get_token method"
74*cda5da8dSAndroid Build Coastguard Worker        if self.debug >= 1:
75*cda5da8dSAndroid Build Coastguard Worker            print("shlex: pushing token " + repr(tok))
76*cda5da8dSAndroid Build Coastguard Worker        self.pushback.appendleft(tok)
77*cda5da8dSAndroid Build Coastguard Worker
78*cda5da8dSAndroid Build Coastguard Worker    def push_source(self, newstream, newfile=None):
79*cda5da8dSAndroid Build Coastguard Worker        "Push an input source onto the lexer's input source stack."
80*cda5da8dSAndroid Build Coastguard Worker        if isinstance(newstream, str):
81*cda5da8dSAndroid Build Coastguard Worker            newstream = StringIO(newstream)
82*cda5da8dSAndroid Build Coastguard Worker        self.filestack.appendleft((self.infile, self.instream, self.lineno))
83*cda5da8dSAndroid Build Coastguard Worker        self.infile = newfile
84*cda5da8dSAndroid Build Coastguard Worker        self.instream = newstream
85*cda5da8dSAndroid Build Coastguard Worker        self.lineno = 1
86*cda5da8dSAndroid Build Coastguard Worker        if self.debug:
87*cda5da8dSAndroid Build Coastguard Worker            if newfile is not None:
88*cda5da8dSAndroid Build Coastguard Worker                print('shlex: pushing to file %s' % (self.infile,))
89*cda5da8dSAndroid Build Coastguard Worker            else:
90*cda5da8dSAndroid Build Coastguard Worker                print('shlex: pushing to stream %s' % (self.instream,))
91*cda5da8dSAndroid Build Coastguard Worker
92*cda5da8dSAndroid Build Coastguard Worker    def pop_source(self):
93*cda5da8dSAndroid Build Coastguard Worker        "Pop the input source stack."
94*cda5da8dSAndroid Build Coastguard Worker        self.instream.close()
95*cda5da8dSAndroid Build Coastguard Worker        (self.infile, self.instream, self.lineno) = self.filestack.popleft()
96*cda5da8dSAndroid Build Coastguard Worker        if self.debug:
97*cda5da8dSAndroid Build Coastguard Worker            print('shlex: popping to %s, line %d' \
98*cda5da8dSAndroid Build Coastguard Worker                  % (self.instream, self.lineno))
99*cda5da8dSAndroid Build Coastguard Worker        self.state = ' '
100*cda5da8dSAndroid Build Coastguard Worker
101*cda5da8dSAndroid Build Coastguard Worker    def get_token(self):
102*cda5da8dSAndroid Build Coastguard Worker        "Get a token from the input stream (or from stack if it's nonempty)"
103*cda5da8dSAndroid Build Coastguard Worker        if self.pushback:
104*cda5da8dSAndroid Build Coastguard Worker            tok = self.pushback.popleft()
105*cda5da8dSAndroid Build Coastguard Worker            if self.debug >= 1:
106*cda5da8dSAndroid Build Coastguard Worker                print("shlex: popping token " + repr(tok))
107*cda5da8dSAndroid Build Coastguard Worker            return tok
108*cda5da8dSAndroid Build Coastguard Worker        # No pushback.  Get a token.
109*cda5da8dSAndroid Build Coastguard Worker        raw = self.read_token()
110*cda5da8dSAndroid Build Coastguard Worker        # Handle inclusions
111*cda5da8dSAndroid Build Coastguard Worker        if self.source is not None:
112*cda5da8dSAndroid Build Coastguard Worker            while raw == self.source:
113*cda5da8dSAndroid Build Coastguard Worker                spec = self.sourcehook(self.read_token())
114*cda5da8dSAndroid Build Coastguard Worker                if spec:
115*cda5da8dSAndroid Build Coastguard Worker                    (newfile, newstream) = spec
116*cda5da8dSAndroid Build Coastguard Worker                    self.push_source(newstream, newfile)
117*cda5da8dSAndroid Build Coastguard Worker                raw = self.get_token()
118*cda5da8dSAndroid Build Coastguard Worker        # Maybe we got EOF instead?
119*cda5da8dSAndroid Build Coastguard Worker        while raw == self.eof:
120*cda5da8dSAndroid Build Coastguard Worker            if not self.filestack:
121*cda5da8dSAndroid Build Coastguard Worker                return self.eof
122*cda5da8dSAndroid Build Coastguard Worker            else:
123*cda5da8dSAndroid Build Coastguard Worker                self.pop_source()
124*cda5da8dSAndroid Build Coastguard Worker                raw = self.get_token()
125*cda5da8dSAndroid Build Coastguard Worker        # Neither inclusion nor EOF
126*cda5da8dSAndroid Build Coastguard Worker        if self.debug >= 1:
127*cda5da8dSAndroid Build Coastguard Worker            if raw != self.eof:
128*cda5da8dSAndroid Build Coastguard Worker                print("shlex: token=" + repr(raw))
129*cda5da8dSAndroid Build Coastguard Worker            else:
130*cda5da8dSAndroid Build Coastguard Worker                print("shlex: token=EOF")
131*cda5da8dSAndroid Build Coastguard Worker        return raw
132*cda5da8dSAndroid Build Coastguard Worker
133*cda5da8dSAndroid Build Coastguard Worker    def read_token(self):
134*cda5da8dSAndroid Build Coastguard Worker        quoted = False
135*cda5da8dSAndroid Build Coastguard Worker        escapedstate = ' '
136*cda5da8dSAndroid Build Coastguard Worker        while True:
137*cda5da8dSAndroid Build Coastguard Worker            if self.punctuation_chars and self._pushback_chars:
138*cda5da8dSAndroid Build Coastguard Worker                nextchar = self._pushback_chars.pop()
139*cda5da8dSAndroid Build Coastguard Worker            else:
140*cda5da8dSAndroid Build Coastguard Worker                nextchar = self.instream.read(1)
141*cda5da8dSAndroid Build Coastguard Worker            if nextchar == '\n':
142*cda5da8dSAndroid Build Coastguard Worker                self.lineno += 1
143*cda5da8dSAndroid Build Coastguard Worker            if self.debug >= 3:
144*cda5da8dSAndroid Build Coastguard Worker                print("shlex: in state %r I see character: %r" % (self.state,
145*cda5da8dSAndroid Build Coastguard Worker                                                                  nextchar))
146*cda5da8dSAndroid Build Coastguard Worker            if self.state is None:
147*cda5da8dSAndroid Build Coastguard Worker                self.token = ''        # past end of file
148*cda5da8dSAndroid Build Coastguard Worker                break
149*cda5da8dSAndroid Build Coastguard Worker            elif self.state == ' ':
150*cda5da8dSAndroid Build Coastguard Worker                if not nextchar:
151*cda5da8dSAndroid Build Coastguard Worker                    self.state = None  # end of file
152*cda5da8dSAndroid Build Coastguard Worker                    break
153*cda5da8dSAndroid Build Coastguard Worker                elif nextchar in self.whitespace:
154*cda5da8dSAndroid Build Coastguard Worker                    if self.debug >= 2:
155*cda5da8dSAndroid Build Coastguard Worker                        print("shlex: I see whitespace in whitespace state")
156*cda5da8dSAndroid Build Coastguard Worker                    if self.token or (self.posix and quoted):
157*cda5da8dSAndroid Build Coastguard Worker                        break   # emit current token
158*cda5da8dSAndroid Build Coastguard Worker                    else:
159*cda5da8dSAndroid Build Coastguard Worker                        continue
160*cda5da8dSAndroid Build Coastguard Worker                elif nextchar in self.commenters:
161*cda5da8dSAndroid Build Coastguard Worker                    self.instream.readline()
162*cda5da8dSAndroid Build Coastguard Worker                    self.lineno += 1
163*cda5da8dSAndroid Build Coastguard Worker                elif self.posix and nextchar in self.escape:
164*cda5da8dSAndroid Build Coastguard Worker                    escapedstate = 'a'
165*cda5da8dSAndroid Build Coastguard Worker                    self.state = nextchar
166*cda5da8dSAndroid Build Coastguard Worker                elif nextchar in self.wordchars:
167*cda5da8dSAndroid Build Coastguard Worker                    self.token = nextchar
168*cda5da8dSAndroid Build Coastguard Worker                    self.state = 'a'
169*cda5da8dSAndroid Build Coastguard Worker                elif nextchar in self.punctuation_chars:
170*cda5da8dSAndroid Build Coastguard Worker                    self.token = nextchar
171*cda5da8dSAndroid Build Coastguard Worker                    self.state = 'c'
172*cda5da8dSAndroid Build Coastguard Worker                elif nextchar in self.quotes:
173*cda5da8dSAndroid Build Coastguard Worker                    if not self.posix:
174*cda5da8dSAndroid Build Coastguard Worker                        self.token = nextchar
175*cda5da8dSAndroid Build Coastguard Worker                    self.state = nextchar
176*cda5da8dSAndroid Build Coastguard Worker                elif self.whitespace_split:
177*cda5da8dSAndroid Build Coastguard Worker                    self.token = nextchar
178*cda5da8dSAndroid Build Coastguard Worker                    self.state = 'a'
179*cda5da8dSAndroid Build Coastguard Worker                else:
180*cda5da8dSAndroid Build Coastguard Worker                    self.token = nextchar
181*cda5da8dSAndroid Build Coastguard Worker                    if self.token or (self.posix and quoted):
182*cda5da8dSAndroid Build Coastguard Worker                        break   # emit current token
183*cda5da8dSAndroid Build Coastguard Worker                    else:
184*cda5da8dSAndroid Build Coastguard Worker                        continue
185*cda5da8dSAndroid Build Coastguard Worker            elif self.state in self.quotes:
186*cda5da8dSAndroid Build Coastguard Worker                quoted = True
187*cda5da8dSAndroid Build Coastguard Worker                if not nextchar:      # end of file
188*cda5da8dSAndroid Build Coastguard Worker                    if self.debug >= 2:
189*cda5da8dSAndroid Build Coastguard Worker                        print("shlex: I see EOF in quotes state")
190*cda5da8dSAndroid Build Coastguard Worker                    # XXX what error should be raised here?
191*cda5da8dSAndroid Build Coastguard Worker                    raise ValueError("No closing quotation")
192*cda5da8dSAndroid Build Coastguard Worker                if nextchar == self.state:
193*cda5da8dSAndroid Build Coastguard Worker                    if not self.posix:
194*cda5da8dSAndroid Build Coastguard Worker                        self.token += nextchar
195*cda5da8dSAndroid Build Coastguard Worker                        self.state = ' '
196*cda5da8dSAndroid Build Coastguard Worker                        break
197*cda5da8dSAndroid Build Coastguard Worker                    else:
198*cda5da8dSAndroid Build Coastguard Worker                        self.state = 'a'
199*cda5da8dSAndroid Build Coastguard Worker                elif (self.posix and nextchar in self.escape and self.state
200*cda5da8dSAndroid Build Coastguard Worker                      in self.escapedquotes):
201*cda5da8dSAndroid Build Coastguard Worker                    escapedstate = self.state
202*cda5da8dSAndroid Build Coastguard Worker                    self.state = nextchar
203*cda5da8dSAndroid Build Coastguard Worker                else:
204*cda5da8dSAndroid Build Coastguard Worker                    self.token += nextchar
205*cda5da8dSAndroid Build Coastguard Worker            elif self.state in self.escape:
206*cda5da8dSAndroid Build Coastguard Worker                if not nextchar:      # end of file
207*cda5da8dSAndroid Build Coastguard Worker                    if self.debug >= 2:
208*cda5da8dSAndroid Build Coastguard Worker                        print("shlex: I see EOF in escape state")
209*cda5da8dSAndroid Build Coastguard Worker                    # XXX what error should be raised here?
210*cda5da8dSAndroid Build Coastguard Worker                    raise ValueError("No escaped character")
211*cda5da8dSAndroid Build Coastguard Worker                # In posix shells, only the quote itself or the escape
212*cda5da8dSAndroid Build Coastguard Worker                # character may be escaped within quotes.
213*cda5da8dSAndroid Build Coastguard Worker                if (escapedstate in self.quotes and
214*cda5da8dSAndroid Build Coastguard Worker                        nextchar != self.state and nextchar != escapedstate):
215*cda5da8dSAndroid Build Coastguard Worker                    self.token += self.state
216*cda5da8dSAndroid Build Coastguard Worker                self.token += nextchar
217*cda5da8dSAndroid Build Coastguard Worker                self.state = escapedstate
218*cda5da8dSAndroid Build Coastguard Worker            elif self.state in ('a', 'c'):
219*cda5da8dSAndroid Build Coastguard Worker                if not nextchar:
220*cda5da8dSAndroid Build Coastguard Worker                    self.state = None   # end of file
221*cda5da8dSAndroid Build Coastguard Worker                    break
222*cda5da8dSAndroid Build Coastguard Worker                elif nextchar in self.whitespace:
223*cda5da8dSAndroid Build Coastguard Worker                    if self.debug >= 2:
224*cda5da8dSAndroid Build Coastguard Worker                        print("shlex: I see whitespace in word state")
225*cda5da8dSAndroid Build Coastguard Worker                    self.state = ' '
226*cda5da8dSAndroid Build Coastguard Worker                    if self.token or (self.posix and quoted):
227*cda5da8dSAndroid Build Coastguard Worker                        break   # emit current token
228*cda5da8dSAndroid Build Coastguard Worker                    else:
229*cda5da8dSAndroid Build Coastguard Worker                        continue
230*cda5da8dSAndroid Build Coastguard Worker                elif nextchar in self.commenters:
231*cda5da8dSAndroid Build Coastguard Worker                    self.instream.readline()
232*cda5da8dSAndroid Build Coastguard Worker                    self.lineno += 1
233*cda5da8dSAndroid Build Coastguard Worker                    if self.posix:
234*cda5da8dSAndroid Build Coastguard Worker                        self.state = ' '
235*cda5da8dSAndroid Build Coastguard Worker                        if self.token or (self.posix and quoted):
236*cda5da8dSAndroid Build Coastguard Worker                            break   # emit current token
237*cda5da8dSAndroid Build Coastguard Worker                        else:
238*cda5da8dSAndroid Build Coastguard Worker                            continue
239*cda5da8dSAndroid Build Coastguard Worker                elif self.state == 'c':
240*cda5da8dSAndroid Build Coastguard Worker                    if nextchar in self.punctuation_chars:
241*cda5da8dSAndroid Build Coastguard Worker                        self.token += nextchar
242*cda5da8dSAndroid Build Coastguard Worker                    else:
243*cda5da8dSAndroid Build Coastguard Worker                        if nextchar not in self.whitespace:
244*cda5da8dSAndroid Build Coastguard Worker                            self._pushback_chars.append(nextchar)
245*cda5da8dSAndroid Build Coastguard Worker                        self.state = ' '
246*cda5da8dSAndroid Build Coastguard Worker                        break
247*cda5da8dSAndroid Build Coastguard Worker                elif self.posix and nextchar in self.quotes:
248*cda5da8dSAndroid Build Coastguard Worker                    self.state = nextchar
249*cda5da8dSAndroid Build Coastguard Worker                elif self.posix and nextchar in self.escape:
250*cda5da8dSAndroid Build Coastguard Worker                    escapedstate = 'a'
251*cda5da8dSAndroid Build Coastguard Worker                    self.state = nextchar
252*cda5da8dSAndroid Build Coastguard Worker                elif (nextchar in self.wordchars or nextchar in self.quotes
253*cda5da8dSAndroid Build Coastguard Worker                      or (self.whitespace_split and
254*cda5da8dSAndroid Build Coastguard Worker                          nextchar not in self.punctuation_chars)):
255*cda5da8dSAndroid Build Coastguard Worker                    self.token += nextchar
256*cda5da8dSAndroid Build Coastguard Worker                else:
257*cda5da8dSAndroid Build Coastguard Worker                    if self.punctuation_chars:
258*cda5da8dSAndroid Build Coastguard Worker                        self._pushback_chars.append(nextchar)
259*cda5da8dSAndroid Build Coastguard Worker                    else:
260*cda5da8dSAndroid Build Coastguard Worker                        self.pushback.appendleft(nextchar)
261*cda5da8dSAndroid Build Coastguard Worker                    if self.debug >= 2:
262*cda5da8dSAndroid Build Coastguard Worker                        print("shlex: I see punctuation in word state")
263*cda5da8dSAndroid Build Coastguard Worker                    self.state = ' '
264*cda5da8dSAndroid Build Coastguard Worker                    if self.token or (self.posix and quoted):
265*cda5da8dSAndroid Build Coastguard Worker                        break   # emit current token
266*cda5da8dSAndroid Build Coastguard Worker                    else:
267*cda5da8dSAndroid Build Coastguard Worker                        continue
268*cda5da8dSAndroid Build Coastguard Worker        result = self.token
269*cda5da8dSAndroid Build Coastguard Worker        self.token = ''
270*cda5da8dSAndroid Build Coastguard Worker        if self.posix and not quoted and result == '':
271*cda5da8dSAndroid Build Coastguard Worker            result = None
272*cda5da8dSAndroid Build Coastguard Worker        if self.debug > 1:
273*cda5da8dSAndroid Build Coastguard Worker            if result:
274*cda5da8dSAndroid Build Coastguard Worker                print("shlex: raw token=" + repr(result))
275*cda5da8dSAndroid Build Coastguard Worker            else:
276*cda5da8dSAndroid Build Coastguard Worker                print("shlex: raw token=EOF")
277*cda5da8dSAndroid Build Coastguard Worker        return result
278*cda5da8dSAndroid Build Coastguard Worker
279*cda5da8dSAndroid Build Coastguard Worker    def sourcehook(self, newfile):
280*cda5da8dSAndroid Build Coastguard Worker        "Hook called on a filename to be sourced."
281*cda5da8dSAndroid Build Coastguard Worker        if newfile[0] == '"':
282*cda5da8dSAndroid Build Coastguard Worker            newfile = newfile[1:-1]
283*cda5da8dSAndroid Build Coastguard Worker        # This implements cpp-like semantics for relative-path inclusion.
284*cda5da8dSAndroid Build Coastguard Worker        if isinstance(self.infile, str) and not os.path.isabs(newfile):
285*cda5da8dSAndroid Build Coastguard Worker            newfile = os.path.join(os.path.dirname(self.infile), newfile)
286*cda5da8dSAndroid Build Coastguard Worker        return (newfile, open(newfile, "r"))
287*cda5da8dSAndroid Build Coastguard Worker
288*cda5da8dSAndroid Build Coastguard Worker    def error_leader(self, infile=None, lineno=None):
289*cda5da8dSAndroid Build Coastguard Worker        "Emit a C-compiler-like, Emacs-friendly error-message leader."
290*cda5da8dSAndroid Build Coastguard Worker        if infile is None:
291*cda5da8dSAndroid Build Coastguard Worker            infile = self.infile
292*cda5da8dSAndroid Build Coastguard Worker        if lineno is None:
293*cda5da8dSAndroid Build Coastguard Worker            lineno = self.lineno
294*cda5da8dSAndroid Build Coastguard Worker        return "\"%s\", line %d: " % (infile, lineno)
295*cda5da8dSAndroid Build Coastguard Worker
296*cda5da8dSAndroid Build Coastguard Worker    def __iter__(self):
297*cda5da8dSAndroid Build Coastguard Worker        return self
298*cda5da8dSAndroid Build Coastguard Worker
299*cda5da8dSAndroid Build Coastguard Worker    def __next__(self):
300*cda5da8dSAndroid Build Coastguard Worker        token = self.get_token()
301*cda5da8dSAndroid Build Coastguard Worker        if token == self.eof:
302*cda5da8dSAndroid Build Coastguard Worker            raise StopIteration
303*cda5da8dSAndroid Build Coastguard Worker        return token
304*cda5da8dSAndroid Build Coastguard Worker
305*cda5da8dSAndroid Build Coastguard Workerdef split(s, comments=False, posix=True):
306*cda5da8dSAndroid Build Coastguard Worker    """Split the string *s* using shell-like syntax."""
307*cda5da8dSAndroid Build Coastguard Worker    if s is None:
308*cda5da8dSAndroid Build Coastguard Worker        import warnings
309*cda5da8dSAndroid Build Coastguard Worker        warnings.warn("Passing None for 's' to shlex.split() is deprecated.",
310*cda5da8dSAndroid Build Coastguard Worker                      DeprecationWarning, stacklevel=2)
311*cda5da8dSAndroid Build Coastguard Worker    lex = shlex(s, posix=posix)
312*cda5da8dSAndroid Build Coastguard Worker    lex.whitespace_split = True
313*cda5da8dSAndroid Build Coastguard Worker    if not comments:
314*cda5da8dSAndroid Build Coastguard Worker        lex.commenters = ''
315*cda5da8dSAndroid Build Coastguard Worker    return list(lex)
316*cda5da8dSAndroid Build Coastguard Worker
317*cda5da8dSAndroid Build Coastguard Worker
318*cda5da8dSAndroid Build Coastguard Workerdef join(split_command):
319*cda5da8dSAndroid Build Coastguard Worker    """Return a shell-escaped string from *split_command*."""
320*cda5da8dSAndroid Build Coastguard Worker    return ' '.join(quote(arg) for arg in split_command)
321*cda5da8dSAndroid Build Coastguard Worker
322*cda5da8dSAndroid Build Coastguard Worker
323*cda5da8dSAndroid Build Coastguard Worker_find_unsafe = re.compile(r'[^\w@%+=:,./-]', re.ASCII).search
324*cda5da8dSAndroid Build Coastguard Worker
325*cda5da8dSAndroid Build Coastguard Workerdef quote(s):
326*cda5da8dSAndroid Build Coastguard Worker    """Return a shell-escaped version of the string *s*."""
327*cda5da8dSAndroid Build Coastguard Worker    if not s:
328*cda5da8dSAndroid Build Coastguard Worker        return "''"
329*cda5da8dSAndroid Build Coastguard Worker    if _find_unsafe(s) is None:
330*cda5da8dSAndroid Build Coastguard Worker        return s
331*cda5da8dSAndroid Build Coastguard Worker
332*cda5da8dSAndroid Build Coastguard Worker    # use single quotes, and put single quotes into double quotes
333*cda5da8dSAndroid Build Coastguard Worker    # the string $'b is then quoted as '$'"'"'b'
334*cda5da8dSAndroid Build Coastguard Worker    return "'" + s.replace("'", "'\"'\"'") + "'"
335*cda5da8dSAndroid Build Coastguard Worker
336*cda5da8dSAndroid Build Coastguard Worker
337*cda5da8dSAndroid Build Coastguard Workerdef _print_tokens(lexer):
338*cda5da8dSAndroid Build Coastguard Worker    while 1:
339*cda5da8dSAndroid Build Coastguard Worker        tt = lexer.get_token()
340*cda5da8dSAndroid Build Coastguard Worker        if not tt:
341*cda5da8dSAndroid Build Coastguard Worker            break
342*cda5da8dSAndroid Build Coastguard Worker        print("Token: " + repr(tt))
343*cda5da8dSAndroid Build Coastguard Worker
344*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__':
345*cda5da8dSAndroid Build Coastguard Worker    if len(sys.argv) == 1:
346*cda5da8dSAndroid Build Coastguard Worker        _print_tokens(shlex())
347*cda5da8dSAndroid Build Coastguard Worker    else:
348*cda5da8dSAndroid Build Coastguard Worker        fn = sys.argv[1]
349*cda5da8dSAndroid Build Coastguard Worker        with open(fn) as f:
350*cda5da8dSAndroid Build Coastguard Worker            _print_tokens(shlex(f, fn))
351