Пример #1
0
  def _RunPipeline(self, node):
    pi = process.Pipeline()

    # First n-1 processes (which is empty when n == 1)
    n = len(node.children)
    for i in xrange(n - 1):
      p = self._MakeProcess(node.children[i])
      pi.Add(p)

    # Last piece of code is in THIS PROCESS.  'echo foo | read line; echo $line'
    pi.AddLast((self, node.children[n-1]))

    pipe_status = pi.Run(self.waiter, self.fd_state)
    state.SetGlobalArray(self.mem, 'PIPESTATUS', [str(p) for p in pipe_status])

    if self.exec_opts.pipefail:
      # The status is that of the last command that is non-zero.
      status = 0
      for st in pipe_status:
        if st != 0:
          status = st
    else:
      status = pipe_status[-1]  # status of last one is pipeline status

    return status
Пример #2
0
    def Matches(self, comp):
        # TODO: Delete COMPREPLY here?  It doesn't seem to be defined in bash by
        # default.
        argv, comp_words = comp.GetApiInput()

        state.SetGlobalArray(self.ex.mem, 'COMP_WORDS', comp_words)
        state.SetGlobalString(self.ex.mem, 'COMP_CWORD', str(comp.index))
        state.SetGlobalString(self.ex.mem, 'COMP_LINE', comp.line)
        state.SetGlobalString(self.ex.mem, 'COMP_POINT', str(comp.end))

        self.log('Running completion function %r with arguments %s',
                 self.func.name, argv)

        status = self.ex.RunFuncForCompletion(self.func, argv)
        if status == 124:
            self.log('Got status 124 from %r', self.func.name)
            raise _RetryCompletion()

        # Lame: COMP_REPLY would follow the naming convention!
        val = state.GetGlobal(self.ex.mem, 'COMPREPLY')
        if val.tag == value_e.Undef:
            util.error('Ran function %s but COMPREPLY was not defined',
                       self.func.name)
            return []

        if val.tag != value_e.StrArray:
            log('ERROR: COMPREPLY should be an array, got %s', val)
            return []
        self.log('COMPREPLY %s', val)

        # Return this all at once so we don't have a generator.  COMPREPLY happens
        # all at once anyway.
        return val.strs
Пример #3
0
  def _RunPipeline(self, node):
    pi = self._MakePipeline(node)

    pipe_status = pi.Run(self.waiter)
    state.SetGlobalArray(self.mem, 'PIPESTATUS', [str(p) for p in pipe_status])

    if self.exec_opts.pipefail:
      # The status is that of the last command that is non-zero.
      status = 0
      for st in pipe_status:
        if st != 0:
          status = st
    else:
      status = pipe_status[-1]  # status of last one is pipeline status

    return status
Пример #4
0
  def Matches(self, words, index, prefix):
    # TODO:
    # - Set COMP_CWORD etc. in ex.mem -- in the global namespace I guess
    # - Then parse the reply here

    # This is like a stack code:
    # for word in words:
    #   self.ex.PushString(word)
    # self.ex.PushString('COMP_WORDS')
    # self.ex.MakeArray()

    # self.ex.PushString(str(index))
    # self.ex.PushString('COMP_CWORD')

    # TODO: Get the name instead!
    # self.ex.PushString(self.func_name)
    # self.ex.Call()  # call wit no arguments

    # self.ex.PushString('COMP_REPLY')

    # How does this one work?
    # reply = []
    # self.ex.GetArray(reply)

    state.SetGlobalArray(self.ex.mem, 'COMP_WORDS', words)
    state.SetGlobalString(self.ex.mem, 'COMP_CWORD', str(index))

    self.ex.RunFunc(self.func, [])  # call with no arguments

    # Should be COMP_REPLY to follow naming convention!  Lame.
    val = state.GetGlobal(self.ex.mem, 'COMPREPLY')
    if val.tag == value_e.Undef:
      log('COMPREPLY not defined')
      return

    if val.tag != value_e.StrArray:
      log('ERROR: COMPREPLY should be an array, got %s', val)
      return
    reply = val.strs

    print('REPLY', reply)
    #reply = ['g1', 'g2', 'h1', 'i1']
    for name in sorted(reply):
      if name.startswith(prefix):
        yield name + ' '  # full word
