示例#1
0
def Export(argv, mem):
    arg, i = EXPORT_SPEC.Parse(argv)
    if arg.n:
        for name in argv[i:]:
            m = match.IsValidVarName(name)
            if not m:
                raise args.UsageError('export: Invalid variable name %r' %
                                      name)

            # NOTE: bash doesn't care if it wasn't found.
            mem.ClearFlag(name, var_flags_e.Exported, scope_e.Dynamic)
    else:
        for arg in argv[i:]:
            parts = arg.split('=', 1)
            if len(parts) == 1:
                name = parts[0]
                val = None  # Creates an empty variable
            else:
                name, s = parts
                val = value.Str(s)

            m = match.IsValidVarName(name)
            if not m:
                raise args.UsageError('export: Invalid variable name %r' %
                                      name)

            #log('%s %s', name, val)
            mem.SetVar(lvalue.LhsName(name), val, (var_flags_e.Exported, ),
                       scope_e.Dynamic)

    return 0
示例#2
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()

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

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

        spec = self.spec_cache.get(spec_str)
        if spec is None:
            spec = _ParseOptSpec(spec_str)
            self.spec_cache[spec_str] = spec

        user_argv = self.mem.GetArgv() if arg_r.AtEnd() else arg_r.Rest()
        #util.log('user_argv %s', user_argv)
        status, flag_char = _GetOpts(spec, user_argv, self.my_state,
                                     self.errfmt)

        if match.IsValidVarName(var_name):
            state.SetStringDynamic(self.mem, var_name, flag_char)
        else:
            # NOTE: The builtin has PARTIALLY set state.  This happens in all shells
            # except mksh.
            raise error.Usage('got invalid variable name %r' % var_name,
                              span_id=var_spid)
        return status
示例#3
0
def _MakeAssignment(parse_ctx, assign_kw, suffix_words):
  """Create an command.Assignment node from a keyword and a list of words.

  NOTE: We don't allow dynamic assignments like:

  local $1

  This can be replaced with eval 'local $1'
  """
  # First parse flags, e.g. -r -x -a -A.  None of the flags have arguments.
  flags = []
  n = len(suffix_words)
  i = 1
  while i < n:
    w = suffix_words[i]
    ok, static_val, quoted = word.StaticEval(w)
    if not ok or quoted:
      break  # can't statically evaluate

    if static_val.startswith('-'):
      flags.append(static_val)
    else:
      break  # not a flag, rest are args
    i += 1

  # Now parse bindings or variable names
  pairs = []
  while i < n:
    w = suffix_words[i]
    # declare x[y]=1 is valid
    left_token, close_token, part_offset = word.DetectAssignment(w)
    if left_token:
      pair = _MakeAssignPair(parse_ctx, (left_token, close_token, part_offset, w))
    else:
      # In aboriginal in variables/sources: export_if_blank does export "$1".
      # We should allow that.

      # Parse this differently then?  # dynamic-export?  It sets global
      # variables.
      ok, static_val, quoted = word.StaticEval(w)
      if not ok or quoted:
        p_die("Variable names must be unquoted constants", word=w)

      # No value is equivalent to ''
      if not match.IsValidVarName(static_val):
        p_die('Invalid variable name %r', static_val, word=w)

      lhs = lhs_expr.LhsName(static_val)
      lhs.spids.append(word.LeftMostSpanForWord(w))
      pair = syntax_asdl.assign_pair(lhs, assign_op_e.Equal, None)

      left_spid = word.LeftMostSpanForWord(w)
      pair.spids.append(left_spid)
    pairs.append(pair)

    i += 1

  node = command.Assignment(assign_kw, flags, pairs)
  return node
示例#4
0
  def _UnsetVar(self, name, spid):
    if not match.IsValidVarName(name):
      raise args.UsageError(
          'got invalid variable name %r' % name, span_id=spid)

    ok, found = self.mem.Unset(lvalue.Named(name), scope_e.Dynamic)
    if not ok:
      self.errfmt.Print("Can't unset readonly variable %r", name,
                        span_id=spid)
    return ok, found
