| 1 | #!/usr/bin/env python2
 | 
| 2 | """
 | 
| 3 | pure_osh.py - Builtins that don't do any I/O.
 | 
| 4 | 
 | 
| 5 | If the OSH interpreter were embedded in another program, these builtins can be
 | 
| 6 | safely used, e.g. without worrying about modifying the file system.
 | 
| 7 | 
 | 
| 8 | NOTE: There can be spew on stdout, e.g. for shopt -p and so forth.
 | 
| 9 | """
 | 
| 10 | from __future__ import print_function
 | 
| 11 | 
 | 
| 12 | from _devbuild.gen import arg_types
 | 
| 13 | from _devbuild.gen.syntax_asdl import loc
 | 
| 14 | from _devbuild.gen.types_asdl import opt_group_i
 | 
| 15 | 
 | 
| 16 | from core import error
 | 
| 17 | from core.error import e_usage
 | 
| 18 | from core import state
 | 
| 19 | from core import ui
 | 
| 20 | from core import vm
 | 
| 21 | from data_lang import j8_lite
 | 
| 22 | from frontend import args
 | 
| 23 | from frontend import consts
 | 
| 24 | from frontend import flag_util
 | 
| 25 | from frontend import match
 | 
| 26 | from frontend import typed_args
 | 
| 27 | from mycpp import mylib
 | 
| 28 | from mycpp.mylib import print_stderr, log
 | 
| 29 | 
 | 
| 30 | from typing import List, Dict, Tuple, Optional, TYPE_CHECKING
 | 
| 31 | if TYPE_CHECKING:
 | 
| 32 |     from _devbuild.gen.runtime_asdl import cmd_value
 | 
| 33 |     from core.state import MutableOpts, Mem, SearchPath
 | 
| 34 |     from osh.cmd_eval import CommandEvaluator
 | 
| 35 | 
 | 
| 36 | _ = log
 | 
| 37 | 
 | 
| 38 | 
 | 
| 39 | class Boolean(vm._Builtin):
 | 
| 40 |     """For :, true, false."""
 | 
| 41 | 
 | 
| 42 |     def __init__(self, status):
 | 
| 43 |         # type: (int) -> None
 | 
| 44 |         self.status = status
 | 
| 45 | 
 | 
| 46 |     def Run(self, cmd_val):
 | 
| 47 |         # type: (cmd_value.Argv) -> int
 | 
| 48 | 
 | 
| 49 |         # These ignore regular args, but shouldn't accept typed args.
 | 
| 50 |         typed_args.DoesNotAccept(cmd_val.typed_args)
 | 
| 51 |         return self.status
 | 
| 52 | 
 | 
| 53 | 
 | 
| 54 | class Alias(vm._Builtin):
 | 
| 55 | 
 | 
| 56 |     def __init__(self, aliases, errfmt):
 | 
| 57 |         # type: (Dict[str, str], ui.ErrorFormatter) -> None
 | 
| 58 |         self.aliases = aliases
 | 
| 59 |         self.errfmt = errfmt
 | 
| 60 | 
 | 
| 61 |     def Run(self, cmd_val):
 | 
| 62 |         # type: (cmd_value.Argv) -> int
 | 
| 63 |         _, arg_r = flag_util.ParseCmdVal('alias', cmd_val)
 | 
| 64 |         argv = arg_r.Rest()
 | 
| 65 | 
 | 
| 66 |         if len(argv) == 0:
 | 
| 67 |             for name in sorted(self.aliases):
 | 
| 68 |                 alias_exp = self.aliases[name]
 | 
| 69 |                 # This is somewhat like bash, except we use %r for ''.
 | 
| 70 |                 print('alias %s=%r' % (name, alias_exp))
 | 
| 71 |             return 0
 | 
| 72 | 
 | 
| 73 |         status = 0
 | 
| 74 |         for i, arg in enumerate(argv):
 | 
| 75 |             name, alias_exp = mylib.split_once(arg, '=')
 | 
| 76 |             if alias_exp is None:  # if we get a plain word without, print alias
 | 
| 77 |                 alias_exp = self.aliases.get(name)
 | 
| 78 |                 if alias_exp is None:
 | 
| 79 |                     self.errfmt.Print_('No alias named %r' % name,
 | 
| 80 |                                        blame_loc=cmd_val.arg_locs[i])
 | 
| 81 |                     status = 1
 | 
| 82 |                 else:
 | 
