示例#1
0
  def _RunSimpleCommand(self, argv, fork_external):
    # This happens when you write "$@" but have no arguments.
    if not argv:
      return 0  # status 0, or skip it?

    arg0 = argv[0]

    builtin_id = builtin.ResolveSpecial(arg0)
    if builtin_id != builtin_e.NONE:
      try:
        status = self._RunBuiltin(builtin_id, argv)
      except args.UsageError as e:
        # TODO: Make this message more consistent?
        util.usage(str(e))
        status = 2  # consistent error code for usage error
      return status

    # Builtins like 'true' can be redefined as functions.
    func_node = self.funcs.get(arg0)
    if func_node is not None:
      # NOTE: Functions could call 'exit 42' directly, etc.
      status = self.RunFunc(func_node, argv)
      return status

    builtin_id = builtin.Resolve(arg0)
    if builtin_id != builtin_e.NONE:
      try:
        status = self._RunBuiltin(builtin_id, argv)
      except args.UsageError as e:
        # TODO: Make this message more consistent?
        util.usage(str(e))
        status = 2  # consistent error code for usage error
      return status

    environ = self.mem.GetExported()  # Include temporary variables

    if fork_external:
      thunk = process.ExternalThunk(argv, environ)
      p = process.Process(thunk)
      status = p.Run(self.waiter)
      return status

    # NOTE: Never returns!
    process.ExecExternalProgram(argv, environ)
示例#2
0
def OilMain(argv):
    spec = args.FlagsAndOptions()
    # TODO: -h too
    spec.LongFlag('--help')
    spec.LongFlag('--version')
    #builtin.AddOptionsToArgSpec(spec)

    try:
        opts, opt_index = spec.Parse(argv)
    except args.UsageError as e:
        util.usage(str(e))
        return 2

    if opts.help:
        loader = util.GetResourceLoader()
        builtin.Help(['oil-usage'], loader)
        return 0
    if opts.version:
        # OSH version is the only binary in Oil right now, so it's all one version.
        _ShowVersion()
        return 0

    raise NotImplementedError('oil')
    return 0