示例#5
0
  def _ParseForEachLoop(self):
    node = command.ForEach()
    node.do_arg_iter = False

    ok, iter_name, quoted = word.StaticEval(self.cur_word)
    if not ok or quoted:
      p_die("Loop variable name should be a constant", word=self.cur_word)
    if not match.IsValidVarName(iter_name):
      p_die("Invalid loop variable name", word=self.cur_word)
    node.iter_name = iter_name
    self._Next()  # skip past name

    self._NewlineOk()

    in_spid = const.NO_INTEGER
    semi_spid = const.NO_INTEGER

    self._Peek()
    if self.c_id == Id.KW_In:
      self._Next()  # skip in

      in_spid = word.LeftMostSpanForWord(self.cur_word) + 1
      iter_words, semi_spid = self.ParseForWords()
      assert iter_words is not None

      words2 = braces.BraceDetectAll(iter_words)
      words3 = word.TildeDetectAll(words2)
      node.iter_words = words3

    elif self.c_id == Id.Op_Semi:
      node.do_arg_iter = True  # implicit for loop
      self._Next()

    elif self.c_id == Id.KW_Do:
      node.do_arg_iter = True  # implicit for loop
      # do not advance

    else:  # for foo BAD
      p_die('Unexpected word after for loop variable', word=self.cur_word)

    node.spids.extend((in_spid, semi_spid))

    body_node = self.ParseDoGroup()
    assert body_node is not None

    node.body = body_node
    return node
示例#6
0
    def __call__(self, arg_vec):
        status = 0
        for i in xrange(1, len(arg_vec.strs)):
            name = arg_vec.strs[i]
            if not match.IsValidVarName(name):
                raise args.UsageError('got invalid variable name %r' % name,
                                      span_id=arg_vec.spids[i])

            cell = self.mem.GetCell(name)
            if cell is None:
                print('%r is not defined' % name)
                status = 1
            else:
                sys.stdout.write('%s = ' % name)
                cell.PrettyPrint()  # may be color
                sys.stdout.write('\n')
        return status
示例#7
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()

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

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

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

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

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

        # Bug fix: bash-completion uses a *local* OPTIND !  Not global.
        state.SetStringDynamic(self.mem, 'OPTARG', optarg)
        state.SetStringDynamic(self.mem, 'OPTIND', str(optind))
        if match.IsValidVarName(var_name):
            state.SetStringDynamic(self.mem, var_name, opt_char)
        else:
            # NOTE: The builtin has PARTIALLY filed.  This happens in all shells
            # except mksh.
            raise error.Usage('got invalid variable name %r' % var_name,
                              span_id=var_spid)
        return status
示例#8
0
def Repr(argv, mem):
    """Given a list of variable names, print their values.

  'repr a' is a lot easier to type than 'argv.py "${a[@]}"'.
  """
    status = 0
    for name in argv:
        if not match.IsValidVarName(name):
            util.error('%r is not a valid variable name', name)
            return 1

        # TODO: Should we print flags too?
        val = mem.GetVar(name)
        if val.tag == value_e.Undef:
            print('%r is not defined' % name)
            status = 1
        else:
            print('%s = %s' % (name, val))
    return status
示例#9
0
    def Run(self, cmd_val):
        arg, arg_r = flag_spec.ParseOilCmdVal('push', cmd_val)

        var_name, var_spid = arg_r.ReadRequired2('requires a variable name')

        if var_name.startswith(':'):  # optional : sigil
            var_name = var_name[1:]

        if not match.IsValidVarName(var_name):
            raise error.Usage('got invalid variable name %r' % var_name,
                              span_id=var_spid)

        val = self.mem.GetValue(var_name)
        # TODO: value.Obj too
        if val.tag != value_e.MaybeStrArray:
            self.errfmt.Print("%r isn't an array", var_name, span_id=var_spid)
            return 1

        val.strs.extend(arg_r.Rest())
        return 0
示例#10
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()

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

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

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

        # TODO: OPTIND could be value.Int?
        try:
            optind = state.GetInteger(self.mem, 'OPTIND')
        except error.Runtime as e:
            self.errfmt.Print_(e.UserErrorString())
            return 1

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

        # Bug fix: bash-completion uses a *local* OPTIND !  Not global.
        state.SetStringDynamic(self.mem, 'OPTARG', optarg)
        state.SetStringDynamic(self.mem, 'OPTIND', str(optind))
        if match.IsValidVarName(var_name):
            state.SetStringDynamic(self.mem, var_name, opt_char)
        else:
            # NOTE: The builtin has PARTIALLY filed.  This happens in all shells
            # except mksh.
            raise error.Usage('got invalid variable name %r' % var_name,
                              span_id=var_spid)
        return status