| 83 |                     print('alias %s=%r' % (name, alias_exp))
 | 
| 84 |             else:
 | 
| 85 |                 self.aliases[name] = alias_exp
 | 
| 86 | 
 | 
| 87 |         #print(argv)
 | 
| 88 |         #log('AFTER ALIAS %s', aliases)
 | 
| 89 |         return status
 | 
| 90 | 
 | 
| 91 | 
 | 
| 92 | class UnAlias(vm._Builtin):
 | 
| 93 | 
 | 
| 94 |     def __init__(self, aliases, errfmt):
 | 
| 95 |         # type: (Dict[str, str], ui.ErrorFormatter) -> None
 | 
| 96 |         self.aliases = aliases
 | 
| 97 |         self.errfmt = errfmt
 | 
| 98 | 
 | 
| 99 |     def Run(self, cmd_val):
 | 
| 100 |         # type: (cmd_value.Argv) -> int
 | 
| 101 |         attrs, arg_r = flag_util.ParseCmdVal('unalias', cmd_val)
 | 
| 102 |         arg = arg_types.unalias(attrs.attrs)
 | 
| 103 | 
 | 
| 104 |         if arg.a:
 | 
| 105 |             self.aliases.clear()
 | 
| 106 |             return 0
 | 
| 107 | 
 | 
| 108 |         argv = arg_r.Rest()
 | 
| 109 | 
 | 
| 110 |         if len(argv) == 0:
 | 
| 111 |             e_usage('requires an argument', loc.Missing)
 | 
| 112 | 
 | 
| 113 |         status = 0
 | 
| 114 |         for i, name in enumerate(argv):
 | 
| 115 |             if name in self.aliases:
 | 
| 116 |                 mylib.dict_erase(self.aliases, name)
 | 
| 117 |             else:
 | 
| 118 |                 self.errfmt.Print_('No alias named %r' % name,
 | 
| 119 |                                    blame_loc=cmd_val.arg_locs[i])
 | 
| 120 |                 status = 1
 | 
| 121 |         return status
 | 
| 122 | 
 | 
| 123 | 
 | 
| 124 | def SetOptionsFromFlags(exec_opts, opt_changes, shopt_changes):
 | 
| 125 |     # type: (MutableOpts, List[Tuple[str, bool]], List[Tuple[str, bool]]) -> None
 | 
| 126 |     """Used by core/shell.py."""
 | 
| 127 | 
 | 
| 128 |     # We can set ANY option with -o.  -O is too annoying to type.
 | 
| 129 |     for opt_name, b in opt_changes:
 | 
| 130 |         exec_opts.SetAnyOption(opt_name, b)
 | 
| 131 | 
 | 
| 132 |     for opt_name, b in shopt_changes:
 | 
| 133 |         exec_opts.SetAnyOption(opt_name, b)
 | 
| 134 | 
 | 
| 135 | 
 | 
| 136 | class Set(vm._Builtin):
 | 
| 137 | 
 | 
| 138 |     def __init__(self, exec_opts, mem):
 | 
| 139 |         # type: (MutableOpts, Mem) -> None
 | 
| 140 |         self.exec_opts = exec_opts
 | 
| 141 |         self.mem = mem
 | 
| 142 | 
 | 
| 143 |     def Run(self, cmd_val):
 | 
| 144 |         # type: (cmd_value.Argv) -> int
 | 
| 145 | 
 | 
| 146 |         # TODO:
 | 
| 147 |         # - How to integrate this with auto-completion?  Have to handle '+'.
 | 
| 148 | 
 | 
| 149 |         if len(cmd_val.argv) == 1:
 | 
| 150 |             # 'set' without args shows visible variable names and values.  According
 | 
| 151 |             # to POSIX:
 | 
| 152 |             # - the names should be sorted, and
 | 
| 153 |             # - the code should be suitable for re-input to the shell.  We have a
 | 
| 154 |             #   spec test for this.
 | 
| 155 |             # Also:
 | 
| 156 |             # - autoconf also wants them to fit on ONE LINE.
 | 
| 157 |             # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
 | 
| 158 |             mapping = self.mem.GetAllVars()
 | 
| 159 |             for name in sorted(mapping):
 | 
| 160 |                 str_val = mapping[name]
 | 
| 161 |                 code_str = '%s=%s' % (name, j8_lite.MaybeShellEncode(str_val))
 | 
| 162 |                 print(code_str)
 | 
| 163 |             return 0
 | 
