예제 #1
0
파일: completion.py 프로젝트: gnprice/oil
    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
예제 #2
0
파일: builtin.py 프로젝트: gnprice/oil
def Cd(argv, mem, dir_stack):
    arg, i = CD_SPEC.Parse(argv)
    # TODO: error checking, etc.
    # TODO: ensure that if multiple flags are provided, the *last* one overrides
    # the others.

    try:
        dest_dir = argv[i]
    except IndexError:
        val = mem.GetVar('HOME')
        if val.tag == value_e.Undef:
            util.error("$HOME isn't defined")
            return 1
        elif val.tag == value_e.Str:
            dest_dir = val.s
        elif val.tag == value_e.StrArray:
            util.error("$HOME shouldn't be an array.")
            return 1

    if dest_dir == '-':
        old = mem.GetVar('OLDPWD', scope_e.GlobalOnly)
        if old.tag == value_e.Undef:
            log('OLDPWD not set')
            return 1
        elif old.tag == value_e.Str:
            dest_dir = old.s
            print(dest_dir)  # Shells print the directory
        elif old.tag == value_e.StrArray:
            # TODO: Prevent the user from setting OLDPWD to array (or maybe they
            # can't even set it at all.)
            raise AssertionError('Invalid OLDPWD')

    pwd = mem.GetVar('PWD')
    assert pwd.tag == value_e.Str, pwd  # TODO: Need a general scheme to avoid

    # 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.s, 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:
        # TODO: Add line number, etc.
        util.error("cd %r: %s", real_dest_dir, posix.strerror(e.errno))
        return 1

    state.SetGlobalString(mem, 'OLDPWD', pwd.s)
    state.SetGlobalString(mem, 'PWD', real_dest_dir)
    dir_stack.Reset()  # for pushd/popd/dirs
    return 0
예제 #3
0
파일: builtin.py 프로젝트: nicolashahn/oil
def Cd(argv, mem, dir_stack):
  arg, i = CD_SPEC.Parse(argv)
  # TODO: error checking, etc.

  try:
    dest_dir = argv[i]
  except IndexError:
    val = mem.GetVar('HOME')
    if val.tag == value_e.Undef:
      util.error("$HOME isn't defined")
      return 1
    elif val.tag == value_e.Str:
      dest_dir = val.s
    elif val.tag == value_e.StrArray:
      util.error("$HOME shouldn't be an array.")
      return 1

  if dest_dir == '-':
    old = mem.GetVar('OLDPWD', scope_e.GlobalOnly)
    if old.tag == value_e.Undef:
      log('OLDPWD not set')
      return 1
    elif old.tag == value_e.Str:
      dest_dir = old.s
      print(dest_dir)  # Shells print the directory
    elif old.tag == value_e.StrArray:
      # TODO: Prevent the user from setting OLDPWD to array (or maybe they
      # can't even set it at all.)
      raise AssertionError('Invalid OLDPWD')

  # NOTE: We can't call os.getcwd() because it can raise OSError if the
  # directory was removed (ENOENT.)

  pwd = mem.GetVar('PWD')
  assert pwd.tag == value_e.Str, pwd  # TODO: Need a general scheme to avoid
  state.SetGlobalString(mem, 'OLDPWD', pwd.s)
  try:
    os.chdir(dest_dir)
  except OSError as e:
    # TODO: Add line number, etc.
    util.error("cd %r: %s", dest_dir, os.strerror(e.errno))
    return 1

  # Set $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
  pwd = os.path.realpath(dest_dir) if arg.P else dest_dir
  state.SetGlobalString(mem, 'PWD', pwd)

  dir_stack.Reset()  # for pushd/popd/dirs

  return 0
예제 #4
0
def Cd(argv, mem, dir_stack):
    # TODO: Parse flags, error checking, etc.
    try:
        dest_dir = argv[0]
    except IndexError:
        val = mem.GetVar('HOME')
        if val.tag == value_e.Undef:
            util.error("$HOME isn't defined")
            return 1
        elif val.tag == value_e.Str:
            dest_dir = val.s
        elif val.tag == value_e.StrArray:
            util.error("$HOME shouldn't be an array.")
            return 1

    if dest_dir == '-':
        old = mem.GetVar('OLDPWD', scope_e.GlobalOnly)
        if old.tag == value_e.Undef:
            log('OLDPWD not set')
            return 1
        elif old.tag == value_e.Str:
            dest_dir = old.s
            print(dest_dir)  # Shells print the directory
        elif old.tag == value_e.StrArray:
            # TODO: Prevent the user from setting OLDPWD to array (or maybe they
            # can't even set it at all.)
            raise AssertionError('Invalid OLDPWD')

    # NOTE: We can't call os.getcwd() because it can raise OSError if the
    # directory was removed (ENOENT.)

    pwd = mem.GetVar('PWD')
    assert pwd.tag == value_e.Str, pwd  # TODO: Need a general scheme to avoid
    state.SetGlobalString(mem, 'OLDPWD', pwd.s)
    try:
        os.chdir(dest_dir)
    except OSError as e:
        # TODO: Add line number, etc.
        util.error("cd %r: %s", dest_dir, os.strerror(e.errno))
        return 1

    state.SetGlobalString(mem, 'PWD', dest_dir)  # Set $PWD.
    dir_stack.Reset()  # for pushd/popd/dirs

    return 0
