Beispiel #1
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int

        attrs, arg_r = flag_spec.ParseCmdVal('help', cmd_val)
        #arg = arg_types.help(attrs.attrs)

        topic, blame_spid = arg_r.Peek2()
        if topic is None:
            topic = 'help'
            blame_spid = runtime.NO_SPID
        else:
            arg_r.Next()

        try:
            contents = self.loader.Get('_devbuild/help/%s' % topic)
        except IOError:
            # Notes:
            # 1. bash suggests:
            # man -k zzz
            # info zzz
            # help help
            # We should do something smarter.

            # 2. This also happens on 'build/dev.sh minimal', which isn't quite
            # accurate.  We don't have an exact list of help topics!

            # 3. This is mostly an interactive command.  Is it obnoxious to
            # quote the line of code?
            self.errfmt.Print_('no help topics match %r' % topic,
                               span_id=blame_spid)
            return 1

        print(contents)
        return 0
Beispiel #2
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('hash', cmd_val)
        arg = arg_types.hash(attrs.attrs)

        rest = arg_r.Rest()
        if arg.r:
            if len(rest):
                e_usage('got extra arguments after -r')
            self.search_path.ClearCache()
            return 0

        status = 0
        if len(rest):
            for cmd in rest:  # enter in cache
                full_path = self.search_path.CachedLookup(cmd)
                if full_path is None:
                    stderr_line('hash: %r not found', cmd)
                    status = 1
        else:  # print cache
            commands = self.search_path.CachedCommands()
            commands.sort()
            for cmd in commands:
                print(cmd)

        return status
Beispiel #3
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('read', cmd_val)
        arg = arg_types.read(attrs.attrs)
        names = arg_r.Rest()

        fd = self.stdin.fileno()

        if arg.t >= 0.0:
            if arg.t != 0.0:
                e_die("read -t isn't implemented (except t=0)")
            else:
                return 0 if pyos.InputAvailable(fd) else 1

        bits = 0
        if self.stdin.isatty():
            bits |= pyos.TERM_ICANON
            if arg.s:  # silent
                bits |= pyos.TERM_ECHO

            if arg.p is not None:  # only if tty
                mylib.Stderr().write(arg.p)

        if bits == 0:
            status = self._Read(arg, names)
        else:
            term = pyos.TermState(fd, ~bits)
            try:
                status = self._Read(arg, names)
            finally:
                term.Restore()
        return status
Beispiel #4
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        _, arg_r = flag_spec.ParseCmdVal('alias', cmd_val)
        argv = arg_r.Rest()

        if len(argv) == 0:
            for name in sorted(self.aliases):
                alias_exp = self.aliases[name]
                # This is somewhat like bash, except we use %r for ''.
                print('alias %s=%r' % (name, alias_exp))
            return 0

        status = 0
        for i, arg in enumerate(argv):
            name, alias_exp = mylib.split_once(arg, '=')
            if alias_exp is None:  # if we get a plain word without, print alias
                alias_exp = self.aliases.get(name)
                if alias_exp is None:
                    self.errfmt.Print_('No alias named %r' % name,
                                       span_id=cmd_val.arg_spids[i])
                    status = 1
                else:
                    print('alias %s=%r' % (name, alias_exp))
            else:
                self.aliases[name] = alias_exp

        #print(argv)
        #log('AFTER ALIAS %s', aliases)
        return status
Beispiel #5
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('command', cmd_val)
        arg = arg_types.command(attrs.attrs)
        if arg.v:
            status = 0
            names = arg_r.Rest()
            for kind, argument in _ResolveNames(names, self.funcs,
                                                self.aliases,
                                                self.search_path):
                if kind is None:
                    status = 1  # nothing printed, but we fail
                else:
                    # This is for -v, -V is more detailed.
                    print(argument)
            return status

        # shift by one
        cmd_val = cmd_value.Argv(cmd_val.argv[1:], cmd_val.arg_spids[1:], None)

        # If we respected do_fork here instead of passing True, the case
        # 'command date | wc -l' would take 2 processes instead of 3.  But no other
        # shell does that, and this rare case isn't worth the bookkeeping.
        # See test/syscall
        return self.shell_ex.RunSimpleCommand(cmd_val, True, call_procs=False)