示例#11
0
    def __call__(self, cmd_val):
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()  # skip 'push'

        var_name, var_spid = arg_r.ReadRequired2('requires a variable name')

        if var_name.startswith(':'):  # optional : sigil
            var_name = var_name[1:]

        if not match.IsValidVarName(var_name):
            raise args.UsageError('got invalid variable name %r' % var_name,
                                  span_id=var_spid)

        val = self.mem.GetVar(var_name)
        # TODO: value.Obj too
        if val.tag != value_e.MaybeStrArray:
            self.errfmt.Print("%r isn't an array", var_name, span_id=var_spid)
            return 1

        val.strs.extend(arg_r.Rest())
        return 0
示例#12
0
    def Run(self, cmd_val):
        status = 0
        for i in xrange(1, len(cmd_val.argv)):
            name = cmd_val.argv[i]
            if name.startswith(':'):
                name = name[1:]

            if not match.IsValidVarName(name):
                raise error.Usage('got invalid variable name %r' % name,
                                  span_id=cmd_val.arg_spids[i])

            cell = self.mem.GetCell(name)
            if cell is None:
                self.errfmt.Print("Couldn't find a variable named %r" % name,
                                  span_id=cmd_val.arg_spids[i])
                status = 1
            else:
                sys.stdout.write('%s = ' % name)
                cell.PrettyPrint()  # may be color
                sys.stdout.write('\n')
        return status
示例#13
0
  def _EvalIndirectArrayExpansion(self, name, index):
    """Expands ${!ref} when $ref has the form `name[index]`.

    Args:
      name, index: arbitrary strings
    Returns:
      value, or None if invalid
    """
    if not match.IsValidVarName(name):
      return None
    val = self.mem.GetVar(name)
    if val.tag == value_e.StrArray:
      if index in ('@', '*'):
        # TODO: maybe_decay_array
        return value.StrArray(val.strs)
      try:
        index_num = int(index)
      except ValueError:
        return None
      try:
        return value.Str(val.strs[index_num])
      except IndexError:
        return value.Undef()
    elif val.tag == value_e.AssocArray:
      if index in ('@', '*'):
        raise NotImplementedError
      try:
        return value.Str(val.d[index])
      except KeyError:
        return value.Undef()
    elif val.tag == value_e.Undef:
      return value.Undef()
    elif val.tag == value_e.Str:
      return None
    else:
      raise AssertionError
示例#14
0
    def Run(self, cmd_val):
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()  # skip 'json'

        action, action_spid = arg_r.Peek2()
        if action is None:
            raise error.Usage(_JSON_ACTION_ERROR)
        arg_r.Next()

        if action == 'write':
            arg, _ = JSON_WRITE_SPEC.Parse(arg_r)

            # GetVar() of each name and print it.

            for var_name in arg_r.Rest():
                if var_name.startswith(':'):
                    var_name = var_name[1:]

                val = self.mem.GetVar(var_name)
                with tagswitch(val) as case:
                    if case(value_e.Undef):
                        # TODO: blame the right span_id
                        self.errfmt.Print("no variable named %r is defined",
                                          var_name)
                        return 1
                    elif case(value_e.Str):
                        obj = val.s
                    elif case(value_e.MaybeStrArray):
                        obj = val.strs
                    elif case(value_e.AssocArray):
                        obj = val.d
                    elif case(value_e.Obj):
                        obj = val.obj
                    else:
                        raise AssertionError(val)

                if arg.pretty:
                    indent = arg.indent
                    extra_newline = False
                else:
                    # How yajl works: if indent is -1, then everything is on one line.
                    indent = -1
                    extra_newline = True

                j = yajl.dump(obj, sys.stdout, indent=indent)
                if extra_newline:
                    sys.stdout.write('\n')

            # TODO: Accept a block.  They aren't hooked up yet.
            if cmd_val.block:
                # TODO: flatten value.{Str,Obj} into a flat dict?
                namespace = self.cmd_ev.EvalBlock(cmd_val.block)

                print(yajl.dump(namespace))

        elif action == 'read':
            arg, _ = JSON_READ_SPEC.Parse(arg_r)
            # TODO:
            # Respect -validate=F

            var_name, name_spid = arg_r.ReadRequired2("expected variable name")
            if var_name.startswith(':'):
                var_name = var_name[1:]

            if not match.IsValidVarName(var_name):
                raise error.Usage('got invalid variable name %r' % var_name,
                                  span_id=name_spid)

            try:
                # Use a global _STDIN, because we get EBADF on a redirect if we use a
                # local.  A Py_DECREF closes the file, which we don't want, because the
                # redirect is responsible for freeing it.
                #
                # https://github.com/oilshell/oil/issues/675
                #
                # TODO: write a better binding like yajl.readfd()
                #
                # It should use streaming like here:
                # https://lloyd.github.io/yajl/

                obj = yajl.load(_STDIN)
            except ValueError as e:
                self.errfmt.Print('json read: %s', e, span_id=action_spid)
                return 1

            self.mem.SetVar(sh_lhs_expr.Name(var_name), value.Obj(obj),
                            scope_e.LocalOnly)

        else:
            raise error.Usage(_JSON_ACTION_ERROR, span_id=action_spid)

        return 0