示例#3
0
def OshMain(argv0, argv, login_shell):
    spec = args.FlagsAndOptions()
    spec.ShortFlag('-c', args.Str, quit_parsing_flags=True)  # command string
    spec.ShortFlag('-i')  # interactive

    # TODO: -h too
    spec.LongFlag('--help')
    spec.LongFlag('--version')
    spec.LongFlag(
        '--ast-format',
        ['text', 'abbrev-text', 'html', 'abbrev-html', 'oheap', 'none'],
        default='abbrev-text')
    spec.LongFlag('--show-ast')  # execute and show
    spec.LongFlag('--fix')
    spec.LongFlag('--debug-spans')  # For oshc translate
    spec.LongFlag('--print-status')
    spec.LongFlag(
        '--trace',
        ['cmd-parse', 'word-parse', 'lexer'])  # NOTE: can only trace one now
    spec.LongFlag('--hijack-shebang')

    # For benchmarks/*.sh
    spec.LongFlag('--parser-mem-dump', args.Str)
    spec.LongFlag('--runtime-mem-dump', args.Str)

    builtin.AddOptionsToArgSpec(spec)

    try:
        opts, opt_index = spec.Parse(argv)
    except args.UsageError as e:
        util.usage(str(e))
        return 2

    if opts.help:
        loader = util.GetResourceLoader()
        builtin.Help(['osh-usage'], loader)
        return 0
    if opts.version:
        # OSH version is the only binary in Oil right now, so it's all one version.
        _ShowVersion()
        return 0

    trace_state = util.TraceState()
    if 'cmd-parse' == opts.trace:
        util.WrapMethods(cmd_parse.CommandParser, trace_state)
    if 'word-parse' == opts.trace:
        util.WrapMethods(word_parse.WordParser, trace_state)
    if 'lexer' == opts.trace:
        util.WrapMethods(lexer.Lexer, trace_state)

    if opt_index == len(argv):
        dollar0 = argv0
    else:
        dollar0 = argv[opt_index]  # the script name, or the arg after -c

    # TODO: Create a --parse action or 'osh parse' or 'oil osh-parse'
    # osh-fix
    # It uses a different memory-management model.  It's a batch program and not
    # an interactive program.

    pool = alloc.Pool()
    arena = pool.NewArena()

    # TODO: Maybe wrap this initialization sequence up in an oil_State, like
    # lua_State.
    status_lines = ui.MakeStatusLines()
    mem = state.Mem(dollar0, argv[opt_index + 1:], os.environ, arena)
    funcs = {}

    # Passed to Executor for 'complete', and passed to completion.Init
    if completion:
        comp_lookup = completion.CompletionLookup()
    else:
        # TODO: NullLookup?
        comp_lookup = None

    exec_opts = state.ExecOpts(mem)
    builtin.SetExecOpts(exec_opts, opts.opt_changes)

    fd_state = process.FdState()
    ex = cmd_exec.Executor(mem, fd_state, status_lines, funcs, readline,
                           completion, comp_lookup, exec_opts, arena)

    # NOTE: The rc file can contain both commands and functions... ideally we
    # would only want to save nodes/lines for the functions.
    try:
        rc_path = 'oilrc'
        arena.PushSource(rc_path)
        with open(rc_path) as f:
            rc_line_reader = reader.FileLineReader(f, arena)
            _, rc_c_parser = parse_lib.MakeParser(rc_line_reader, arena)
            try:
                rc_node = rc_c_parser.ParseWholeFile()
                if not rc_node:
                    err = rc_c_parser.Error()
                    ui.PrintErrorStack(err, arena, sys.stderr)
                    return 2  # parse error is code 2
            finally:
                arena.PopSource()

        status = ex.Execute(rc_node)
        #print('oilrc:', status, cflow, file=sys.stderr)
        # Ignore bad status?
    except IOError as e:
        if e.errno != errno.ENOENT:
            raise

    if opts.c is not None:
        arena.PushSource('<command string>')
        line_reader = reader.StringLineReader(opts.c, arena)
        if opts.i:  # -c and -i can be combined
            exec_opts.interactive = True
    elif opts.i:  # force interactive
        arena.PushSource('<stdin -i>')
        line_reader = reader.InteractiveLineReader(OSH_PS1, arena)
        exec_opts.interactive = True
    else:
        try:
            script_name = argv[opt_index]
        except IndexError:
            if sys.stdin.isatty():
                arena.PushSource('<interactive>')
                line_reader = reader.InteractiveLineReader(OSH_PS1, arena)
                exec_opts.interactive = True
            else:
                arena.PushSource('<stdin>')
                line_reader = reader.FileLineReader(sys.stdin, arena)
        else:
            arena.PushSource(script_name)
            try:
                f = fd_state.Open(script_name)
            except OSError as e:
                util.error("Couldn't open %r: %s", script_name,
                           os.strerror(e.errno))
                return 1
            line_reader = reader.FileLineReader(f, arena)

    # TODO: assert arena.NumSourcePaths() == 1
    # TODO: .rc file needs its own arena.
    w_parser, c_parser = parse_lib.MakeParser(line_reader, arena)

    if exec_opts.interactive:
        # NOTE: We're using a different evaluator here.  The completion system can
        # also run functions... it gets the Executor through Executor._Complete.
        if HAVE_READLINE:
            splitter = legacy.SplitContext(mem)
            ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter)
            status_out = completion.StatusOutput(status_lines, exec_opts)
            completion.Init(pool, builtin.BUILTIN_DEF, mem, funcs, comp_lookup,
                            status_out, ev)

        return InteractiveLoop(opts, ex, c_parser, w_parser, line_reader)
    else:
        # Parse the whole thing up front
        #print('Parsing file')

        _tlog('ParseWholeFile')
        # TODO: Do I need ParseAndEvalLoop?  How is it different than
        # InteractiveLoop?
        try:
            node = c_parser.ParseWholeFile()
        except util.ParseError as e:
            ui.PrettyPrintError(e, arena, sys.stderr)
            print('parse error: %s' % e.UserErrorString(), file=sys.stderr)
            return 2
        else:
            # TODO: Remove this older form of error handling.
            if not node:
                err = c_parser.Error()
                assert err, err  # can't be empty
                ui.PrintErrorStack(err, arena, sys.stderr)
                return 2  # parse error is code 2

        do_exec = True
        if opts.fix:
            #log('SPANS: %s', arena.spans)
            osh2oil.PrintAsOil(arena, node, opts.debug_spans)
            do_exec = False
        if exec_opts.noexec:
            do_exec = False

        # Do this after parsing the entire file.  There could be another option to
        # do it before exiting runtime?
        if opts.parser_mem_dump:
            # This might be superstition, but we want to let the value stabilize
            # after parsing.  bash -c 'cat /proc/$$/status' gives different results
            # with a sleep.
            time.sleep(0.001)
            input_path = '/proc/%d/status' % os.getpid()
            with open(input_path) as f, open(opts.parser_mem_dump, 'w') as f2:
                contents = f.read()
                f2.write(contents)
                log('Wrote %s to %s (--parser-mem-dump)', input_path,
                    opts.parser_mem_dump)

        # -n prints AST, --show-ast prints and executes
        if exec_opts.noexec or opts.show_ast:
            if opts.ast_format == 'none':
                print('AST not printed.', file=sys.stderr)
            elif opts.ast_format == 'oheap':
                # TODO: Make this a separate flag?
                if sys.stdout.isatty():
                    raise RuntimeError(
                        'ERROR: Not dumping binary data to a TTY.')
                f = sys.stdout

                enc = encode.Params()
                out = encode.BinOutput(f)
                encode.EncodeRoot(node, enc, out)

            else:  # text output
                f = sys.stdout

                if opts.ast_format in ('text', 'abbrev-text'):
                    ast_f = fmt.DetectConsoleOutput(f)
                elif opts.ast_format in ('html', 'abbrev-html'):
                    ast_f = fmt.HtmlOutput(f)
                else:
                    raise AssertionError
                abbrev_hook = (ast_lib.AbbreviateNodes
                               if 'abbrev-' in opts.ast_format else None)
                tree = fmt.MakeTree(node, abbrev_hook=abbrev_hook)
                ast_f.FileHeader()
                fmt.PrintTree(tree, ast_f)
                ast_f.FileFooter()
                ast_f.write('\n')

            #util.log("Execution skipped because 'noexec' is on ")
            status = 0

        if do_exec:
            _tlog('Execute(node)')
            status = ex.ExecuteAndRunExitTrap(node)
            # NOTE: 'exit 1' is ControlFlow and gets here, but subshell/commandsub
            # don't because they call sys.exit().
            if opts.runtime_mem_dump:
                # This might be superstition, but we want to let the value stabilize
                # after parsing.  bash -c 'cat /proc/$$/status' gives different results
                # with a sleep.
                time.sleep(0.001)
                input_path = '/proc/%d/status' % os.getpid()
                with open(input_path) as f, open(opts.runtime_mem_dump,
                                                 'w') as f2:
                    contents = f.read()
                    f2.write(contents)
                    log('Wrote %s to %s (--runtime-mem-dump)', input_path,
                        opts.runtime_mem_dump)

        else:
            status = 0

    return status