Beispiel #6
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int

        # There are no flags, but we need it to respect --
        _, arg_r = flag_spec.ParseCmdVal('eval', cmd_val)

        if self.exec_opts.simple_eval_builtin():
            code_str, eval_spid = arg_r.ReadRequired2('requires code string')
            if not arg_r.AtEnd():
                e_usage('requires exactly 1 argument')
        else:
            code_str = ' '.join(arg_r.Rest())
            # code_str could be EMPTY, so just use the first one
            eval_spid = cmd_val.arg_spids[0]

        line_reader = reader.StringLineReader(code_str, self.arena)
        c_parser = self.parse_ctx.MakeOshParser(line_reader)

        src = source.EvalArg(eval_spid)
        with dev.ctx_Tracer(self.tracer, 'eval', None):
            with alloc.ctx_Location(self.arena, src):
                return main_loop.Batch(self.cmd_ev,
                                       c_parser,
                                       self.arena,
                                       cmd_flags=cmd_eval.IsEvalSource)
Beispiel #7
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('shopt', cmd_val)

        arg = arg_types.shopt(attrs.attrs)
        opt_names = arg_r.Rest()

        if arg.p:  # print values
            if arg.o:  # use set -o names
                self.mutable_opts.ShowOptions(opt_names)
            else:
                self.mutable_opts.ShowShoptOptions(opt_names)
            return 0

        if arg.q:  # query values
            for name in opt_names:
                index = match.MatchOption(name)
                if index == 0:
                    return 2  # bash gives 1 for invalid option; 2 is better
                if not self.mutable_opts.opt0_array[index]:
                    return 1  # at least one option is not true
            return 0  # all options are true

        if arg.s:
            b = True
        elif arg.u:
            b = False
        else:
            # If no flags are passed, print the options.  bash prints uses a
            # different format for 'shopt', but we use the same format as 'shopt
            # -p'.
            self.mutable_opts.ShowShoptOptions(opt_names)
            return 0

        if cmd_val.block:
            opt_nums = []  # type: List[int]
            for name in opt_names:
                index = match.MatchOption(name)
                if index == 0:
                    # TODO: compute span_id
                    e_usage('got invalid option %r' % name)
                opt_nums.append(index)

            with state.ctx_Option(self.mutable_opts, opt_nums, b):
                unused = self.cmd_ev.EvalBlock(cmd_val.block)
            return 0  # cd also returns 0

        # Otherwise, set options.
        for name in opt_names:
            #if arg.o:
            #  self.mutable_opts.SetOption(name, b)
            #else:
            # We allow set -o options here
            self.mutable_opts.SetShoptOption(name, b)

        return 0
Beispiel #8
0
  def Run(self, cmd_val):
    # type: (cmd_value__Argv) -> int
    # NOTE: This builtin doesn't do anything in non-interactive mode in bash?
    # It silently exits zero.
    # zsh -c 'history' produces an error.
    readline_mod = self.readline_mod
    if not readline_mod:
      e_usage("is disabled because Oil wasn't compiled with 'readline'")

    attrs, arg_r = flag_spec.ParseCmdVal('history', cmd_val)
    arg = arg_types.history(attrs.attrs)

    # Clear all history
    if arg.c:
      readline_mod.clear_history()
      return 0

    # Delete history entry by id number
    if arg.d >= 0:
      cmd_index = arg.d - 1

      try:
        readline_mod.remove_history_item(cmd_index)
      except ValueError:
        e_usage("couldn't find item %d" % arg.d)

      return 0

    # Returns 0 items in non-interactive mode?
    num_items = readline_mod.get_current_history_length()
    #log('len = %d', num_items)

    rest = arg_r.Rest()
    if len(rest) == 0:
      start_index = 1
    elif len(rest) == 1:
      arg0 = rest[0]
      try:
        num_to_show = int(arg0)
      except ValueError:
        e_usage('got invalid argument %r' % arg0)
      start_index = max(1, num_items + 1 - num_to_show)
    else:
      e_usage('got many arguments')

    # TODO:
    # - Exclude lines that don't parse from the history!  bash and zsh don't do
    # that.
    # - Consolidate multiline commands.

    for i in xrange(start_index, num_items+1):  # 1-based index
      item = readline_mod.get_history_item(i)
      self.f.write('%5d  %s\n' % (i, item))
    return 0
Beispiel #9
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        call_spid = cmd_val.arg_spids[0]
        _, arg_r = flag_spec.ParseCmdVal('source', cmd_val)

        path = arg_r.Peek()
        if path is None:
            e_usage('missing required argument')
        arg_r.Next()

        resolved = self.search_path.Lookup(path, exec_required=False)
        if resolved is None:
            resolved = path
        try:
            f = self.fd_state.Open(resolved)  # Shell can't use descriptors 3-9
        except OSError as e:
            self.errfmt.Print_('source %r failed: %s' %
                               (path, pyutil.strerror(e)),
                               span_id=cmd_val.arg_spids[1])
            return 1

        try:
            line_reader = reader.FileLineReader(f, self.arena)
            c_parser = self.parse_ctx.MakeOshParser(line_reader)

            # A sourced module CAN have a new arguments array, but it always shares
            # the same variable scope as the caller.  The caller could be at either a
            # global or a local scope.
            source_argv = arg_r.Rest()
            self.mem.PushSource(path, source_argv)

            src = source.SourcedFile(path, call_spid)
            try:
                with alloc.ctx_Location(self.arena, src):
                    status = main_loop.Batch(self.cmd_ev,
                                             c_parser,
                                             self.arena,
                                             cmd_flags=cmd_eval.IsEvalSource)
            finally:
                self.mem.PopSource(source_argv)

            return status

        except _ControlFlow as e:
            if e.IsReturn():
                return e.StatusCode()
            else:
                raise
        finally:
            f.close()