示例#15
0
  def _ApplyPrefixOp(self, val, op_id, token):
    """
    Returns:
      value
    """
    assert val.tag != value_e.Undef

    if op_id == Id.VSub_Pound:  # LENGTH
      if val.tag == value_e.Str:
        # NOTE: Whether bash counts bytes or chars is affected by LANG
        # environment variables.
        # Should we respect that, or another way to select?  set -o
        # count-bytes?

        # https://stackoverflow.com/questions/17368067/length-of-string-in-bash
        try:
          length = string_ops.CountUtf8Chars(val.s)
        except util.InvalidUtf8 as e:
          # TODO: Add location info from 'part'?  Only the caller has it.
          if self.exec_opts.strict_word_eval:
            raise
          else:
            # NOTE: Doesn't make the command exit with 1; it just returns a
            # length of -1.
            util.warn(e.UserErrorString())
            return value.Str('-1')

      elif val.tag == value_e.StrArray:
        # There can be empty placeholder values in the array.
        length = sum(1 for s in val.strs if s is not None)

      return value.Str(str(length))

    elif op_id == Id.VSub_Bang:  # ${!foo}, "indirect expansion"
      # NOTES:
      # - Could translate to eval('$' + name) or eval("\$$name")
      # - ${!array[@]} means something completely different.  TODO: implement
      #   that.
      # - It might make sense to suggest implementing this with associative
      #   arrays?

      if val.tag == value_e.Str:
        # plain variable name, like 'foo'
        if match.IsValidVarName(val.s):
          return self.mem.GetVar(val.s)

        # positional argument, like '1'
        try:
          return self.mem.GetArgNum(int(val.s))
        except ValueError:
          pass

        if val.s in ('@', '*'):
          # TODO maybe_decay_array
          return value.StrArray(self.mem.GetArgv())

        # otherwise an array reference, like 'arr[0]' or 'arr[xyz]' or 'arr[@]'
        i = val.s.find('[')
        if i >= 0 and val.s[-1] == ']':
          name, index = val.s[:i], val.s[i+1:-1]
          result = self._EvalIndirectArrayExpansion(name, index)
          if result is not None:
            return result

        # Note that bash doesn't consider this fatal.  It makes the
        # command exit with '1', but we don't have that ability yet?
        e_die('Bad indirect expansion: %r', val.s, token=token)

      elif val.tag == value_e.StrArray:
        indices = [str(i) for i, s in enumerate(val.strs) if s is not None]
        return value.StrArray(indices)
      else:
        raise AssertionError

    else:
      raise AssertionError(op_id)
