Esempio n. 1
0
    def __call__(self, argv):
        arg_r = args.Reader(argv)
        arg = INIT_COMPLETION_SPEC.Parse(arg_r)
        var_names = arg_r.Rest()  # Output variables to set
        for name in var_names:
            # Ironically we could complete these
            if name not in ['cur', 'prev', 'words', 'cword']:
                raise args.UsageError('Invalid output variable name %r' % name)
        #print(arg)

        # TODO: How does the user test a completion function programmatically?  Set
        # COMP_ARGV?
        val = self.mem.GetVar('COMP_ARGV')
        if val.tag != value_e.StrArray:
            raise args.UsageError("COMP_ARGV should be an array")
        comp_argv = val.strs

        # These are the ones from COMP_WORDBREAKS that we care about.  The rest occur
        # "outside" of words.
        break_chars = [':', '=']
        if arg.s:  # implied
            break_chars.remove('=')
        # NOTE: The syntax is -n := and not -n : -n =.
        omit_chars = arg.n or ''
        for c in omit_chars:
            if c in break_chars:
                break_chars.remove(c)

        # argv adjusted according to 'break_chars'.
        adjusted_argv = []
        for a in comp_argv:
            completion.AdjustArg(a, break_chars, adjusted_argv)

        if 'words' in var_names:
            state.SetArrayDynamic(self.mem, 'words', adjusted_argv)

        n = len(adjusted_argv)
        cur = adjusted_argv[-1]
        prev = '' if n < 2 else adjusted_argv[-2]

        if arg.s:
            if cur.startswith(
                    '--') and '=' in cur:  # Split into flag name and value
                prev, cur = cur.split('=', 1)
                split = 'true'
            else:
                split = 'false'
            # Do NOT set 'split' without -s.  Caller might not have declared it.
            # Also does not respect var_names, because we don't need it.
            state.SetStringDynamic(self.mem, 'split', split)

        if 'cur' in var_names:
            state.SetStringDynamic(self.mem, 'cur', cur)
        if 'prev' in var_names:
            state.SetStringDynamic(self.mem, 'prev', prev)
        if 'cword' in var_names:
            # Same weird invariant after adjustment
            state.SetStringDynamic(self.mem, 'cword', str(n - 1))

        return 0
Esempio n. 2
0
  def __call__(self, cmd_val):
    arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
    arg_r.Next()

    # NOTE: If first char is a colon, error reporting is different.  Alpine
    # might not use that?
    spec_str = arg_r.ReadRequired('requires an argspec')

    var_name, var_spid = arg_r.ReadRequired2(
        'requires the name of a variable to set')

    try:
      spec = self.spec_cache[spec_str]
    except KeyError:
      spec = _ParseOptSpec(spec_str)
      self.spec_cache[spec_str] = spec

    # These errors are fatal errors, not like the builtin exiting with code 1.
    # Because the invariants of the shell have been violated!
    v = self.mem.GetVar('OPTIND')
    if v.tag != value_e.Str:
      e_die('OPTIND should be a string, got %r', v)
    try:
      optind = int(v.s)
    except ValueError:
      e_die("OPTIND doesn't look like an integer, got %r", v.s)

    user_argv = arg_r.Rest() or self.mem.GetArgv()
    #util.log('user_argv %s', user_argv)
    status, opt_char, optarg, optind = _GetOpts(spec, user_argv, optind,
                                                self.errfmt)

    # Bug fix: bash-completion uses a *local* OPTIND !  Not global.
    state.SetStringDynamic(self.mem, 'OPTARG', optarg)
    state.SetStringDynamic(self.mem, 'OPTIND', str(optind))
    if match.IsValidVarName(var_name):
      state.SetStringDynamic(self.mem, var_name, opt_char)
    else:
      # NOTE: The builtin has PARTIALLY filed.  This happens in all shells
      # except mksh.
      raise args.UsageError('got invalid variable name %r' % var_name,
                            span_id=var_spid)
    return status