示例#4
0
def OshMain(argv, login_shell):
    spec = args.FlagsAndOptions()
    spec.ShortFlag('-c', args.Str, quit_parsing_flags=True)  # command string
    spec.ShortFlag('-i')  # interactive

    # TODO: -h too
    spec.LongFlag('--help')
    spec.LongFlag('--version')
    spec.LongFlag('--ast-format',
                  ['text', 'abbrev-text', 'html', 'abbrev-html', 'oheap'],
                  default='abbrev-text')
    spec.LongFlag('--show-ast')  # execute and show
    spec.LongFlag('--fix')
    spec.LongFlag('--debug-spans')
    spec.LongFlag('--print-status')
    spec.LongFlag(
        '--trace',
        ['cmd-parse', 'word-parse', 'lexer'])  # NOTE: can only trace one now
    spec.LongFlag('--hijack-shebang')

    builtin.AddOptionsToArgSpec(spec)

    try:
        opts, opt_index = spec.Parse(argv)
    except args.UsageError as e:
        util.usage(str(e))
        return 2

    if opts.help:
        loader = util.GetResourceLoader()  # TOOD: Use Global
        builtin.Help(['osh-usage'], loader)
        return 0
    if opts.version:
        # OSH version is the only binary in Oil right now, so it's all one version.
        _ShowVersion()
        return 0

    trace_state = util.TraceState()
    if 'cmd-parse' == opts.trace:
        util.WrapMethods(cmd_parse.CommandParser, trace_state)
    if 'word-parse' == opts.trace:
        util.WrapMethods(word_parse.WordParser, trace_state)
    if 'lexer' == opts.trace:
        util.WrapMethods(lexer.Lexer, trace_state)

    if opt_index == len(argv):
        dollar0 = sys.argv[0]  # e.g. bin/osh
    else:
        dollar0 = argv[opt_index]  # the script name, or the arg after -c

    # TODO: Create a --parse action or 'osh parse' or 'oil osh-parse'
    # osh-fix
    # It uses a different memory-management model.  It's a batch program and not
    # an interactive program.

    pool = alloc.Pool()
    arena = pool.NewArena()

    # TODO: Maybe wrap this initialization sequence up in an oil_State, like
    # lua_State.
    status_lines = ui.MakeStatusLines()
    mem = state.Mem(dollar0, argv[opt_index + 1:], os.environ)
    funcs = {}

    # Passed to Executor for 'complete', and passed to completion.Init
    if completion:
        comp_lookup = completion.CompletionLookup()
    else:
        # TODO: NullLookup?
        comp_lookup = None
    exec_opts = state.ExecOpts()
    builtin.SetExecOpts(exec_opts, opts.opt_changes)

    # TODO: How to get a handle to initialized builtins here?
    # tokens.py has it.  I think you just make a separate table, with
    # metaprogramming.
    ex = cmd_exec.Executor(mem, status_lines, funcs, completion, comp_lookup,
                           exec_opts, arena)

    # NOTE: The rc file can contain both commands and functions... ideally we
    # would only want to save nodes/lines for the functions.
    try:
        rc_path = 'oilrc'
        arena.PushSource(rc_path)
        with open(rc_path) as f:
            rc_line_reader = reader.FileLineReader(f, arena=arena)
            _, rc_c_parser = parse_lib.MakeParser(rc_line_reader, arena)
            try:
                rc_node = rc_c_parser.ParseWholeFile()
                if not rc_node:
                    # TODO: Error should return a token, and then the token should have a
                    # arena index, and then look that up in the arena.
                    err = rc_c_parser.Error()
                    ui.PrintErrorStack(err, arena, sys.stderr)
                    return 2  # parse error is code 2
            finally:
                arena.PopSource()

        status = ex.Execute(rc_node)
        #print('oilrc:', status, cflow, file=sys.stderr)
        # Ignore bad status?
    except IOError as e:
        if e.errno != errno.ENOENT:
            raise

    if opts.c is not None:
        arena.PushSource('<command string>')
        line_reader = reader.StringLineReader(opts.c, arena=arena)
        interactive = False
    elif opts.i:  # force interactive
        arena.PushSource('<stdin -i>')
        line_reader = reader.InteractiveLineReader(OSH_PS1, arena=arena)
        interactive = True
    else:
        try:
            script_name = argv[opt_index]
        except IndexError:
            if sys.stdin.isatty():
                arena.PushSource('<interactive>')
                line_reader = reader.InteractiveLineReader(OSH_PS1,
                                                           arena=arena)
                interactive = True
            else:
                arena.PushSource('<stdin>')
                line_reader = reader.FileLineReader(sys.stdin, arena=arena)
                interactive = False
        else:
            arena.PushSource(script_name)
            # TODO: Does this open file descriptor need to be moved beyond 3..9 ?
            # Yes!  See dash input.c setinputfile.  It calls savefd().
            # TODO: It also needs to be closed later.
            try:
                f = open(script_name)
            except IOError as e:
                util.error("Couldn't open %r: %s", script_name,
                           os.strerror(e.errno))
                return 1
            line_reader = reader.FileLineReader(f, arena=arena)
            interactive = False

    # TODO: assert arena.NumSourcePaths() == 1
    # TODO: .rc file needs its own arena.
    w_parser, c_parser = parse_lib.MakeParser(line_reader, arena)

    if interactive:
        # NOTE: We're using a different evaluator here.  The completion system can
        # also run functions... it gets the Executor through Executor._Complete.
        if HAVE_READLINE:
            ev = word_eval.CompletionWordEvaluator(mem, exec_opts)
            status_out = completion.StatusOutput(status_lines, exec_opts)
            completion.Init(builtin.BUILTIN_DEF, mem, funcs, comp_lookup,
                            status_out, ev)

        # TODO: Could instantiate "printer" instead of showing ops
        InteractiveLoop(opts, ex, c_parser, w_parser, line_reader)
        status = 0  # TODO: set code
    else:
        # Parse the whole thing up front
        #print('Parsing file')

        tlog('ParseWholeFile')
        # TODO: Do I need ParseAndEvalLoop?  How is it different than
        # InteractiveLoop?
        try:
            node = c_parser.ParseWholeFile()
        except util.ParseError as e:
            ui.PrettyPrintError(e, arena, sys.stderr)
            print('parse error: %s' % e.UserErrorString(), file=sys.stderr)
            return 2
        else:
            # TODO: Remove this older form of error handling.
            if not node:
                err = c_parser.Error()
                ui.PrintErrorStack(err, arena, sys.stderr)
                return 2  # parse error is code 2

        do_exec = True
        if opts.fix:
            osh2oil.PrintAsOil(arena, node, opts.debug_spans)
            do_exec = False
        if exec_opts.noexec:
            do_exec = False

        if exec_opts.noexec or opts.show_ast:  # -n shows the AST
            if opts.ast_format == 'oheap':
                # TODO: Make this a separate flag?
                if sys.stdout.isatty():
                    raise RuntimeError(
                        'ERROR: Not dumping binary data to a TTY.')
                f = sys.stdout

                enc = encode.Params()
                out = encode.BinOutput(f)
                encode.EncodeRoot(node, enc, out)

            else:  # text output
                f = sys.stdout

                if opts.ast_format in ('text', 'abbrev-text'):
                    ast_f = fmt.DetectConsoleOutput(f)
                elif opts.ast_format in ('html', 'abbrev-html'):
                    ast_f = fmt.HtmlOutput(f)
                else:
                    raise AssertionError
                abbrev_hook = (ast.AbbreviateNodes
                               if 'abbrev-' in opts.ast_format else None)
                tree = fmt.MakeTree(node, abbrev_hook=abbrev_hook)
                ast_f.FileHeader()
                fmt.PrintTree(tree, ast_f)
                ast_f.FileFooter()
                ast_f.write('\n')

            #util.log("Execution skipped because 'noexec' is on ")
            status = 0

        if do_exec:
            tlog('Execute(node)')
            status = ex.Execute(node)
        else:
            status = 0

    return status