Пример #5
0
 def _SetRegexMatches(self, matches):
     # type: (List[str]) -> None
     """For ~= to set the BASH_REMATCH array."""
     state.SetGlobalArray(self.mem, 'BASH_REMATCH', matches)
Пример #6
0
    def Matches(self, comp):
        # type: (Api) -> List[str]
        # Have to clear the response every time.  TODO: Reuse the object?
        state.SetGlobalArray(self.cmd_ev.mem, 'COMPREPLY', [])

        # New completions should use COMP_ARGV, a construct specific to OSH>
        state.SetGlobalArray(self.cmd_ev.mem, 'COMP_ARGV', comp.partial_argv)

        # Old completions may use COMP_WORDS.  It is split by : and = to emulate
        # bash's behavior.
        # More commonly, they will call _init_completion and use the 'words' output
        # of that, ignoring COMP_WORDS.
        comp_words = []
        for a in comp.partial_argv:
            AdjustArg(a, [':', '='], comp_words)
        if comp.index == -1:  # cmopgen
            comp_cword = comp.index
        else:
            comp_cword = len(comp_words) - 1  # weird invariant

        state.SetGlobalArray(self.cmd_ev.mem, 'COMP_WORDS', comp_words)
        state.SetGlobalString(self.cmd_ev.mem, 'COMP_CWORD', str(comp_cword))
        state.SetGlobalString(self.cmd_ev.mem, 'COMP_LINE', comp.line)
        state.SetGlobalString(self.cmd_ev.mem, 'COMP_POINT', str(comp.end))

        argv = [comp.first, comp.to_complete, comp.prev]
        self.log('Running completion function %r with arguments %s',
                 self.func.name, argv)

        self.comp_lookup.ClearCommandsChanged()
        status = self.cmd_ev.RunFuncForCompletion(self.func, argv)
        commands_changed = self.comp_lookup.GetCommandsChanged()

        self.log('comp.first %s, commands_changed: %s', comp.first,
                 commands_changed)

        if status == 124:
            cmd = os_path.basename(comp.first)
            if cmd in commands_changed:
                self.log('Got status 124 from %r and %s commands changed',
                         self.func.name, commands_changed)
                raise _RetryCompletion()
            else:
                # This happens with my own completion scripts.  bash doesn't show an
                # error.
                self.log(
                    "Function %r returned 124, but the completion spec for %r wasn't "
                    "changed", self.func.name, cmd)
                return []

        # Read the response.
        # NOTE: 'COMP_REPLY' would follow the naming convention!
        val = state.GetGlobal(self.cmd_ev.mem, 'COMPREPLY')
        if val.tag_() == value_e.Undef:
            # We set it above, so this error would only happen if the user unset it.
            # Not changing it means there were no completions.
            # TODO: This writes over the command line; it would be better to use an
            # error object.
            stderr_line('osh: Ran function %r but COMPREPLY was unset',
                        self.func.name)
            return []

        if val.tag_() != value_e.MaybeStrArray:
            log('ERROR: COMPREPLY should be an array, got %s', val)
            return []
        self.log('COMPREPLY %s', val)

        # Return this all at once so we don't have a generator.  COMPREPLY happens
        # all at once anyway.
        return val.strs