| 164 | 
 | 
| 165 |         arg_r = args.Reader(cmd_val.argv, locs=cmd_val.arg_locs)
 | 
| 166 |         arg_r.Next()  # skip 'set'
 | 
| 167 |         arg = flag_util.ParseMore('set', arg_r)
 | 
| 168 | 
 | 
| 169 |         # 'set -o' shows options.  This is actually used by autoconf-generated
 | 
| 170 |         # scripts!
 | 
| 171 |         if arg.show_options:
 | 
| 172 |             self.exec_opts.ShowOptions([])
 | 
| 173 |             return 0
 | 
| 174 | 
 | 
| 175 |         # Note: set -o nullglob is not valid.  The 'shopt' builtin is preferred in
 | 
| 176 |         # YSH, and we want code to be consistent.
 | 
| 177 |         for opt_name, b in arg.opt_changes:
 | 
| 178 |             self.exec_opts.SetOldOption(opt_name, b)
 | 
| 179 | 
 | 
| 180 |         for opt_name, b in arg.shopt_changes:
 | 
| 181 |             self.exec_opts.SetAnyOption(opt_name, b)
 | 
| 182 | 
 | 
| 183 |         # Hm do we need saw_double_dash?
 | 
| 184 |         if arg.saw_double_dash or not arg_r.AtEnd():
 | 
| 185 |             self.mem.SetArgv(arg_r.Rest())
 | 
| 186 |         return 0
 | 
| 187 | 
 | 
| 188 | 
 | 
| 189 | class Shopt(vm._Builtin):
 | 
| 190 | 
 | 
| 191 |     def __init__(self, mutable_opts, cmd_ev):
 | 
| 192 |         # type: (MutableOpts, CommandEvaluator) -> None
 | 
| 193 |         self.mutable_opts = mutable_opts
 | 
| 194 |         self.cmd_ev = cmd_ev
 | 
| 195 | 
 | 
| 196 |     def Run(self, cmd_val):
 | 
| 197 |         # type: (cmd_value.Argv) -> int
 | 
| 198 |         attrs, arg_r = flag_util.ParseCmdVal('shopt',
 | 
| 199 |                                              cmd_val,
 | 
| 200 |                                              accept_typed_args=True)
 | 
| 201 | 
 | 
| 202 |         arg = arg_types.shopt(attrs.attrs)
 | 
| 203 |         opt_names = arg_r.Rest()
 | 
| 204 | 
 | 
| 205 |         if arg.p:  # print values
 | 
| 206 |             if arg.o:  # use set -o names
 | 
| 207 |                 self.mutable_opts.ShowOptions(opt_names)
 | 
| 208 |             else:
 | 
| 209 |                 self.mutable_opts.ShowShoptOptions(opt_names)
 | 
| 210 |             return 0
 | 
| 211 | 
 | 
| 212 |         if arg.q:  # query values
 | 
| 213 |             for name in opt_names:
 | 
| 214 |                 index = consts.OptionNum(name)
 | 
| 215 |                 if index == 0:
 | 
| 216 |                     return 2  # bash gives 1 for invalid option; 2 is better
 | 
| 217 |                 if not self.mutable_opts.opt0_array[index]:
 | 
| 218 |                     return 1  # at least one option is not true
 | 
| 219 |             return 0  # all options are true
 | 
| 220 | 
 | 
| 221 |         if arg.s:
 | 
| 222 |             b = True
 | 
| 223 |         elif arg.u:
 | 
| 224 |             b = False
 | 
| 225 |         else:
 | 
| 226 |             # If no flags are passed, print the options.  bash prints uses a
 | 
| 227 |             # different format for 'shopt', but we use the same format as 'shopt
 | 
| 228 |             # -p'.
 | 
| 229 |             self.mutable_opts.ShowShoptOptions(opt_names)
 | 
| 230 |             return 0
 | 
| 231 | 
 | 
| 232 |         cmd = typed_args.OptionalBlock(cmd_val)
 | 
| 233 |         if cmd:
 | 
| 234 |             opt_nums = []  # type: List[int]
 | 
| 235 |             for opt_name in opt_names:
 | 
| 236 |                 # TODO: could consolidate with checks in core/state.py and option
 | 
| 237 |                 # lexer?
 | 
| 238 |                 opt_group = consts.OptionGroupNum(opt_name)
 | 
| 239 |                 if opt_group == opt_group_i.YshUpgrade:
 | 
