Beispiel #1
0
    def _parse_comsub(self,
                      doublequotes,
                      open,
                      close,
                      parsingcommand=False,
                      dquote=False,
                      firstclose=False):
        peekc = self._getc(False)
        self._ungetc(peekc)

        if peekc == '(':
            return self._parse_matched_pair(doublequotes, open, close)

        count = 1
        dollarok = True

        checkcase = bool(parsingcommand and
                         (doublequotes is None or doublequotes not in "'\"")
                         and not dquote)
        checkcomment = checkcase

        startlineno = self._line_number
        heredelim = ''
        stripdoc = insideheredoc = insidecomment = insideword = insidecase = False
        readingheredocdelim = False
        wasdollar = passnextchar = False
        reservedwordok = True
        lexfirstind = -1
        lexrwlen = 0

        ret = ''

        while count:
            c = self._getc(doublequotes != "'" and not insidecomment
                           and not passnextchar)

            if c is None:
                raise MatchedPairError(
                    startlineno,
                    'unexpected EOF while looking for matching %r' % close,
                    self)

            # bashlex/parse.y L3571
            if c == '\n':
                if readingheredocdelim and heredelim:
                    readingheredocdelim = False
                    insideheredoc = True
                    lexfirstind = len(ret) + 1
                elif insideheredoc:
                    tind = lexfirstind
                    while stripdoc and ret[tind] == '\t':
                        tind += 1
                    if ret[tind:] == heredelim:
                        stripdoc = insideheredoc = False
                        heredelim = ''
                        lexfirstind = -1
                    else:
                        lexfirstind = len(ret) + 1
            # bashlex/parse.y L3599
            if insideheredoc and c == close and count == 1:
                tind = lexfirstind
                while stripdoc and ret[tind] == '\t':
                    tind += 1
                if ret[tind:] == heredelim:
                    stripdoc = insideheredoc = False
                    heredelim = ''
                    lexfirstind = -1

            if insidecomment or insideheredoc:
                ret += c

                if insidecomment and c == '\n':
                    insidecomment = False

                continue

            if passnextchar:
                passnextchar = False
                # XXX is this needed?
                # if doublequotes != "'" and c == '\n':
                #     if ret:
                #         ret = ret[:-1]
                # else:
                #     ret += c
                ret += c
                continue

            if _shellbreak(c):
                insideword = False
            else:
                if insideword:
                    lexwlen += 1
                else:
                    insideword = True
                    lexwlen = 0

            if _shellblank(c) and not readingheredocdelim and not lexrwlen:
                ret += c
                continue

            # bashlex/parse.y L3686
            if readingheredocdelim:
                if lexfirstind == -1 and not _shellbreak(c):
                    lexfirstind = len(ret)
                elif lexfirstind >= 0 and not passnextchar and _shellbreak(c):
                    if not heredelim:
                        nestret = ret[lexfirstind:]
                        heredelim = shutils.removequotes(nestret)
                    if c == '\n':
                        insideheredoc = True
                        readingheredocdelim = False
                        lexfirstind = len(ret) + 1
                    else:
                        lexfirstind = -1

            if not reservedwordok and checkcase and not insidecomment and (
                    _shellmeta(c) or c == '\n'):
                ret += c
                peekc = self._getc(True)
                if c == peekc and c in '&|;':
                    ret += peekc
                    reservedwordok = True
                    lexrwlen = 0
                    continue
                elif c == '\n' or c in '&|;':
                    self._ungetc(peekc)
                    reservedwordok = True
                    lexrwlen = 0
                    continue
                elif c is None:
                    raise MatchedPairError(
                        startlineno,
                        'unexpected EOF while looking for matching %r' % close,
                        self)  # pragma: no coverage
                else:
                    ret = ret[:-1]
                    self._ungetc(peekc)

            # bashlex/parse.y L3761
            if reservedwordok:
                if c.islower():
                    ret += c
                    lexrwlen += 1
                    continue
                elif lexrwlen == 4 and _shellbreak(c):
                    if ret[-4:] == 'case':
                        insidecase = True
                    elif ret[-4:] == 'esac':
                        insidecase = False
                    reservedwordok = False
                elif (checkcomment and c == '#'
                      and (lexrwlen == 0 or (insideword and lexwlen == 0))):
                    pass
                elif (not insidecase and (_shellblank(c) or c == '\n')
                      and lexrwlen == 2 and ret[-2:] == 'do'):
                    lexrwlen = 0
                elif insidecase and c != '\n':
                    reservedwordok = False
                elif not _shellbreak(c):
                    reservedwordok = False

            if not insidecomment and checkcase and c == '<':
                ret += c
                peekc = self._getc(True)
                if peekc is None:
                    raise MatchedPairError(
                        startlineno,
                        'unexpected EOF while looking for matching %r' % close,
                        self)
                if peekc == c:
                    ret += peekc
                    peekc = self._getc(True)
                    if peekc is None:
                        raise MatchedPairError(
                            startlineno,
                            'unexpected EOF while looking for matching %r' %
                            close, self)
                    elif peekc == '-':
                        ret += peekc
                        stripdoc = True
                    else:
                        self._ungetc(peekc)

                    if peekc != '<':
                        readingheredocdelim = True
                        lexfirstind = -1

                    continue
                else:
                    c = peekc
            elif checkcomment and not insidecomment and c == '#' and (
                (reservedwordok and lexrwlen == 0) or insideword
                    or lexwlen == 0):
                insidecomment = True

            if c == close and not insidecase:
                count -= 1
            elif not firstclose and not insidecase and c == open:
                count += 1

            ret += c

            if count == 0:
                break

            if c == '\\':
                passnextchar = True

            # bashlex/parse.y L3897
            if _shellquote(c):
                self._push_delimiter(c)
                try:
                    if wasdollar and c == "'":
                        nestret = self._parse_matched_pair(c,
                                                           c,
                                                           c,
                                                           allowesc=True,
                                                           dquote=True)
                    else:
                        nestret = self._parse_matched_pair(c,
                                                           c,
                                                           c,
                                                           dquote=True)
                finally:
                    self._pop_delimiter()

                # XXX is this necessary?
                # if wasdollar and c == "'" and not rdquote:
                #     if not rdquote:
                #         nestret = shutils.single_quote(nestret)
                #     ret = ret[:-2]
                # elif wasdollar and c == '"' and not rdquote:
                #     nestret = shutils.double_quote(nestret)
                #     ret = ret[:-2]

                ret += nestret
            # check for $(), $[], or ${} inside command substitution
            elif wasdollar and c in '({[':
                if not insidecase and open == c:
                    count -= 1
                if c == '(':
                    nestret = self._parse_comsub(None,
                                                 '(',
                                                 ')',
                                                 parsingcommand=True,
                                                 dquote=False)
                elif c == '{':
                    nestret = self._parse_matched_pair(None,
                                                       '{',
                                                       '}',
                                                       firstclose=True,
                                                       dolbrace=True,
                                                       dquote=True)
                elif c == '[':
                    nestret = self._parse_matched_pair(None,
                                                       '[',
                                                       ']',
                                                       dquote=True)

                ret += nestret

            wasdollar = c == '$'

        return ret