Beispiel #10
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('pwd', cmd_val)
        arg = arg_types.pwd(attrs.attrs)

        # NOTE: 'pwd' will succeed even if the directory has disappeared.  Other
        # shells behave that way too.
        pwd = self.mem.pwd

        # '-L' is the default behavior; no need to check it
        # TODO: ensure that if multiple flags are provided, the *last* one overrides
        # the others
        if arg.P:
            pwd = libc.realpath(pwd)
        print(pwd)
        return 0
Beispiel #11
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        _, arg_r = flag_spec.ParseCmdVal('unalias', cmd_val)
        argv = arg_r.Rest()

        if len(argv) == 0:
            e_usage('requires an argument')

        status = 0
        for i, name in enumerate(argv):
            if name in self.aliases:
                del self.aliases[name]
            else:
                self.errfmt.Print_('No alias named %r' % name,
                                   span_id=cmd_val.arg_spids[i])
                status = 1
        return status
Beispiel #12
0
  def Run(self, cmd_val):
    # type: (cmd_value__Argv) -> int
    attrs, arg_r = flag_spec.ParseCmdVal('type', cmd_val)
    arg = arg_types.type(attrs.attrs)

    if arg.f:
      funcs = {}  # type: Dict[str, command__ShFunction]
    else:
      funcs = self.funcs

    status = 0
    r = _ResolveNames(arg_r.Rest(), funcs, self.aliases, self.search_path)
    for kind, name in r:
      if kind is None:
        self.errfmt.StderrLine('type: %r not found' % name)
        status = 1  # nothing printed, but we fail
      else:
        if arg.t:
          print(kind)
        elif arg.p:
          if kind == 'file':
            print(name)
        elif arg.P:
          if kind == 'file':
            print(name)
          else:
            resolved = self.search_path.Lookup(name)
            if resolved is None:
              status = 1
            else:
              print(resolved)

        else:
          # Alpine's abuild relies on this text because busybox ash doesn't have
          # -t!
          # ash prints "is a shell function" instead of "is a function", but the
          # regex accouts for that.
          print('%s is a %s' % (name, kind))
          if kind == 'function':
            # bash prints the function body, busybox ash doesn't.
            pass

    return status
Beispiel #13
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('mapfile', cmd_val)
        # TODO: Implement flags to mapfile
        #arg = arg_types.mapfile(attrs.attrs)

        var_name, _ = arg_r.Peek2()
        if var_name is None:
            var_name = 'MAPFILE'

        lines = []  # type: List[str]
        while True:
            line = self.f.readline()
            if len(line) == 0:
                break
            lines.append(line)

        state.SetArrayDynamic(self.mem, var_name, lines)
        return 0
Beispiel #14
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('mapfile', cmd_val)
        arg = arg_types.mapfile(attrs.attrs)

        var_name, _ = arg_r.Peek2()
        if var_name is None:
            var_name = 'MAPFILE'

        lines = []  # type: List[str]
        while True:
            line = self.f.readline()
            if len(line) == 0:
                break
            if arg.t and line.endswith('\n'):
                line = line[:-1]
            lines.append(line)

        state.SetArrayDynamic(self.mem, var_name, lines)
        return 0
Beispiel #15
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('shopt', cmd_val)

        arg = arg_types.shopt(attrs.attrs)
        opt_names = arg_r.Rest()

        if arg.p:  # print values
            if arg.o:  # use set -o names
                self.exec_opts.ShowOptions(opt_names)
            else:
                self.exec_opts.ShowShoptOptions(opt_names)
            return 0

        if arg.q:  # query values
            for name in opt_names:
                index = match.MatchOption(name)
                if index == 0:
                    return 2  # bash gives 1 for invalid option; 2 is better
                if not self.exec_opts.opt_array[index]:
                    return 1  # at least one option is not true
            return 0  # all options are true

        if arg.s:
            b = True
        elif arg.u:
            b = False
        else:
            # bash prints uses a different format for 'shopt', but we use the
            # same format as 'shopt -p'.
            self.exec_opts.ShowShoptOptions(opt_names)
            return 0

        # Otherwise, set options.
        for name in opt_names:
            if arg.o:
                self.exec_opts.SetOption(name, b)
            else:
                self.exec_opts.SetShoptOption(name, b)

        return 0