示例#5
0
文件: builtin.py 项目: lheckemann/oil
def Wait(argv, waiter, job_state, mem):
    """
  wait: wait [-n] [id ...]
      Wait for job completion and return exit status.

      Waits for each process identified by an ID, which may be a process ID or a
      job specification, and reports its termination status.  If ID is not
      given, waits for all currently active child processes, and the return
      status is zero.  If ID is a a job specification, waits for all processes
      in that job's pipeline.

      If the -n option is supplied, waits for the next job to terminate and
      returns its exit status.

      Exit Status:
      Returns the status of the last ID; fails if ID is invalid or an invalid
      option is given.

  # Job spec, %1 %2, %%, %?a etc.

  http://mywiki.wooledge.org/BashGuide/JobControl#jobspec

  This is different than a PID?  But it does have a PID.
  """
    # NOTE: echo can't use getopt because of 'echo ---'
    # But that's a special case; the rest of the builtins can use it.
    # We must respect -- everywhere, EXCEPT echo.  'wait -- -n' should work must work.

    opt_n = False

    try:
        opts, args = getopt.getopt(argv, 'n')
    except getopt.GetoptError as e:
        util.usage(str(e))
        sys.exit(2)
    for name, val in opts:
        if name == '-n':
            opt_n = True
        else:
            raise AssertionError

    if opt_n:
        # wait -n returns the exit status of the process.  But how do you know
        # WHICH process?  That doesn't seem useful.
        log('wait next')
        if waiter.Wait():
            return waiter.last_status
        else:
            return 127  # nothing to wait for

    if not args:
        log('wait all')
        # TODO: get all background jobs from JobState?
        i = 0
        while True:
            if not waiter.Wait():
                break  # nothing to wait for
            i += 1
            if job_state.AllDone():
                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 FINSIH.

    status = 1  # error
    for a in args:
        # NOTE: osh doesn't accept 'wait %1' yet
        try:
            jid = int(a)
        except ValueError:
            util.error('Invalid argument %r', a)
            return 127

        job = job_state.jobs.get(jid)
        if job is None:
            util.error('No such job: %s', jid)
            return 127

        st = job.WaitUntilDone(waiter)
        if isinstance(st, list):
            status = st[-1]
            state.SetGlobalArray(mem, 'PIPESTATUS', [str(p) for p in st])
        else:
            status = st

    return status