예제 #5
0
파일: builtin.py 프로젝트: lheckemann/oil
def Cd(argv, mem):
    # TODO: Parse flags, error checking, etc.
    try:
        dest_dir = argv[0]
    except IndexError:
        val = mem.GetVar('HOME')
        if val.tag == value_e.Undef:
            util.error("$HOME isn't defined")
            return 1
        elif val.tag == value_e.Str:
            dest_dir = val.s
        elif val.tag == value_e.StrArray:
            util.error("$HOME shouldn't be an array.")
            return 1

    if dest_dir == '-':
        old = mem.GetVar('OLDPWD', scope.GlobalOnly)
        if old.tag == value_e.Undef:
            log('OLDPWD not set')
            return 1
        elif old.tag == value_e.Str:
            dest_dir = old.s
            print(dest_dir)  # Shells print the directory
        elif old.tag == value_e.StrArray:
            # TODO: Prevent the user from setting OLDPWD to array (or maybe they
            # can't even set it at all.)
            raise AssertionError('Invalid OLDPWD')

    # Save OLDPWD.
    state.SetGlobalString(mem, 'OLDPWD', os.getcwd())
    try:
        os.chdir(dest_dir)
    except OSError as e:
        # TODO: Add line number, etc.
        util.error("cd %r: %s", dest_dir, os.strerror(e.errno))
        return 1
    state.SetGlobalString(mem, 'PWD', dest_dir)
    return 0
예제 #6
0
def GetOpts(argv, mem):
    """
  Vars to set:
    OPTIND - initialized to 1 at startup
    OPTARG - argument
  Vars used:
    OPTERR: disable printing of error messages
  """
    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)

    status, opt_char, optarg, optind = _GetOpts(spec, mem, optind)

    state.SetGlobalString(mem, var_name, opt_char)
    state.SetGlobalString(mem, 'OPTARG', optarg)
    state.SetGlobalString(mem, 'OPTIND', str(optind))
    return status
예제 #7
0
파일: builtin_misc.py 프로젝트: ilyash/oil
def _PopDirStack(mem, dir_stack, errfmt):
    # type: (Mem, DirStack, ErrorFormatter) -> bool
    """Helper for popd and cd { ... }."""
    dest_dir = dir_stack.Pop()
    if dest_dir is None:
        errfmt.Print_('popd: directory stack is empty')
        return False

    err_num = pyos.Chdir(dest_dir)
    if err_num != 0:
        # Happens if a directory is deleted in pushing and popping
        errfmt.Print_("popd: %r: %s" % (dest_dir, posix.strerror(err_num)))
        return False

    state.SetGlobalString(mem, 'PWD', dest_dir)
    mem.SetPwd(dest_dir)
    return True
예제 #8
0
파일: completion.py 프로젝트: xydinesh/oil
  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
예제 #9
0
def _PopDirStack(mem, dir_stack, errfmt):
    # type: (Mem, DirStack, ErrorFormatter) -> bool
    """Helper for popd and cd { ... }."""
    dest_dir = dir_stack.Pop()
    if dest_dir is None:
        errfmt.Print('popd: directory stack is empty')
        return False

    try:
        posix.chdir(dest_dir)
    except OSError as e:
        # Happens if a directory is deleted in pushing and popping
        errfmt.Print("popd: %r: %s", dest_dir, posix.strerror(e.errno))
        return False

    state.SetGlobalString(mem, 'PWD', dest_dir)
    mem.SetPwd(dest_dir)
    return True