| 240 |                     opt_nums.extend(consts.YSH_UPGRADE)
 | 
| 241 |                     continue
 | 
| 242 | 
 | 
| 243 |                 if opt_group == opt_group_i.YshAll:
 | 
| 244 |                     opt_nums.extend(consts.YSH_ALL)
 | 
| 245 |                     continue
 | 
| 246 | 
 | 
| 247 |                 if opt_group == opt_group_i.StrictAll:
 | 
| 248 |                     opt_nums.extend(consts.STRICT_ALL)
 | 
| 249 |                     continue
 | 
| 250 | 
 | 
| 251 |                 index = consts.OptionNum(opt_name)
 | 
| 252 |                 if index == 0:
 | 
| 253 |                     # TODO: location info
 | 
| 254 |                     e_usage('got invalid option %r' % opt_name, loc.Missing)
 | 
| 255 |                 opt_nums.append(index)
 | 
| 256 | 
 | 
| 257 |             with state.ctx_Option(self.mutable_opts, opt_nums, b):
 | 
| 258 |                 unused = self.cmd_ev.EvalCommand(cmd)
 | 
| 259 |             return 0  # cd also returns 0
 | 
| 260 | 
 | 
| 261 |         # Otherwise, set options.
 | 
| 262 |         for opt_name in opt_names:
 | 
| 263 |             # We allow set -o options here
 | 
| 264 |             self.mutable_opts.SetAnyOption(opt_name, b)
 | 
| 265 | 
 | 
| 266 |         return 0
 | 
| 267 | 
 | 
| 268 | 
 | 
| 269 | class Hash(vm._Builtin):
 | 
| 270 | 
 | 
| 271 |     def __init__(self, search_path):
 | 
| 272 |         # type: (SearchPath) -> None
 | 
| 273 |         self.search_path = search_path
 | 
| 274 | 
 | 
| 275 |     def Run(self, cmd_val):
 | 
| 276 |         # type: (cmd_value.Argv) -> int
 | 
| 277 |         attrs, arg_r = flag_util.ParseCmdVal('hash', cmd_val)
 | 
| 278 |         arg = arg_types.hash(attrs.attrs)
 | 
| 279 | 
 | 
| 280 |         rest = arg_r.Rest()
 | 
| 281 |         if arg.r:
 | 
| 282 |             if len(rest):
 | 
| 283 |                 e_usage('got extra arguments after -r', loc.Missing)
 | 
| 284 |             self.search_path.ClearCache()
 | 
| 285 |             return 0
 | 
| 286 | 
 | 
| 287 |         status = 0
 | 
| 288 |         if len(rest):
 | 
| 289 |             for cmd in rest:  # enter in cache
 | 
| 290 |                 full_path = self.search_path.CachedLookup(cmd)
 | 
| 291 |                 if full_path is None:
 | 
| 292 |                     print_stderr('hash: %r not found' % cmd)
 | 
| 293 |                     status = 1
 | 
| 294 |         else:  # print cache
 | 
| 295 |             commands = self.search_path.CachedCommands()
 | 
| 296 |             commands.sort()
 | 
| 297 |             for cmd in commands:
 | 
| 298 |                 print(cmd)
 | 
| 299 | 
 | 
| 300 |         return status
 | 
| 301 | 
 | 
| 302 | 
 | 
| 303 | def _ParseOptSpec(spec_str):
 | 
| 304 |     # type: (str) -> Dict[str, bool]
 | 
| 305 |     spec = {}  # type: Dict[str, bool]
 | 
| 306 |     i = 0
 | 
| 307 |     n = len(spec_str)
 | 
| 308 |     while True:
 | 
| 309 |         if i >= n:
 | 
| 310 |             break
 | 
| 311 |         ch = spec_str[i]
 | 
| 312 |         spec[ch] = False
 | 
| 313 |         i += 1
 | 
| 314 |         if i >= n:
 | 
| 315 |             break
 | 
| 316 |         # If the next character is :, change the value to True.
 | 
| 317 |         if spec_str[i] == ':':
 | 
| 318 |             spec[ch] = True
 | 
| 319 |             i += 1
 | 
| 320 |     return spec
 | 
| 321 | 
 | 
| 322 | 
 | 
| 323 | class GetOptsState(object):
 | 
| 324 |     """State persisted across invocations.
 | 
| 325 | 
 | 
| 326 |     This would be simpler in GetOpts.
 | 
| 327 |     """
 | 