Пример #7
0
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.
  """
    arg, i = WAIT_SPEC.Parse(argv)
    pids = argv[i:]

    if arg.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 pids:
        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 pid in pids:
        # NOTE: osh doesn't accept 'wait %1' yet
        try:
            jid = int(pid)
        except ValueError:
            util.error('Invalid argument %r', pid)
            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
Пример #8
0
 def _SetRegexMatches(self, matches):
   """For ~= to set the BASH_REMATCH array."""
   state.SetGlobalArray(self.mem, 'BASH_REMATCH', matches)
Пример #9
0
  def testMatchesOracle(self):
    for i, case in enumerate(bash_oracle.CASES):  # generated data
      flags = case.get('_init_completion_flags')
      if flags is None:
        continue

      # This was input
      code_str = case['code']
      assert code_str.endswith('\t')

      log('')
      log('--- Case %d: %r with flags %s', i, code_str, flags)
      log('')
      #print(case)

      oracle_comp_words = case['COMP_WORDS']
      oracle_comp_cword = case['COMP_CWORD']
      oracle_comp_line = case['COMP_LINE']
      oracle_comp_point = case['COMP_POINT']

      # Init completion data
      oracle_words = case['words']
      oracle_cur = case['cur']
      oracle_prev = case['prev']
      oracle_cword = case['cword']
      oracle_split = case['split']

      #
      # First test some invariants on the oracle's data.
      #

      self.assertEqual(code_str[:-1], oracle_comp_line)
      # weird invariant that always holds.  So isn't COMP_CWORD useless?
      self.assertEqual(int(oracle_comp_cword), len(oracle_comp_words)-1)
      # Another weird invariant.  Note this is from the bash ORACLE, not from
      # our mocks.
      self.assertEqual(int(oracle_comp_point), len(code_str) - 1)

      #
      # Now run a piece of code that compares OSH's actual data against the
      # oracle.
      #

      init_code = _INIT_TEMPLATE % {
        'flags': ' '.join(flags),
        'command': oracle_comp_words[0]
      }

      arena = test_lib.MakeArena('<InitCompletionTest>')
      parse_ctx = test_lib.InitParseContext(arena=arena)
      mem = state.Mem('', [], arena, [])
      parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None)
      mem.exec_opts = exec_opts

      mutable_opts.Init()

      #
      # Allow our code to access oracle data
      #
      state.SetGlobalArray(mem, 'ORACLE_COMP_WORDS', oracle_comp_words)
      state.SetGlobalString(mem, 'ORACLE_COMP_CWORD', oracle_comp_cword)
      state.SetGlobalString(mem, 'ORACLE_COMP_LINE', oracle_comp_line)
      state.SetGlobalString(mem, 'ORACLE_COMP_POINT', oracle_comp_point)

      state.SetGlobalArray(mem, 'ORACLE_words', oracle_words)
      state.SetGlobalString(mem, 'ORACLE_cur', oracle_cur)
      state.SetGlobalString(mem, 'ORACLE_prev', oracle_prev)
      state.SetGlobalString(mem, 'ORACLE_cword', oracle_cword)
      state.SetGlobalString(mem, 'ORACLE_split', oracle_split)

      comp_lookup = completion.Lookup()
      cmd_ev = test_lib.EvalCode(init_code, parse_ctx, comp_lookup=comp_lookup,
                             mem=mem)

      r = _MakeRootCompleter(comp_lookup=comp_lookup)
      comp = MockApi(code_str[:-1])
      m = list(r.Matches(comp))
      log('matches = %s', m)

      # Unterminated quote in case 5.  Nothing to complete.
      # TODO: use a label
      if i == 5:
        continue

      # Our test shell script records what passed in an array.
      val = mem.GetValue('PASSED')
      self.assertEqual(value_e.MaybeStrArray, val.tag,
          "[case %d] Expected array, got %s" % (i, val))
      actually_passed = val.strs

      should_pass = [
          'COMP_WORDS', 'COMP_CWORD', 'COMP_LINE', 'COMP_POINT',  # old API
          'words', 'cur', 'prev', 'cword', 'split'  # new API
      ]

      if i == 4:
        should_pass.remove('COMP_WORDS')
        should_pass.remove('COMP_CWORD')
        should_pass.remove('cword')
        should_pass.remove('words')  # double quotes aren't the same

      for t in should_pass:
        self.assert_(
            t in actually_passed, "%r was expected to pass (case %d)" % (t, i))

    log('Ran %d cases', len(bash_oracle.CASES))
Пример #10
0
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