Beispiel #16
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('dirs', cmd_val)
        arg = arg_types.dirs(attrs.attrs)

        home_dir = state.MaybeString(self.mem, 'HOME')
        style = SINGLE_LINE

        # Following bash order of flag priority
        if arg.l:
            home_dir = None  # disable pretty ~
        if arg.c:
            self.dir_stack.Reset()
            return 0
        elif arg.v:
            style = WITH_LINE_NUMBERS
        elif arg.p:
            style = WITHOUT_LINE_NUMBERS

        _PrintDirStack(self.dir_stack, style, home_dir)
        return 0
Beispiel #17
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('unset', cmd_val)
        arg = arg_types.unset(attrs.attrs)

        argv, arg_spids = arg_r.Rest2()
        for i, name in enumerate(argv):
            spid = arg_spids[i]

            if arg.f:
                if name in self.funcs:
                    del self.funcs[name]

            elif arg.v:
                if not self._UnsetVar(name, spid, False):
                    return 1

            else:
                # proc_fallback: Try to delete var first, then func.
                if not self._UnsetVar(name, spid, True):
                    return 1

        return 0
Beispiel #18
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('read', cmd_val)
        arg = arg_types.read(attrs.attrs)
        names = arg_r.Rest()

        # Don't respect any of the other options here?  This is buffered I/O.
        if arg.line:  # read --line
            var_name, var_spid = arg_r.Peek2()
            if var_name is None:
                var_name = '_line'
            else:
                if var_name.startswith(':'):  # optional : sigil
                    var_name = var_name[1:]
                arg_r.Next()

            next_arg, next_spid = arg_r.Peek2()
            if next_arg is not None:
                raise error.Usage('got extra argument', span_id=next_spid)

            return self._Line(arg, var_name)

        if arg.q:
            e_usage('--qsn can only be used with --line')

        if arg.all:  # read --all
            var_name, var_spid = arg_r.Peek2()
            if var_name is None:
                var_name = '_all'
            else:
                if var_name.startswith(':'):  # optional : sigil
                    var_name = var_name[1:]
                arg_r.Next()

            next_arg, next_spid = arg_r.Peek2()
            if next_arg is not None:
                raise error.Usage('got extra argument', span_id=next_spid)

            return self._All(var_name)

        if arg.q:
            e_usage('--qsn not implemented yet')

        fd = self.stdin.fileno()

        if arg.t >= 0.0:
            if arg.t != 0.0:
                e_die("read -t isn't implemented (except t=0)")
            else:
                return 0 if pyos.InputAvailable(fd) else 1

        bits = 0
        if self.stdin.isatty():
            bits |= pyos.TERM_ICANON
            if arg.s:  # silent
                bits |= pyos.TERM_ECHO

            if arg.p is not None:  # only if tty
                mylib.Stderr().write(arg.p)

        if bits == 0:
            status = self._Read(arg, names)
        else:
            term = pyos.TermState(fd, ~bits)
            try:
                status = self._Read(arg, names)
            finally:
                term.Restore()
        return status