示例#6
0
def Trap(argv, traps, nodes_to_run, ex):
    arg, i = TRAP_SPEC.Parse(argv)

    if arg.p:  # Print registered handlers
        for name, value in traps.iteritems():
            print(name)
            print(value)
            print()

        sys.stdout.flush()
        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))

        sys.stdout.flush()
        return 0

    try:
        code_str = argv[0]
        sig_spec = argv[1]
    except IndexError:
        util.usage('trap CODE SIGNAL_SPEC')
        return 1

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

        sig_val = _GetSignalValue(sig_spec)
        if sig_val is not None:
            try:
                del traps[sig_spec]
            except KeyError:
                pass

            # Restore default
            signal.signal(sig_val, signal.SIG_DFL)
            return 0

        util.error("Can't remove invalid trap %r" % sig_spec)
        return 1

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

    # Register a hook.
    if sig_spec in _HOOK_NAMES:
        if sig_spec in ('ERR', 'RETURN', 'DEBUG'):
            util.warn("*** The %r isn't yet implemented in OSH ***", sig_spec)
        traps[sig_spec] = _TrapHandler(node, nodes_to_run)
        return 0

    # Register a signal.
    sig_val = _GetSignalValue(sig_spec)
    if sig_val is not None:
        handler = _TrapHandler(node, nodes_to_run)
        # For signal handlers, the traps dictionary is used only for debugging.
        traps[sig_spec] = handler

        signal.signal(sig_val, handler)
        return 0

    util.error('Invalid trap %r' % sig_spec)
    return 1