| 328 | 
 | 
| 329 |     def __init__(self, mem, errfmt):
 | 
| 330 |         # type: (Mem, ui.ErrorFormatter) -> None
 | 
| 331 |         self.mem = mem
 | 
| 332 |         self.errfmt = errfmt
 | 
| 333 |         self._optind = -1
 | 
| 334 |         self.flag_pos = 1  # position within the arg, public var
 | 
| 335 | 
 | 
| 336 |     def _OptInd(self):
 | 
| 337 |         # type: () -> int
 | 
| 338 |         """Returns OPTIND that's >= 1, or -1 if it's invalid."""
 | 
| 339 |         # Note: OPTIND could be value.Int?
 | 
| 340 |         try:
 | 
| 341 |             result = state.GetInteger(self.mem, 'OPTIND')
 | 
| 342 |         except error.Runtime as e:
 | 
| 343 |             self.errfmt.Print_(e.UserErrorString())
 | 
| 344 |             result = -1
 | 
| 345 |         return result
 | 
| 346 | 
 | 
| 347 |     def GetArg(self, argv):
 | 
| 348 |         # type: (List[str]) -> Optional[str]
 | 
| 349 |         """Get the value of argv at OPTIND.
 | 
| 350 | 
 | 
| 351 |         Returns None if it's out of range.
 | 
| 352 |         """
 | 
| 353 | 
 | 
| 354 |         #log('_optind %d flag_pos %d', self._optind, self.flag_pos)
 | 
| 355 | 
 | 
| 356 |         optind = self._OptInd()
 | 
| 357 |         if optind == -1:
 | 
| 358 |             return None
 | 
| 359 |         self._optind = optind  # save for later
 | 
| 360 | 
 | 
| 361 |         i = optind - 1  # 1-based index
 | 
| 362 |         #log('argv %s i %d', argv, i)
 | 
| 363 |         if 0 <= i and i < len(argv):
 | 
| 364 |             return argv[i]
 | 
| 365 |         else:
 | 
| 366 |             return None
 | 
| 367 | 
 | 
| 368 |     def IncIndex(self):
 | 
| 369 |         # type: () -> None
 | 
| 370 |         """Increment OPTIND."""
 | 
| 371 |         # Note: bash-completion uses a *local* OPTIND !  Not global.
 | 
| 372 |         assert self._optind != -1
 | 
| 373 |         state.BuiltinSetString(self.mem, 'OPTIND', str(self._optind + 1))
 | 
| 374 |         self.flag_pos = 1
 | 
| 375 | 
 | 
| 376 |     def SetArg(self, optarg):
 | 
| 377 |         # type: (str) -> None
 | 
| 378 |         """Set OPTARG."""
 | 
| 379 |         state.BuiltinSetString(self.mem, 'OPTARG', optarg)
 | 
| 380 | 
 | 
| 381 |     def Fail(self):
 | 
| 382 |         # type: () -> None
 | 
| 383 |         """On failure, reset OPTARG."""
 | 
| 384 |         state.BuiltinSetString(self.mem, 'OPTARG', '')
 | 
| 385 | 
 | 
| 386 | 
 | 
| 387 | def _GetOpts(
 | 
| 388 |         spec,  # type: Dict[str, bool]
 | 
| 389 |         argv,  # type: List[str]
 | 
| 390 |         my_state,  # type: GetOptsState
 | 
| 391 |         errfmt,  # type: ui.ErrorFormatter
 | 
| 392 | ):
 | 
| 393 |     # type: (...) -> Tuple[int, str]
 | 
| 394 |     current = my_state.GetArg(argv)
 | 
| 395 |     #log('current %s', current)
 | 
| 396 | 
 | 
| 397 |     if current is None:  # out of range, etc.
 | 
| 398 |         my_state.Fail()
 | 
| 399 |         return 1, '?'
 | 
| 400 | 
 | 
| 401 |     if not current.startswith('-') or current == '-':
 | 
| 402 |         my_state.Fail()
 | 
| 403 |         return 1, '?'
 | 
| 404 | 
 | 
| 405 |     flag_char = current[my_state.flag_pos]
 | 
| 406 | 
 | 
| 407 |     if my_state.flag_pos < len(current) - 1:
 | 
| 408 |         my_state.flag_pos += 1  # don't move past this arg yet
 | 
| 409 |         more_chars = True
 | 
| 410 |     else:
 | 
| 411 |         my_state.IncIndex()
 | 