Esempio n. 3
0
def GetOpts(argv, mem):
    """
  Vars to set:
    OPTIND - initialized to 1 at startup
    OPTARG - argument
  Vars used:
    OPTERR: disable printing of error messages
  """
    # TODO: need to handle explicit args.

    try:
        # NOTE: If first char is a colon, error reporting is different.  Alpine
        # might not use that?
        spec_str = argv[0]
        var_name = argv[1]
    except IndexError:
        raise args.UsageError('getopts optstring name [arg]')

    try:
        spec = _GETOPTS_CACHE[spec_str]
    except KeyError:
        spec = _ParseOptSpec(spec_str)
        _GETOPTS_CACHE[spec_str] = spec

    # These errors are fatal errors, not like the builtin exiting with code 1.
    # Because the invariants of the shell have been violated!
    v = mem.GetVar('OPTIND')
    if v.tag != value_e.Str:
        e_die('OPTIND should be a string, got %r', v)
    try:
        optind = int(v.s)
    except ValueError:
        e_die("OPTIND doesn't look like an integer, got %r", v.s)

    user_argv = argv[2:] or mem.GetArgv()
    status, opt_char, optarg, optind = _GetOpts(spec, user_argv, optind)

    # Bug fix: bash-completion uses a *local* OPTIND !  Not global.
    state.SetStringDynamic(mem, var_name, opt_char)
    state.SetStringDynamic(mem, 'OPTARG', optarg)
    state.SetStringDynamic(mem, 'OPTIND', str(optind))
    return status
Esempio n. 4
0
def Read(argv, splitter, mem):
    arg, i = READ_SPEC.Parse(argv)

    names = argv[i:]
    if arg.n is not None:  # read a certain number of bytes
        try:
            name = names[0]
        except IndexError:
            name = 'REPLY'  # default variable name
        s = posix.read(sys.stdin.fileno(), arg.n)
        #log('read -n: %s = %s', name, s)

        state.SetLocalString(mem, name, s)
        # NOTE: Even if we don't get n bytes back, there is no error?
        return 0

    if not names:
        names.append('REPLY')

    # leftover words assigned to the last name
    if arg.a:
        max_results = 0  # no max
    else:
        max_results = len(names)

    # We have to read more than one line if there is a line continuation (and
    # it's not -r).

    parts = []
    join_next = False
    while True:
        line = ReadLineFromStdin()
        #log('LINE %r', line)
        if not line:  # EOF
            status = 1
            break

        if line.endswith('\n'):  # strip trailing newline
            line = line[:-1]
            status = 0
        else:
            # odd bash behavior: fail even if we can set variables.
            status = 1

        spans = splitter.SplitForRead(line, not arg.r)
        done, join_next = _AppendParts(line, spans, max_results, join_next,
                                       parts)

        #log('PARTS %s continued %s', parts, continued)
        if done:
            break

    if arg.a:
        state.SetArrayDynamic(mem, arg.a, parts)
    else:
        for i in xrange(max_results):
            try:
                s = parts[i]
            except IndexError:
                s = ''  # if there are too many variables
            #log('read: %s = %s', names[i], s)
            state.SetStringDynamic(mem, names[i], s)

    return status
Esempio n. 5
0
    def __call__(self, arg_vec):
        arg, i = READ_SPEC.ParseVec(arg_vec)

        names = arg_vec.strs[i:]
        if arg.n is not None:  # read a certain number of bytes
            stdin = sys.stdin.fileno()
            try:
                name = names[0]
            except IndexError:
                name = 'REPLY'  # default variable name
            s = ""
            if sys.stdin.isatty():  # set stdin to read in unbuffered mode
                orig_attrs = termios.tcgetattr(stdin)
                attrs = termios.tcgetattr(stdin)
                # disable canonical (buffered) mode
                # see `man termios` for an extended discussion
                attrs[3] &= ~termios.ICANON
                try:
                    termios.tcsetattr(stdin, termios.TCSANOW, attrs)
                    # posix.read always returns a single character in unbuffered mode
                    while arg.n > 0:
                        s += posix.read(stdin, 1)
                        arg.n -= 1
                finally:
                    termios.tcsetattr(stdin, termios.TCSANOW, orig_attrs)
            else:
                s_len = 0
                while arg.n > 0:
                    buf = posix.read(stdin, arg.n)
                    # EOF
                    if buf == '':
                        break
                    arg.n -= len(buf)
                    s += buf

            state.SetLocalString(self.mem, name, s)
            # NOTE: Even if we don't get n bytes back, there is no error?
            return 0

        if not names:
            names.append('REPLY')

        # leftover words assigned to the last name
        if arg.a:
            max_results = 0  # no max
        else:
            max_results = len(names)

        # We have to read more than one line if there is a line continuation (and
        # it's not -r).

        parts = []
        join_next = False
        while True:
            line = ReadLineFromStdin()
            #log('LINE %r', line)
            if not line:  # EOF
                status = 1
                break

            if line.endswith('\n'):  # strip trailing newline
                line = line[:-1]
                status = 0
            else:
                # odd bash behavior: fail even if we can set variables.
                status = 1

            spans = self.splitter.SplitForRead(line, not arg.r)
            done, join_next = _AppendParts(line, spans, max_results, join_next,
                                           parts)

            #log('PARTS %s continued %s', parts, continued)
            if done:
                break

        if arg.a:
            state.SetArrayDynamic(self.mem, arg.a, parts)
        else:
            for i in xrange(max_results):
                try:
                    s = parts[i]
                except IndexError:
                    s = ''  # if there are too many variables
                #log('read: %s = %s', names[i], s)
                state.SetStringDynamic(self.mem, names[i], s)

        return status