Beispiel #19
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        """
    printf: printf [-v var] format [argument ...]
    """
        attrs, arg_r = flag_spec.ParseCmdVal('printf', cmd_val)
        arg = arg_types.printf(attrs.attrs)

        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)
            parser = _FormatStringParser(lexer)

            with alloc.ctx_Location(arena, source.ArgvWord(fmt_spid)):
                try:
                    parts = parser.Parse()
                except error.Parse as e:
                    self.errfmt.PrettyPrintError(e)
                    return 2  # parse error

            self.parse_cache[fmt] = parts

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

        out = []  # type: List[str]
        arg_index = 0
        num_args = len(varargs)
        backslash_c = False

        while True:
            for part in parts:
                UP_part = part
                if part.tag_() == printf_part_e.Literal:
                    part = cast(printf_part__Literal, UP_part)
                    token = part.token
                    if token.id == Id.Format_EscapedPercent:
                        s = '%'
                    else:
                        s = word_compile.EvalCStringToken(token)
                    out.append(s)

                elif part.tag_() == printf_part_e.Percent:
                    part = cast(printf_part__Percent, UP_part)
                    flags = []  # type: List[str]
                    if len(part.flags) > 0:
                        for flag_token in part.flags:
                            flags.append(flag_token.val)

                    width = -1  # nonexistent
                    if part.width:
                        if part.width.id in (Id.Format_Num, Id.Format_Zero):
                            width_str = part.width.val
                            width_spid = part.width.span_id
                        elif part.width.id == Id.Format_Star:
                            if arg_index < num_args:
                                width_str = varargs[arg_index]
                                width_spid = spids[arg_index]
                                arg_index += 1
                            else:
                                width_str = ''  # invalid
                                width_spid = runtime.NO_SPID
                        else:
                            raise AssertionError()

                        try:
                            width = int(width_str)
                        except ValueError:
                            if width_spid == runtime.NO_SPID:
                                width_spid = part.width.span_id
                            self.errfmt.Print_("printf got invalid width %r" %
                                               width_str,
                                               span_id=width_spid)
                            return 1

                    precision = -1  # nonexistent
                    if part.precision:
                        if part.precision.id == Id.Format_Dot:
                            precision_str = '0'
                            precision_spid = part.precision.span_id
                        elif part.precision.id in (Id.Format_Num,
                                                   Id.Format_Zero):
                            precision_str = part.precision.val
                            precision_spid = part.precision.span_id
                        elif part.precision.id == Id.Format_Star:
                            if arg_index < num_args:
                                precision_str = varargs[arg_index]
                                precision_spid = spids[arg_index]
                                arg_index += 1
                            else:
                                precision_str = ''
                                precision_spid = runtime.NO_SPID
                        else:
                            raise AssertionError()

                        try:
                            precision = int(precision_str)
                        except ValueError:
                            if precision_spid == runtime.NO_SPID:
                                precision_spid = part.precision.span_id
                            self.errfmt.Print_(
                                'printf got invalid precision %r' %
                                precision_str,
                                span_id=precision_spid)
                            return 1

                    #log('index=%d n=%d', arg_index, num_args)
                    if arg_index < num_args:
                        s = varargs[arg_index]
                        word_spid = spids[arg_index]
                        arg_index += 1
                    else:
                        s = ''
                        word_spid = runtime.NO_SPID

                    typ = part.type.val
                    if typ == 's':
                        if precision >= 0:
                            s = s[:precision]  # truncate

                    elif typ == 'q':
                        s = qsn.maybe_shell_encode(s)

                    elif typ == 'b':
                        # Process just like echo -e, except \c handling is simpler.

                        c_parts = []  # type: List[str]
                        lex = match.EchoLexer(s)
                        while True:
                            id_, tok_val = lex.Next()
                            if id_ == Id.Eol_Tok:  # Note: This is really a NUL terminator
                                break

                            # TODO: add span_id from argv
                            tok = Token(id_, runtime.NO_SPID, tok_val)
                            p = word_compile.EvalCStringToken(tok)

                            # Unusual behavior: '\c' aborts processing!
                            if p is None:
                                backslash_c = True
                                break

                            c_parts.append(p)
                        s = ''.join(c_parts)

                    elif typ in 'diouxX' or part.type.id == Id.Format_Time:
                        try:
                            d = int(s)
                        except ValueError:
                            if len(s) >= 1 and s[0] in '\'"':
                                # TODO: utf-8 decode s[1:] to be more correct.  Probably
                                # depends on issue #366, a utf-8 library.
                                # Note: len(s) == 1 means there is a NUL (0) after the quote..
                                d = ord(s[1]) if len(s) >= 2 else 0
                            elif part.type.id == Id.Format_Time and len(
                                    s) == 0 and word_spid == runtime.NO_SPID:
                                # Note: No argument means -1 for %(...)T as in Bash Reference
                                #   Manual 4.2 "If no argument is specified, conversion behaves
                                #   as if -1 had been given."
                                d = -1
                            else:
                                if word_spid == runtime.NO_SPID:
                                    # Blame the format string
                                    blame_spid = part.type.span_id
                                else:
                                    blame_spid = word_spid
                                self.errfmt.Print_(
                                    'printf expected an integer, got %r' % s,
                                    span_id=blame_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 = mylib.octal(d)
                            elif typ == 'x':
                                s = mylib.hex_lower(d)
                            elif typ == 'X':
                                s = mylib.hex_upper(d)

                        elif part.type.id == Id.Format_Time:
                            # %(...)T

                            # Initialize timezone:
                            #   `localtime' uses the current timezone information initialized
                            #   by `tzset'.  The function `tzset' refers to the environment
                            #   variable `TZ'.  When the exported variable `TZ' is present,
                            #   its value should be reflected in the real environment
                            #   variable `TZ' before call of `tzset'.
                            #
                            # Note: unlike LANG, TZ doesn't seem to change behavior if it's
                            # not exported.
                            #
                            # TODO: In Oil, provide an API that doesn't rely on libc's
                            # global state.

                            tzcell = self.mem.GetCell('TZ')
                            if tzcell and tzcell.exported and tzcell.val.tag_(
                            ) == value_e.Str:
                                tzval = cast(value__Str, tzcell.val)
                                posix.putenv('TZ', tzval.s)

                            time_.tzset()

                            # Handle special values:
                            #   User can specify two special values -1 and -2 as in Bash
                            #   Reference Manual 4.2: "Two special argument values may be
                            #   used: -1 represents the current time, and -2 represents the
                            #   time the shell was invoked." from
                            #   https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-printf
                            if d == -1:  # the current time
                                ts = time_.time()
                            elif d == -2:  # the shell start time
                                ts = self.shell_start_time
                            else:
                                ts = d

                            s = time_.strftime(typ[1:-2], time_.localtime(ts))
                            if precision >= 0:
                                s = s[:precision]  # truncate

                        else:
                            raise AssertionError()

                    else:
                        raise AssertionError()

                    if width >= 0:
                        if len(flags):
                            if '-' in flags:
                                s = s.ljust(width, ' ')
                            elif '0' in flags:
                                s = s.rjust(width, '0')
                            else:
                                pass
                        else:
                            s = s.rjust(width, ' ')

                    out.append(s)

                else:
                    raise AssertionError()

                if backslash_c:  # 'printf %b a\cb xx' - \c terminates processing!
                    break

            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 is not None:
            # TODO: get the span_id for arg.v!
            v_spid = runtime.NO_SPID

            arena = self.parse_ctx.arena
            a_parser = self.parse_ctx.MakeArithParser(arg.v)

            with alloc.ctx_Location(arena, source.ArgvWord(v_spid)):
                try:
                    anode = a_parser.Parse()
                except error.Parse as e:
                    ui.PrettyPrintError(e, arena)  # show parse error
                    e_usage('Invalid -v expression', span_id=v_spid)

            lval = self.arith_ev.EvalArithLhs(anode, v_spid)

            if not self.exec_opts.eval_unsafe_arith(
            ) and lval.tag_() != lvalue_e.Named:
                e_usage(
                    '-v expected a variable name.  shopt -s eval_unsafe_arith allows expressions',
                    span_id=v_spid)

            state.SetRef(self.mem, lval, value.Str(result))
        else:
            mylib.Stdout().write(result)
        return 0
Beispiel #20
0
  def Run(self, cmd_val):
    # type: (cmd_value__Argv) -> int
    attrs, arg_r = flag_spec.ParseCmdVal('trap', cmd_val)
    arg = arg_types.trap(attrs.attrs)

    if arg.p:  # Print registered handlers
      for name, value in self.traps.iteritems():
        # The unit tests rely on this being one line.
        # bash prints a line that can be re-parsed.
        print('%s %s' % (name, value.__class__.__name__))

      return 0

    if arg.l:  # List valid signals and hooks
      ordered = _SIGNAL_NAMES.items()
      ordered.sort(key=lambda x: x[1])

      for name in _HOOK_NAMES:
        print('   %s' % name)
      for name, int_val in ordered:
        print('%2d %s' % (int_val, name))

      return 0

    code_str = arg_r.ReadRequired('requires a code string')
    sig_spec, sig_spid = arg_r.ReadRequired2('requires a signal or hook name')

    # sig_key is NORMALIZED sig_spec: a signal number string or string hook
    # name.
    sig_key = None  # type: Optional[str]
    sig_num = None
    if sig_spec in _HOOK_NAMES:
      sig_key = sig_spec
    elif sig_spec == '0':  # Special case
      sig_key = 'EXIT'
    else:
      sig_num = _GetSignalNumber(sig_spec)
      if sig_num is not None:
        sig_key = str(sig_num)

    if sig_key is None:
      self.errfmt.Print("Invalid signal or hook %r", sig_spec,
                        span_id=cmd_val.arg_spids[2])
      return 1

    # NOTE: sig_spec isn't validated when removing handlers.
    if code_str == '-':
      if sig_key in _HOOK_NAMES:
        try:
          del self.traps[sig_key]
        except KeyError:
          pass
        return 0

      if sig_num is not None:
        try:
          del self.traps[sig_key]
        except KeyError:
          pass

        self.sig_state.RemoveUserTrap(sig_num)
        return 0

      raise AssertionError('Signal or trap')

    # Try parsing the code first.
    node = self._ParseTrapCode(code_str)
    if node is None:
      return 1  # ParseTrapCode() prints an error for us.

    # Register a hook.
    if sig_key in _HOOK_NAMES:
      if sig_key in ('ERR', 'RETURN', 'DEBUG'):
        stderr_line("osh warning: The %r hook isn't implemented", sig_spec)
      self.traps[sig_key] = _TrapHandler(node, self.nodes_to_run)
      return 0

    # Register a signal.
    if sig_num is not None:
      handler = _TrapHandler(node, self.nodes_to_run)
      # For signal handlers, the traps dictionary is used only for debugging.
      self.traps[sig_key] = handler
      if sig_num in (signal.SIGKILL, signal.SIGSTOP):
        self.errfmt.Print("Signal %r can't be handled", sig_spec,
                          span_id=sig_spid)
        # Other shells return 0, but this seems like an obvious error
        return 1
      self.sig_state.AddUserTrap(sig_num, handler)
      return 0

    raise AssertionError('Signal or trap')
Beispiel #21
0
  def Run(self, cmd_val):
    # type: (cmd_value__Argv) -> int
    attrs, arg_r = flag_spec.ParseCmdVal('wait', cmd_val)
    arg = arg_types.wait(attrs.attrs)

    job_ids, arg_spids = arg_r.Rest2()

    if arg.n:
      # wait -n returns the exit status of the JOB.
      # You don't know WHICH process, which is odd.

      # TODO: this should wait for the next JOB, which may be multiple
      # processes.
      # Bash has a wait_for_any_job() function, which loops until the jobs
      # table changes.
      #
      # target_count = self.job_state.NumRunning() - 1
      # while True:
      #   if not self.waiter.WaitForOne():
      #     break
      #
      #   if self.job_state.NumRunning == target_count:
      #     break
      #    
      #log('wait next')

      if self.waiter.WaitForOne():
        return self.waiter.last_status
      else:
        return 127  # nothing to wait for

    if len(job_ids) == 0:
      #log('wait all')

      i = 0
      while True:
        # BUG: If there is a STOPPED process, this will hang forever, because
        # we don't get ECHILD.
        # Not sure it matters since you can now Ctrl-C it.

        if not self.waiter.WaitForOne():
          break  # nothing to wait for
        i += 1
        if self.job_state.NoneAreRunning():
          break

      log('Waited for %d processes', i)
      return 0

    # Get list of jobs.  Then we need to check if they are ALL stopped.
    # Returns the exit code of the last one on the COMMAND LINE, not the exit
    # code of last one to FINISH.
    status = 1  # error
    for i, job_id in enumerate(job_ids):
      span_id = arg_spids[i]

      # The % syntax is sort of like ! history sub syntax, with various queries.
      # https://stackoverflow.com/questions/35026395/bash-what-is-a-jobspec
      if job_id.startswith('%'):
        raise error.Usage(
            "doesn't support bash-style jobspecs (got %r)" % job_id,
            span_id=span_id)

      # Does it look like a PID?
      try:
        pid = int(job_id)
      except ValueError:
        raise error.Usage('expected PID or jobspec, got %r' % job_id,
                              span_id=span_id)

      job = self.job_state.JobFromPid(pid)
      if job is None:
        self.errfmt.Print("%s isn't a child of this shell", pid,
                          span_id=span_id)
        return 127

      # TODO: Does this wait for pipelines?
      job_status = job.JobWait(self.waiter)

      UP_job_status = job_status
      with tagswitch(job_status) as case:
        if case(job_status_e.Proc):
          job_status = cast(job_status__Proc, UP_job_status)
          status = job_status.code
        elif case(job_status_e.Pipeline):
          # TODO: handle PIPESTATUS?
          job_status = cast(job_status__Pipeline, UP_job_status)
          # Is this right?
          status = job_status.codes[-1]
        else:
          raise AssertionError

    return status
Beispiel #22
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('cd', cmd_val)
        arg = arg_types.cd(attrs.attrs)

        dest_dir, arg_spid = arg_r.Peek2()
        if dest_dir is None:
            val = self.mem.GetVar('HOME')
            try:
                dest_dir = state.GetString(self.mem, 'HOME')
            except error.Runtime as e:
                self.errfmt.Print_(e.UserErrorString())
                return 1

        if dest_dir == '-':
            try:
                dest_dir = state.GetString(self.mem, 'OLDPWD')
                print(dest_dir)  # Shells print the directory
            except error.Runtime as e:
                self.errfmt.Print_(e.UserErrorString())
                return 1

        try:
            pwd = state.GetString(self.mem, 'PWD')
        except error.Runtime as e:
            self.errfmt.Print_(e.UserErrorString())
            return 1

        # Calculate new directory, chdir() to it, then set PWD to it.  NOTE: We can't
        # call posix.getcwd() because it can raise OSError if the directory was
        # removed (ENOENT.)
        abspath = os_path.join(pwd, dest_dir)  # make it absolute, for cd ..
        if arg.P:
            # -P means resolve symbolic links, then process '..'
            real_dest_dir = libc.realpath(abspath)
        else:
            # -L means process '..' first.  This just does string manipulation.  (But
            # realpath afterward isn't correct?)
            real_dest_dir = os_path.normpath(abspath)

        try:
            posix.chdir(real_dest_dir)
        except OSError as e:
            self.errfmt.Print_("cd %r: %s" %
                               (real_dest_dir, pyutil.strerror_OS(e)),
                               span_id=arg_spid)
            return 1

        state.ExportGlobalString(self.mem, 'PWD', real_dest_dir)

        # WEIRD: We need a copy that is NOT PWD, because the user could mutate PWD.
        # Other shells use global variables.
        self.mem.SetPwd(real_dest_dir)

        if cmd_val.block:
            self.dir_stack.Push(real_dest_dir)
            try:
                unused = self.cmd_ev.EvalBlock(cmd_val.block)
            finally:  # TODO: Change this to a context manager.
                # note: it might be more consistent to use an exception here.
                if not _PopDirStack(self.mem, self.dir_stack, self.errfmt):
                    return 1

        else:  # No block
            state.ExportGlobalString(self.mem, 'OLDPWD', pwd)
            self.dir_stack.Reset()  # for pushd/popd/dirs

        return 0
Beispiel #23
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        """
    printf: printf [-v var] format [argument ...]
    """
        attrs, arg_r = flag_spec.ParseCmdVal('printf', cmd_val)
        arg = arg_types.printf(attrs.attrs)

        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)
            parser = _FormatStringParser(lexer)

            with alloc.ctx_Location(arena, source.ArgvWord(fmt_spid)):
                try:
                    parts = parser.Parse()
                except error.Parse as e:
                    self.errfmt.PrettyPrintError(e)
                    return 2  # parse error

            self.parse_cache[fmt] = parts

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

        out = []  # type: List[str]
        status = self._Format(parts, varargs, spids, out)
        if status != 0:
            return status  # failure

        result = ''.join(out)
        if arg.v is not None:
            # TODO: get the span_id for arg.v!
            v_spid = runtime.NO_SPID

            arena = self.parse_ctx.arena
            a_parser = self.parse_ctx.MakeArithParser(arg.v)

            with alloc.ctx_Location(arena, source.ArgvWord(v_spid)):
                try:
                    anode = a_parser.Parse()
                except error.Parse as e:
                    ui.PrettyPrintError(e, arena)  # show parse error
                    e_usage('Invalid -v expression', span_id=v_spid)

            lval = self.arith_ev.EvalArithLhs(anode, v_spid)

            if not self.exec_opts.eval_unsafe_arith(
            ) and lval.tag_() != lvalue_e.Named:
                e_usage(
                    '-v expected a variable name.  shopt -s eval_unsafe_arith allows expressions',
                    span_id=v_spid)

            state.SetRef(self.mem, lval, value.Str(result))
        else:
            mylib.Stdout().write(result)
        return 0
