Exemplo n.º 1
0
    def DoRedirect(self, node, local_symbols):
        #print(node, file=sys.stderr)
        op_spid = node.op.span_id
        op_id = node.op.id
        self.cursor.PrintUntil(op_spid)

        # TODO:
        # - Do < and <& the same way.
        # - How to handle here docs and here docs?
        # - >> becomes >+ or >-, or maybe >>>

        #if node.tag == redir_e.Redir:
        if False:
            if node.fd == runtime.NO_SPID:
                if op_id == Id.Redir_Great:
                    self.f.write('>')  # Allow us to replace the operator
                    self.cursor.SkipUntil(op_spid + 1)
                elif op_id == Id.Redir_GreatAnd:
                    self.f.write('> !')  # Replace >& 2 with > !2
                    spid = word_.LeftMostSpanForWord(node.arg_word)
                    self.cursor.SkipUntil(spid)
                    #self.DoWordInCommand(node.arg_word)

            else:
                # NOTE: Spacing like !2>err.txt vs !2 > err.txt can be done in the
                # formatter.
                self.f.write('!%d ' % node.fd)
                if op_id == Id.Redir_Great:
                    self.f.write('>')
                    self.cursor.SkipUntil(op_spid + 1)
                elif op_id == Id.Redir_GreatAnd:
                    self.f.write('> !')  # Replace 1>& 2 with !1 > !2
                    spid = word_.LeftMostSpanForWord(node.arg_word)
                    self.cursor.SkipUntil(spid)

            self.DoWordInCommand(node.arg_word, local_symbols)

        #elif node.tag == redir_e.HereDoc:
        elif False:
            ok, delimiter, delim_quoted = word_.StaticEval(node.here_begin)
            if not ok:
                p_die('Invalid here doc delimiter', word=node.here_begin)

            # Turn everything into <<.  We just change the quotes
            self.f.write('<<')

            #here_begin_spid2 = word_.RightMostSpanForWord(node.here_begin)
            if delim_quoted:
                self.f.write(" '''")
            else:
                self.f.write(' """')

            delim_end_spid = word_.RightMostSpanForWord(node.here_begin)
            self.cursor.SkipUntil(delim_end_spid + 1)

            #self.cursor.SkipUntil(here_begin_spid + 1)

            # Now print the lines.  TODO: Have a flag to indent these to the level of
            # the owning command, e.g.
            #   cat <<EOF
            # EOF
            # Or since most here docs are the top level, you could just have a hack
            # for a fixed indent?  TODO: Look at real use cases.
            for part in node.stdin_parts:
                self.DoWordPart(part, local_symbols)

            self.cursor.SkipUntil(node.here_end_span_id + 1)
            if delim_quoted:
                self.f.write("'''\n")
            else:
                self.f.write('"""\n')

            # Need
            #self.cursor.SkipUntil(here_end_spid2)

        else:
            raise AssertionError(node.__class__.__name__)

        # <<< 'here word'
        # << 'here word'
        #
        # 2> out.txt
        # !2 > out.txt

        # cat 1<< EOF
        # hello $name
        # EOF
        # cat !1 << """
        # hello $name
        # """
        #
        # cat << 'EOF'
        # no expansion
        # EOF
        #   cat <<- 'EOF'
        #   no expansion and indented
        #
        # cat << '''
        # no expansion
        # '''
        #   cat << '''
        #   no expansion and indented
        #   '''

        # Warn about multiple here docs on a line.
        # As an obscure feature, allow
        # cat << \'ONE' << \"TWO"
        # 123
        # ONE
        # 234
        # TWO
        # The _ is an indicator that it's not a string to be piped in.
        pass
Exemplo n.º 2
0
    def Eval(self, line):
        """Returns an expanded line."""

        if not self.readline_mod:
            return line

        tokens = list(HISTORY_LEXER.Tokens(line))
        # Common case: no history expansion.
        if all(id_ == Id.History_Other for (id_, _) in tokens):
            return line

        history_len = self.readline_mod.get_current_history_length()
        if history_len <= 0:  # no commands to expand
            return line

        self.debug_f.log('history length = %d', history_len)

        parts = []
        for id_, val in tokens:
            if id_ == Id.History_Other:
                out = val

            elif id_ == Id.History_Op:
                prev = self.readline_mod.get_history_item(history_len)

                ch = val[1]
                if ch == '!':
                    out = prev
                else:
                    self.parse_ctx.trail.Clear()  # not strictly necessary?
                    line_reader = reader.StringLineReader(
                        prev, self.parse_ctx.arena)
                    c_parser = self.parse_ctx.MakeOshParser(line_reader)
                    try:
                        c_parser.ParseLogicalLine()
                    except util.ParseError as e:
                        #from core import ui
                        #ui.PrettyPrintError(e, self.parse_ctx.arena)

                        # Invalid command in history.  TODO: We should never enter these.
                        self.debug_f.log(
                            "Couldn't parse historical command %r: %s", prev,
                            e)

                    # NOTE: We're using the trail rather than the return value of
                    # ParseLogicalLine because it handles cases like
                    # $ for i in 1 2 3; do sleep ${i}; done
                    # $ echo !$
                    # which should expand to 'echo ${i}'

                    words = self.parse_ctx.trail.words
                    #self.debug_f.log('TRAIL WORDS: %s', words)

                    if ch == '^':
                        try:
                            w = words[1]
                        except IndexError:
                            raise util.HistoryError("No first word in %r",
                                                    prev)
                        spid1 = word_.LeftMostSpanForWord(w)
                        spid2 = word_.RightMostSpanForWord(w)

                    elif ch == '$':
                        try:
                            w = words[-1]
                        except IndexError:
                            raise util.HistoryError("No last word in %r", prev)

                        spid1 = word_.LeftMostSpanForWord(w)
                        spid2 = word_.RightMostSpanForWord(w)

                    elif ch == '*':
                        try:
                            w1 = words[1]
                            w2 = words[-1]
                        except IndexError:
                            raise util.HistoryError(
                                "Couldn't find words in %r", prev)

                        spid1 = word_.LeftMostSpanForWord(w1)
                        spid2 = word_.RightMostSpanForWord(w2)

                    else:
                        raise AssertionError(ch)

                    arena = self.parse_ctx.arena
                    span1 = arena.GetLineSpan(spid1)
                    span2 = arena.GetLineSpan(spid2)

                    begin = span1.col
                    end = span2.col + span2.length

                    out = prev[begin:end]

            elif id_ == Id.History_Num:
                index = int(
                    val[1:])  # regex ensures this.  Maybe have - on the front.
                if index < 0:
                    num = history_len + 1 + index
                else:
                    num = index

                out = self.readline_mod.get_history_item(num)
                if out is None:  # out of range
                    raise util.HistoryError('%s: not found', val)

            elif id_ == Id.History_Search:
                # Remove the required space at the end and save it.  A simple hack than
                # the one bash has.
                last_char = val[-1]
                val = val[:-1]

                # Search backward
                prefix = None
                substring = None
                if val[1] == '?':
                    substring = val[2:]
                else:
                    prefix = val[1:]

                out = None
                for i in xrange(history_len, 1, -1):
                    cmd = self.readline_mod.get_history_item(i)
                    if prefix and cmd.startswith(prefix):
                        out = cmd
                    if substring and substring in cmd:
                        out = cmd
                    if out is not None:
                        out += last_char  # restore required space
                        break

                if out is None:
                    raise util.HistoryError('%r found no results', val)

            else:
                raise AssertionError(id_)

            parts.append(out)

        line = ''.join(parts)
        # show what we expanded to
        sys.stdout.write('! %s' % line)
        return line