Esempio n. 6
0
    def __call__(self, cmd_val):
        """
    printf: printf [-v var] format [argument ...]
    """
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()  # skip argv[0]
        arg, _ = PRINTF_SPEC.Parse(arg_r)

        fmt, fmt_spid = arg_r.ReadRequired2('requires a format string')
        varargs, spids = arg_r.Rest2()

        #log('fmt %s', fmt)
        #log('vals %s', vals)

        arena = self.parse_ctx.arena
        if fmt in self.parse_cache:
            parts = self.parse_cache[fmt]
        else:
            line_reader = reader.StringLineReader(fmt, arena)
            # TODO: Make public
            lexer = self.parse_ctx._MakeLexer(line_reader)
            p = _FormatStringParser(lexer)
            arena.PushSource(source.ArgvWord(fmt_spid))
            try:
                parts = p.Parse()
            except error.Parse as e:
                self.errfmt.PrettyPrintError(e)
                return 2  # parse error
            finally:
                arena.PopSource()

            self.parse_cache[fmt] = parts

        if 0:
            print()
            for part in parts:
                part.PrettyPrint()
                print()

        out = []
        arg_index = 0
        num_args = len(varargs)

        while True:
            for part in parts:
                if isinstance(part, printf_part.Literal):
                    token = part.token
                    if token.id == Id.Format_EscapedPercent:
                        s = '%'
                    else:
                        s = word_compile.EvalCStringToken(token.id, token.val)
                    out.append(s)

                elif isinstance(part, printf_part.Percent):
                    try:
                        s = varargs[arg_index]
                        word_spid = spids[arg_index]
                    except IndexError:
                        s = ''
                        word_spid = runtime.NO_SPID

                    typ = part.type.val
                    if typ == 's':
                        if part.precision:
                            precision = int(part.precision.val)
                            s = s[:precision]  # truncate
                    elif typ == 'q':
                        s = string_ops.ShellQuoteOneLine(s)
                    elif typ in 'diouxX':
                        try:
                            d = int(s)
                        except ValueError:
                            if len(s) >= 2 and s[0] in '\'"':
                                # TODO: utf-8 decode s[1:] to be more correct.  Probably
                                # depends on issue #366, a utf-8 library.
                                d = ord(s[1])
                            else:
                                # This works around the fact that in the arg recycling case, you have no spid.
                                if word_spid == runtime.NO_SPID:
                                    self.errfmt.Print(
                                        "printf got invalid number %r for this substitution",
                                        s,
                                        span_id=part.type.span_id)
                                else:
                                    self.errfmt.Print(
                                        "printf got invalid number %r",
                                        s,
                                        span_id=word_spid)

                                return 1

                        if typ in 'di':
                            s = str(d)
                        elif typ in 'ouxX':
                            if d < 0:
                                e_die(
                                    "Can't format negative number %d with %%%s",
                                    d,
                                    typ,
                                    span_id=part.type.span_id)
                            if typ == 'u':
                                s = str(d)
                            elif typ == 'o':
                                s = '%o' % d
                            elif typ == 'x':
                                s = '%x' % d
                            elif typ == 'X':
                                s = '%X' % d
                        else:
                            raise AssertionError

                    else:
                        raise AssertionError

                    if part.width:
                        width = int(part.width.val)
                        if part.flag:
                            flag = part.flag.val
                            if flag == '-':
                                s = s.ljust(width, ' ')
                            elif flag == '0':
                                s = s.rjust(width, '0')
                            else:
                                pass
                        else:
                            s = s.rjust(width, ' ')

                    out.append(s)
                    arg_index += 1

                else:
                    raise AssertionError

            if arg_index >= num_args:
                break
            # Otherwise there are more args.  So cycle through the loop once more to
            # implement the 'arg recycling' behavior.

        result = ''.join(out)
        if arg.v:
            var_name = arg.v

            # Notes:
            # - bash allows a[i] here (as in unset and ${!x}), but we haven't
            # implemented it.
            # - TODO: get the span_id for arg.v!
            if not match.IsValidVarName(var_name):
                raise args.UsageError('got invalid variable name %r' %
                                      var_name)
            state.SetStringDynamic(self.mem, var_name, result)
        else:
            sys.stdout.write(result)
        return 0