Beispiel #2
0
    def _parse_comsub(self, doublequotes, open, close, parsingcommand=False,
                      dquote=False, firstclose=False):
        peekc = self._getc(False)
        self._ungetc(peekc)

        if peekc == '(':
            return self._parse_matched_pair(doublequotes, open, close)

        count = 1
        dollarok = True

        checkcase = bool(parsingcommand and (doublequotes is None or doublequotes not in "'\"") and not dquote)
        checkcomment = checkcase

        startlineno = self._line_number
        heredelim = ''
        stripdoc = insideheredoc = insidecomment = insideword = insidecase = False
        readingheredocdelim = False
        wasdollar = passnextchar = False
        reservedwordok = True
        lexfirstind = -1
        lexrwlen = 0

        ret = ''

        while count:
            c = self._getc(doublequotes != "'" and not insidecomment and not passnextchar)

            if c is None:
                raise MatchedPairError(startlineno, 'unexpected EOF while looking for matching %r' % close, self)

            # 3571
            if c == '\n':
                if readingheredocdelim and heredelim:
                    readingheredocdelim = False
                    insideheredoc = True
                    lexfirstind = len(ret) + 1
                elif insideheredoc:
                    tind = lexfirstind
                    while stripdoc and ret[tind] == '\t':
                        tind += 1
                    if ret[tind:] == heredelim:
                        stripdoc = insideheredoc = False
                        heredelim = ''
                        lexfirstind = -1
                    else:
                        lexfirstind = len(ret) + 1
            # 3599
            if insideheredoc and c == close and count == 1:
                tind = lexfirstind
                while stripdoc and ret[tind] == '\t':
                    tind += 1
                if ret[tind:] == heredelim:
                    stripdoc = insideheredoc = False
                    heredelim = ''
                    lexfirstind = -1

            if insidecomment or insideheredoc:
                ret += c

                if insidecomment and c == '\n':
                    insidecomment = False

                continue

            if passnextchar:
                passnextchar = False
                # XXX is this needed?
                # if doublequotes != "'" and c == '\n':
                #     if ret:
                #         ret = ret[:-1]
                # else:
                #     ret += c
                ret += c
                continue

            if _shellbreak(c):
                insideword = False
            else:
                if insideword:
                    lexwlen += 1
                else:
                    insideword = True
                    lexwlen = 0

            if _shellblank(c) and not readingheredocdelim and not lexrwlen:
                ret += c
                continue

            # 3686
            if readingheredocdelim:
                if lexfirstind == -1 and not _shellbreak(c):
                    lexfirstind = len(ret)
                elif lexfirstind >= 0 and not passnextchar and _shellbreak(c):
                    if not heredelim:
                        nestret = ret[lexfirstind:]
                        heredelim = shutils.removequotes(nestret)
                    if c == '\n':
                        insideheredoc = True
                        readingheredocdelim = False
                        lexfirstind = len(ret) + 1
                    else:
                        lexfirstind = -1

            if not reservedwordok and checkcase and not insidecomment and (_shellmeta(c) or c == '\n'):
                ret += c
                peekc = self._getc(True)
                if c == peekc and c in '&|;':
                    ret += peekc
                    reservedwordok = True
                    lexrwlen = 0
                    continue
                elif c == '\n' or c in '&|;':
                    self._ungetc(peekc)
                    reservedwordok = True
                    lexrwlen = 0
                    continue
                elif c is None:
                    raise MatchedPairError(startlineno, 'unexpected EOF while looking for matching %r' % close, self) # pragma: no coverage
                else:
                    ret = ret[:-1]
                    self._ungetc(peekc)

            # 3761
            if reservedwordok:
                if c.islower():
                    ret += c
                    lexrwlen += 1
                    continue
                elif lexrwlen == 4 and _shellbreak(c):
                    if ret[-4:] == 'case':
                        insidecase = True
                    elif ret[-4:] == 'esac':
                        insidecase = False
                    reservedwordok = False
                elif (checkcomment and c == '#' and (lexrwlen == 0 or
                        (insideword and lexwlen == 0))):
                    pass
                elif (not insidecase and (_shellblank(c) or c == '\n') and
                    lexrwlen == 2 and ret[-2:] == 'do'):
                    lexrwlen = 0
                elif insidecase and c != '\n':
                    reservedwordok = False
                elif not _shellbreak(c):
                    reservedwordok = False

            if not insidecomment and checkcase and c == '<':
                ret += c
                peekc = self._getc(True)
                if peekc is None:
                    raise MatchedPairError(startlineno, 'unexpected EOF while looking for matching %r' % close, self)
                if peekc == c:
                    ret += peekc
                    peekc = self._getc(True)
                    if peekc is None:
                        raise MatchedPairError(startlineno, 'unexpected EOF while looking for matching %r' % close, self)
                    elif peekc == '-':
                        ret += peekc
                        stripdoc = True
                    else:
                        self._ungetc(peekc)

                    if peekc != '<':
                        readingheredocdelim = True
                        lexfirstind = -1

                    continue
                else:
                    c = peekc
            elif checkcomment and not insidecomment and c == '#' and ((reservedwordok
                    and lexrwlen == 0) or insideword or lexwlen == 0):
                insidecomment = True

            if c == close and not insidecase:
                count -= 1
            elif not firstclose and not insidecase and c == open:
                count += 1

            ret += c

            if count == 0:
                break

            if c == '\\':
                passnextchar = True

            # 3897
            if _shellquote(c):
                self._push_delimiter(c)
                try:
                    if wasdollar and c == "'":
                        nestret = self._parse_matched_pair(c, c, c,
                                                           allowesc=True,
                                                           dquote=True)
                    else:
                        nestret = self._parse_matched_pair(c, c, c,
                                                           dquote=True)
                finally:
                    self._pop_delimiter()

                # XXX is this necessary?
                # if wasdollar and c == "'" and not rdquote:
                #     if not rdquote:
                #         nestret = shutils.single_quote(nestret)
                #     ret = ret[:-2]
                # elif wasdollar and c == '"' and not rdquote:
                #     nestret = shutils.double_quote(nestret)
                #     ret = ret[:-2]

                ret += nestret
            # check for $(), $[], or ${} inside command substitution
            elif wasdollar and c in '({[':
                if not insidecase and open == c:
                    count -= 1
                if c == '(':
                    nestret = self._parse_comsub(None, '(', ')',
                                                 parsingcommand=True,
                                                 dquote=False)
                elif c == '{':
                    nestret = self._parse_matched_pair(None, '{', '}',
                                                       firstclose=True,
                                                       dolbrace=True,
                                                       dquote=True)
                elif c == '[':
                    nestret = self._parse_matched_pair(None, '[', ']',
                                                       dquote=True)

                ret += nestret

            wasdollar = c == '$'

        return ret