Permalink
Browse files

Implement 'command -v'.

Share logic with 'type -t', which largely does the same thing.

The other flags to 'command' aren't implemented yet.

Fixes issue #50.
  • Loading branch information...
Andy Chu
Andy Chu committed Nov 16, 2017
1 parent ea1d24f commit 6ea7878297644006e4d28c6c0406e133c4e3c323
Showing with 93 additions and 22 deletions.
  1. +57 −21 core/builtin.py
  2. +5 −1 core/cmd_exec.py
  3. +26 −0 spec/builtins2.test.sh
  4. +5 −0 test/spec.sh
View
@@ -61,7 +61,7 @@
TRUE FALSE
COLON
TEST BRACKET GETOPTS
TYPE HELP
COMMAND TYPE HELP
""".split())
@@ -203,6 +203,9 @@ def Resolve(argv0):
elif argv0 == "getopts":
return EBuiltin.GETOPTS
elif argv0 == "command":
return EBuiltin.COMMAND
elif argv0 == "type":
return EBuiltin.TYPE
@@ -668,45 +671,78 @@ def Unset(argv, mem, funcs):
return 0
TYPE_SPEC = _Register('type')
TYPE_SPEC.ShortFlag('-t')
def Type(argv, funcs, path_val):
arg, i = TYPE_SPEC.Parse(argv)
def _ResolveNames(names, funcs, path_val):
if path_val.tag == value_e.Str:
path_list = path_val.s.split(':')
else:
path_list = [] # treat as empty path
status = 0
if not arg.t:
util.warn("*** 'type' builtin called without -t ***")
status = 1
# Keep going anyway
for name in argv[i:]:
results = []
for name in names:
if name in funcs:
print('function')
kind = ('function', name)
elif Resolve(name) != EBuiltin.NONE:
print('builtin')
kind = ('builtin', name)
elif ResolveSpecial(name) != EBuiltin.NONE:
print('builtin')
kind = ('builtin', name)
elif lex.IsOtherBuiltin(name): # declare, continue, etc.
print('builtin')
kind = ('builtin', name)
elif lex.IsKeyword(name):
print('keyword')
kind = ('keyword', name)
else:
# Now look for files.
found = False
for path_dir in path_list:
full_path = os.path.join(path_dir, name)
if os.path.exists(full_path):
print('file')
kind = ('file', full_path)
found = True
break
if not found: # Nothing printed, but status is 1.
status = 1
kind = (None, None)
results.append(kind)
return results
COMMAND_SPEC = _Register('command')
COMMAND_SPEC.ShortFlag('-v')
COMMAND_SPEC.ShortFlag('-V')
def Command(argv, funcs, path_val):
arg, i = COMMAND_SPEC.Parse(argv)
status = 0
if arg.v:
for kind, arg in _ResolveNames(argv[i:], funcs, path_val):
if kind is None:
status = 1 # nothing printed, but we fail
else:
# This is for -v, -V is more detailed.
print(arg)
else:
util.warn('*** command without -v not not implemented ***')
status = 1
return status
TYPE_SPEC = _Register('type')
TYPE_SPEC.ShortFlag('-t')
def Type(argv, funcs, path_val):
arg, i = TYPE_SPEC.Parse(argv)
status = 0
if not arg.t:
util.warn("*** 'type' builtin called without -t ***")
status = 1
# Keep going anyway
for kind, arg in _ResolveNames(argv[i:], funcs, path_val):
if kind is None:
status = 1 # nothing printed, but we fail
else:
print(kind)
return status
View
@@ -303,6 +303,10 @@ def _RunBuiltin(self, builtin_id, argv):
elif builtin_id == EBuiltin.GETOPTS:
status = builtin.GetOpts(argv, self.mem)
elif builtin_id == EBuiltin.COMMAND:
path = self.mem.GetVar('PATH')
status = builtin.Command(argv, self.funcs, path)
elif builtin_id == EBuiltin.TYPE:
path = self.mem.GetVar('PATH')
status = builtin.Type(argv, self.funcs, path)
@@ -315,7 +319,7 @@ def _RunBuiltin(self, builtin_id, argv):
status = builtin.DebugLine(argv, self.status_lines)
else:
raise AssertionError('Unhandled builtin: %d' % builtin_id)
raise AssertionError('Unhandled builtin: %s' % builtin_id)
assert isinstance(status, int)
return status
View
@@ -0,0 +1,26 @@
#!/bin/bash
### command -v
myfunc() { echo x; }
command -v echo
echo $?
command -v myfunc
echo $?
command -v nonexistent # doesn't print anything?
echo $?
command -v for
echo $?
# stdout-json: "echo\n0\nmyfunc\n0\n1\nfor\n0\n"
# OK dash stdout-json: "echo\n0\nmyfunc\n0\n127\nfor\n0\n"
### command -v with multiple names
# bash chooses to swallow the error! We agree with zsh if ANY word lookup
# fails, then the whole thing fails.
# All four shells behave differently here!
myfunc() { echo x; }
command -v echo myfunc ZZZ for
echo status=$?
# stdout-json: "echo\nmyfunc\nfor\nstatus=1\n"
# BUG bash stdout-json: "echo\nmyfunc\nfor\nstatus=0\n"
# BUG dash stdout-json: "echo\nstatus=0\n"
# OK mksh stdout-json: "echo\nmyfunc\nstatus=1\n"
View
@@ -256,6 +256,11 @@ builtins() {
${REF_SHELLS[@]} $OSH "$@"
}
builtins2() {
sh-spec spec/builtins2.test.sh --osh-failures-allowed 0 \
${REF_SHELLS[@]} $ZSH $OSH "$@"
}
builtin-vars() {
sh-spec spec/builtin-vars.test.sh --osh-failures-allowed 2 \
${REF_SHELLS[@]} $OSH "$@"

0 comments on commit 6ea7878

Please sign in to comment.