| 412 |         my_state.flag_pos = 1
 | 
| 413 |         more_chars = False
 | 
| 414 | 
 | 
| 415 |     if flag_char not in spec:  # Invalid flag
 | 
| 416 |         return 0, '?'
 | 
| 417 | 
 | 
| 418 |     if spec[flag_char]:  # does it need an argument?
 | 
| 419 |         if more_chars:
 | 
| 420 |             optarg = current[my_state.flag_pos:]
 | 
| 421 |         else:
 | 
| 422 |             optarg = my_state.GetArg(argv)
 | 
| 423 |             if optarg is None:
 | 
| 424 |                 my_state.Fail()
 | 
| 425 |                 # TODO: Add location info
 | 
| 426 |                 errfmt.Print_('getopts: option %r requires an argument.' %
 | 
| 427 |                               current)
 | 
| 428 |                 tmp = [j8_lite.MaybeShellEncode(a) for a in argv]
 | 
| 429 |                 print_stderr('(getopts argv: %s)' % ' '.join(tmp))
 | 
| 430 | 
 | 
| 431 |                 # Hm doesn't cause status 1?
 | 
| 432 |                 return 0, '?'
 | 
| 433 |         my_state.IncIndex()
 | 
| 434 |         my_state.SetArg(optarg)
 | 
| 435 |     else:
 | 
| 436 |         my_state.SetArg('')
 | 
| 437 | 
 | 
| 438 |     return 0, flag_char
 | 
| 439 | 
 | 
| 440 | 
 | 
| 441 | class GetOpts(vm._Builtin):
 | 
| 442 |     """
 | 
| 443 |   Vars used:
 | 
| 444 |     OPTERR: disable printing of error messages
 | 
| 445 |   Vars set:
 | 
| 446 |     The variable named by the second arg
 | 
| 447 |     OPTIND - initialized to 1 at startup
 | 
| 448 |     OPTARG - argument
 | 
| 449 |   """
 | 
| 450 | 
 | 
| 451 |     def __init__(self, mem, errfmt):
 | 
| 452 |         # type: (Mem, ui.ErrorFormatter) -> None
 | 
| 453 |         self.mem = mem
 | 
| 454 |         self.errfmt = errfmt
 | 
| 455 | 
 | 
| 456 |         # TODO: state could just be in this object
 | 
| 457 |         self.my_state = GetOptsState(mem, errfmt)
 | 
| 458 |         self.spec_cache = {}  # type: Dict[str, Dict[str, bool]]
 | 
| 459 | 
 | 
| 460 |     def Run(self, cmd_val):
 | 
| 461 |         # type: (cmd_value.Argv) -> int
 | 
| 462 |         arg_r = args.Reader(cmd_val.argv, locs=cmd_val.arg_locs)
 | 
| 463 |         arg_r.Next()
 | 
| 464 | 
 | 
| 465 |         # NOTE: If first char is a colon, error reporting is different.  Alpine
 | 
| 466 |         # might not use that?
 | 
| 467 |         spec_str = arg_r.ReadRequired('requires an argspec')
 | 
| 468 | 
 | 
| 469 |         var_name, var_loc = arg_r.ReadRequired2(
 | 
| 470 |             'requires the name of a variable to set')
 | 
| 471 | 
 | 
| 472 |         spec = self.spec_cache.get(spec_str)
 | 
| 473 |         if spec is None:
 | 
| 474 |             spec = _ParseOptSpec(spec_str)
 | 
| 475 |             self.spec_cache[spec_str] = spec
 | 
| 476 | 
 | 
| 477 |         user_argv = self.mem.GetArgv() if arg_r.AtEnd() else arg_r.Rest()
 | 
| 478 |         #log('user_argv %s', user_argv)
 | 
| 479 |         status, flag_char = _GetOpts(spec, user_argv, self.my_state,
 | 
| 480 |                                      self.errfmt)
 | 
| 481 | 
 | 
| 482 |         if match.IsValidVarName(var_name):
 | 
| 483 |             state.BuiltinSetString(self.mem, var_name, flag_char)
 | 
| 484 |         else:
 | 
| 485 |             # NOTE: The builtin has PARTIALLY set state.  This happens in all shells
 | 
| 486 |             # except mksh.
 | 
| 487 |             raise error.Usage('got invalid variable name %r' % var_name,
 | 
| 488 |                               var_loc)
 | 
| 489 |         return status
 |