示例#16
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)
            arena.PushSource(source.ArgvWord(fmt_spid))
            try:
                parts = parser.Parse()
            except error.Parse as e:
                self.errfmt.PrettyPrintError(e)
                return 2  # parse error
            finally:
                arena.PopSource()

            self.parse_cache[fmt] = parts

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

        out = []  # 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_, value = 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, value)
                            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:
            var_name = arg.v

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

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

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

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

            self.parse_cache[fmt] = parts

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

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

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

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

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

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

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

                    out.append(s)
                    arg_index += 1

                else:
                    raise AssertionError

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

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

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

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

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

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

            self.parse_cache[fmt] = parts

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

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

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

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

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

                                return 1

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

                    else:
                        raise AssertionError

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

                    out.append(s)
                    arg_index += 1

                else:
                    raise AssertionError

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

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

            # Notes:
            # - bash allows a[i] here (as in unset and ${!x}), but we haven't
            # implemented it.
            # - TODO: get the span_id for arg.v!
            if not match.IsValidVarName(var_name):
                raise args.UsageError('got invalid variable name %r' %
                                      var_name)
            state.SetStringDynamic(self.mem, var_name, result)
        else:
            sys.stdout.write(result)
        return 0
示例#19
0
  def _StringToInteger(self, s, span_id=runtime.NO_SPID):
    # type: (str, int) -> int
    """Use bash-like rules to coerce a string to an integer.

    Runtime parsing enables silly stuff like $(( $(echo 1)$(echo 2) + 1 )) => 13

    0xAB -- hex constant
    042  -- octal constant
    42   -- decimal constant
    64#z -- arbitary base constant

    bare word: variable
    quoted word: string (not done?)
    """
    if s.startswith('0x'):
      try:
        integer = int(s, 16)
      except ValueError:
        e_strict('Invalid hex constant %r', s, span_id=span_id)
      return integer

    if s.startswith('0'):
      try:
        integer = int(s, 8)
      except ValueError:
        e_strict('Invalid octal constant %r', s, span_id=span_id)
      return integer

    if '#' in s:
      b, digits = mylib.split_once(s, '#')
      try:
        base = int(b)
      except ValueError:
        e_strict('Invalid base for numeric constant %r',  b, span_id=span_id)

      integer = 0
      for ch in digits:
        if IsLower(ch):
          digit = ord(ch) - ord('a') + 10
        elif IsUpper(ch):
          digit = ord(ch) - ord('A') + 36
        elif ch == '@':  # horrible syntax
          digit = 62
        elif ch == '_':
          digit = 63
        elif ch.isdigit():
          digit = int(ch)
        else:
          e_strict('Invalid digits for numeric constant %r', digits, span_id=span_id)

        if digit >= base:
          e_strict('Digits %r out of range for base %d', digits, base, span_id=span_id)

        integer = integer * base + digit
      return integer

    try:
      # Normal base 10 integer.  This includes negative numbers like '-42'.
      integer = int(s)
    except ValueError:
      # doesn't look like an integer

      # note: 'test' and '[' never evaluate recursively
      if self.exec_opts.eval_unsafe_arith() and self.parse_ctx:
        # Special case so we don't get EOF error
        if len(s.strip()) == 0:
          return 0

        # For compatibility: Try to parse it as an expression and evaluate it.

        arena = self.parse_ctx.arena

        a_parser = self.parse_ctx.MakeArithParser(s)
        with alloc.ctx_Location(arena, source.Variable(span_id)):
          try:
            node2 = a_parser.Parse()  # may raise error.Parse
          except error.Parse as e:
            ui.PrettyPrintError(e, arena)
            e_die('Parse error in recursive arithmetic', span_id=e.span_id)

        # Prevent infinite recursion of $(( 1x )) -- it's a word that evaluates
        # to itself, and you don't want to reparse it as a word.
        if node2.tag_() == arith_expr_e.Word:
          e_die("Invalid integer constant %r", s, span_id=span_id)
        else:
          integer = self.EvalToInt(node2)
      else:
        if len(s.strip()) == 0 or match.IsValidVarName(s):
          # x42 could evaluate to 0
          e_strict("Invalid integer constant %r", s, span_id=span_id)
        else:
          # 42x is always fatal!
          e_die("Invalid integer constant %r", s, span_id=span_id)

    return integer