Esempio n. 7
0
    def __call__(self, arg_vec):
        """
    printf: printf [-v var] format [argument ...]
    """
        arg_r = args.Reader(arg_vec.strs, spids=arg_vec.spids)
        arg_r.Next()  # skip argv[0]
        arg, _ = PRINTF_SPEC.Parse(arg_r)

        fmt, fmt_spid = arg_r.ReadRequired2('requires a format string')
        varargs, spids = arg_r.Rest2()

        #from core.util import log
        #log('fmt %s', fmt)
        #log('vals %s', vals)

        arena = self.parse_ctx.arena
        if fmt in self.parse_cache:
            parts = self.parse_cache[fmt]
        else:
            line_reader = reader.StringLineReader(fmt, arena)
            # TODO: Make public
            lexer = self.parse_ctx._MakeLexer(line_reader)
            p = _FormatStringParser(lexer)
            arena.PushSource(source.ArgvWord(fmt_spid))
            try:
                parts = p.Parse()
            except util.ParseError as e:
                self.errfmt.PrettyPrintError(e)
                return 2  # parse error
            finally:
                arena.PopSource()

            self.parse_cache[fmt] = parts

        if 0:
            print()
            for part in parts:
                part.PrettyPrint()
                print()

        out = []
        arg_index = 0
        num_args = len(varargs)

        while True:
            for part in parts:
                if isinstance(part, printf_part.Literal):
                    token = part.token
                    if token.id == Id.Format_EscapedPercent:
                        s = '%'
                    else:
                        s = word_compile.EvalCStringToken(token.id, token.val)
                    out.append(s)

                elif isinstance(part, printf_part.Percent):
                    try:
                        s = varargs[arg_index]
                        word_spid = spids[arg_index]
                    except IndexError:
                        s = ''
                        word_spid = const.NO_INTEGER

                    typ = part.type.val
                    if typ == 's':
                        pass  # val remains the same
                    elif typ == 'q':
                        s = string_ops.ShellQuoteOneLine(s)
                    elif typ in 'di':
                        try:
                            d = int(s)
                        except ValueError:
                            # This works around the fact that in the arg recycling case, you have no spid.
                            if word_spid == const.NO_INTEGER:
                                self.errfmt.Print(
                                    "printf got invalid number %r for this substitution",
                                    s,
                                    span_id=part.type.span_id)
                            else:
                                self.errfmt.Print(
                                    "printf got invalid number %r",
                                    s,
                                    span_id=word_spid)

                            return 1
                        s = str(d)
                    else:
                        raise AssertionError

                    if part.width:
                        width = int(part.width.val)
                        if part.flag:
                            flag = part.flag.val
                            if flag == '-':
                                s = s.ljust(width, ' ')
                            elif flag == '0':
                                s = s.rjust(width, '0')
                            else:
                                pass
                        else:
                            s = s.rjust(width, ' ')

                    out.append(s)
                    arg_index += 1

                else:
                    raise AssertionError

            if arg_index >= num_args:
                break
            # Otherwise there are more args.  So cycle through the loop once more to
            # implement the 'arg recycling' behavior.

        result = ''.join(out)
        if arg.v:
            var_name = arg.v

            # Notes:
            # - bash allows a[i] here (as in unset and ${!x}), but we haven't
            # implemented it.
            # - TODO: get the span_id for arg.v!
            if not match.IsValidVarName(var_name):
                raise args.UsageError('got invalid variable name %r' %
                                      var_name)
            state.SetStringDynamic(self.mem, var_name, result)
        else:
            sys.stdout.write(result)
        return 0