예제 #10
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
예제 #11
0
def main(argv):
  # type: (List[str]) -> int
  arena = alloc.Arena()

  dollar0 = argv[0]
  debug_stack = []  # type: List[state.DebugFrame]
  mem = state.Mem(dollar0, argv, arena, debug_stack)
  opt_hook = state.OptHook()
  parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, opt_hook)
  # Dummy value; not respecting aliases!
  aliases = {}  # type: Dict[str, str]
  # parse `` and a[x+1]=bar differently

  state.SetGlobalString(mem, 'SHELLOPTS', '')

  oil_grammar = None  # type: Grammar
  if mylib.PYTHON:
    loader = pyutil.GetResourceLoader()
    oil_grammar = meta.LoadOilGrammar(loader)

  parse_ctx = parse_lib.ParseContext(arena, parse_opts, aliases, oil_grammar)

  argv = argv[1:]  # remove binary name
  i, flag_a, flag_c, flag_n = Parse(argv)

  argv = argv[i:]  # truncate

  if flag_c:
    # This path is easier to run through GDB
    line_reader = reader.StringLineReader(flag_c, arena)
    src = source.CFlag()  # type: source_t

  elif len(argv) == 0:
    line_reader = reader.FileLineReader(mylib.Stdin(), arena)
    src = source.Stdin('')

  elif len(argv) == 1:
    path = argv[0]
    f = mylib.open(path)
    line_reader = reader.FileLineReader(f, arena)
    src = source.MainFile(path)

  else:
    raise AssertionError(argv)

  arena.PushSource(src)
  c_parser = parse_ctx.MakeOshParser(line_reader)

  # C++ doesn't have the abbreviations yet (though there are some differences
  # like omitting spids)
  #tree = node.AbbreviatedTree()
  if flag_n:
    try:
      node = main_loop.ParseWholeFile(c_parser)
    except error.Parse as e:
      ui.PrettyPrintError(e, arena)
      return 2
    assert node is not None

    if flag_a:
      tree = node.PrettyTree()

      ast_f = fmt.DetectConsoleOutput(mylib.Stdout())
      fmt.PrintTree(tree, ast_f)
      ast_f.write('\n')
    return 0

  # New osh_eval.py instantiations

  errfmt = ui.ErrorFormatter(arena)

  splitter = split.SplitContext(mem)
  arith_ev = sh_expr_eval.ArithEvaluator(mem, exec_opts, parse_ctx, errfmt)
  bool_ev = sh_expr_eval.BoolEvaluator(mem, exec_opts, parse_ctx, errfmt)
  word_ev = word_eval.NormalWordEvaluator(mem, exec_opts, splitter, errfmt)

  arith_ev.word_ev = word_ev
  word_ev.arith_ev = arith_ev

  procs = {}  # type: Dict[str, command__ShFunction]

  assign_builtins = {}  # type: Dict[int, _AssignBuiltin]

  new_var = builtin_assign.NewVar(mem, procs, errfmt)
  assign_builtins[builtin_i.declare] = new_var
  assign_builtins[builtin_i.typeset] = new_var
  assign_builtins[builtin_i.local] = new_var

  #assign_builtins = {
  #    # ShAssignment (which are pure)
  #    builtin_i.declare: new_var,
  #    builtin_i.typeset: new_var,
  #    builtin_i.local: new_var,

  #    builtin_i.export_: builtin_assign.Export(mem, errfmt),
  #    builtin_i.readonly: builtin_assign.Readonly(mem, errfmt),
  #}

  cmd_deps = cmd_eval.Deps()
  cmd_deps.mutable_opts = mutable_opts
  cmd_deps.traps = {}
  cmd_deps.trap_nodes = []  # TODO: Clear on fork() to avoid duplicates

  cmd_deps.dumper = dev.CrashDumper('')

  builtins = {}  # type: Dict[int, _Builtin]
  builtins[builtin_i.echo] = Echo()
  builtins[builtin_i.shopt] = Shopt(mutable_opts)
  builtins[builtin_i.set] = Set(mutable_opts)
  ex = NullExecutor(builtins)

  trace_f = util.DebugFile(mylib.Stderr())
  tracer = dev.Tracer(parse_ctx, exec_opts, mutable_opts, mem, word_ev, trace_f)

  cmd_ev = cmd_eval.CommandEvaluator(mem, exec_opts, errfmt, procs,
                                     assign_builtins, arena, cmd_deps)

  # vm.InitCircularDeps
  cmd_ev.arith_ev = arith_ev
  cmd_ev.bool_ev = bool_ev
  cmd_ev.word_ev = word_ev
  cmd_ev.tracer = tracer
  cmd_ev.shell_ex = ex

  bool_ev.word_ev = word_ev

  status = main_loop.Batch(cmd_ev, c_parser, arena, is_main=True)
  return status
예제 #12
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))