示例#20
0
    def __call__(self, cmd_val):
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()  # skip 'json'

        action, action_spid = arg_r.Peek2()
        if action is None:
            raise args.UsageError(_JSON_ACTION_ERROR)
        arg_r.Next()

        if action == 'write':
            arg, _ = JSON_WRITE_SPEC.Parse(arg_r)

            # GetVar() of each name and print it.

            for var_name in arg_r.Rest():
                if var_name.startswith(':'):
                    var_name = var_name[1:]

                val = self.mem.GetVar(var_name)
                with tagswitch(val) as case:
                    if case(value_e.Undef):
                        # TODO: blame the right span_id
                        self.errfmt.Print("no variable named %r is defined",
                                          var_name)
                        return 1
                    elif case(value_e.Str):
                        obj = val.s
                    elif case(value_e.MaybeStrArray):
                        obj = val.strs
                    elif case(value_e.AssocArray):
                        obj = val.d
                    elif case(value_e.Obj):
                        obj = val.obj
                    else:
                        raise AssertionError(val)

                if arg.pretty:
                    indent = arg.indent
                    extra_newline = False
                else:
                    # How yajl works: if indent is -1, then everything is on one line.
                    indent = -1
                    extra_newline = True

                j = yajl.dump(obj, sys.stdout, indent=indent)
                if extra_newline:
                    sys.stdout.write('\n')

            # TODO: Accept a block.  They aren't hooked up yet.
            if cmd_val.block:
                # TODO: flatten value.{Str,Obj} into a flat dict?
                namespace = self.ex.EvalBlock(cmd_val.block)

                print(yajl.dump(namespace))

        elif action == 'read':
            arg, _ = JSON_READ_SPEC.Parse(arg_r)
            # TODO:
            # Respect -validate=F

            var_name, name_spid = arg_r.ReadRequired2("expected variable name")
            if var_name.startswith(':'):
                var_name = var_name[1:]

            if not match.IsValidVarName(var_name):
                raise args.UsageError('got invalid variable name %r' %
                                      var_name,
                                      span_id=name_spid)

            # Have to use this over sys.stdin because of redirects
            # TODO: change binding to yajl.readfd() ?
            stdin = posix_.fdopen(0)
            try:
                obj = yajl.load(stdin)
            except ValueError as e:
                self.errfmt.Print('json read: %s', e, span_id=action_spid)
                return 1

            self.mem.SetVar(sh_lhs_expr.Name(var_name), value.Obj(obj), (),
                            scope_e.LocalOnly)

        else:
            raise args.UsageError(_JSON_ACTION_ERROR, span_id=action_spid)

        return 0
示例#21
0
  def Run(self, cmd_val):
    arg, arg_r = flag_spec.ParseOilCmdVal('repr', cmd_val)

    action, action_spid = arg_r.ReadRequired2(
        'expected an action (proc, .cell, etc.)')

    # Actions that print unstable formats start with '.'
    if action == '.cell':
      argv, spids = arg_r.Rest2()

      status = 0
      for i, name in enumerate(argv):
        if name.startswith(':'):
          name = name[1:]

        if not match.IsValidVarName(name):
          raise error.Usage('got invalid variable name %r' % name,
                            span_id=spids[i])

        cell = self.mem.GetCell(name)
        if cell is None:
          self.errfmt.Print("Couldn't find a variable named %r" % name,
                            span_id=spids[i])
          status = 1
        else:
          sys.stdout.write('%s = ' % name)
          cell.PrettyPrint()  # may be color
          sys.stdout.write('\n')

    elif action == 'proc':
      names, spids = arg_r.Rest2()
      if len(names):
        for i, name in enumerate(names):
          node = self.procs.get(name)
          if node is None:
            self.errfmt.Print_('Invalid proc %r' % name, span_id=spids[i])
            return 1
      else:
        names = sorted(self.procs)

      # QTSV header
      print('proc_name\tdoc_comment')
      for name in names:
        node = self.procs[name]  # must exist
        body = node.body

        # TODO: not just command__ShFunction, but command__Proc!
        doc = ''
        if body.tag_() == command_e.BraceGroup:
          if body.doc_token:
            span_id = body.doc_token.span_id
            span = self.arena.GetLineSpan(span_id)
            line = self.arena.GetLine(span.line_id)
            # 1 to remove leading space
            doc = line[span.col+1 : span.col + span.length]

        # No limits on proc names
        print('%s\t%s' % (qsn.maybe_encode(name), qsn.maybe_encode(doc)))

      status = 0

    else:
      e_usage('got invalid action %r' % action, span_id=action_spid)

    return status