Beispiel #24
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('read', cmd_val)
        arg = arg_types.read(attrs.attrs)
        names = arg_r.Rest()

        if arg.n >= 0:  # read a certain number of bytes (-1 means unset)
            if len(names):
                name = names[0]
            else:
                name = 'REPLY'  # default variable name

            status = 0
            stdin_fd = self.stdin.fileno()
            if self.stdin.isatty():  # set stdin to read in unbuffered mode
                s = passwd.ReadBytesFromTerminal(stdin_fd, arg.n)
            else:
                chunks = []  # type: List[str]
                n = arg.n
                while n > 0:
                    chunk = posix.read(stdin_fd, n)  # read at up to N chars
                    if len(chunk) == 0:
                        break
                    chunks.append(chunk)
                    n -= len(chunk)
                s = ''.join(chunks)

            # DIdn't read all the bytes we wanted
            if len(s) != n:
                status = 1

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

        if len(names) == 0:
            names.append('REPLY')

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

        if arg.d is not None:
            if len(arg.d):
                delim_char = arg.d[0]
            else:
                delim_char = '\0'  # -d '' delimits by NUL
        else:
            delim_char = '\n'  # read a line

        # We have to read more than one line if there is a line continuation (and
        # it's not -r).
        parts = []  # type: List[mylib.BufWriter]
        join_next = False
        status = 0
        while True:
            line, eof = ReadLineFromStdin(delim_char)

            if eof:
                # status 1 to terminate loop.  (This is true even though we set
                # variables).
                status = 1

            #log('LINE %r', line)
            if len(line) == 0:
                break

            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

        entries = [buf.getvalue() for buf in parts]
        num_parts = len(entries)
        if arg.a is not None:
            state.SetArrayDynamic(self.mem, arg.a, entries)
        else:
            for i in xrange(max_results):
                if i < num_parts:
                    s = entries[i]
                else:
                    s = ''  # if there are too many variables
                #log('read: %s = %s', names[i], s)
                state.SetStringDynamic(self.mem, names[i], s)

        return status