Bug 77259

Summary: /bin/sh: shell command "command -v cmd" doesn't work
Product: Base System Reporter: Bram <Bram>
Component: binAssignee: Stefan Farfeleder <stefanf>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   

Description Bram 2005-02-08 11:10:21 UTC
In a "sh" shell the "command" command doesn't accept the "-v" argument.  This breaks shell scripts that were written for a POSIX shell.
This is more lack of a feature than a real bug, but since the shell scripts work fine otherwise, fixing this problem will help people trying to run the POSIX test suite on FreeBSD.

Fix: 

Should modify commandcmd() in /usr/src/bin/sh/eval.c.
How-To-Repeat: % /bin/sh
$ command -v ls
command: unknown option: -v
$
Comment 1 Vsevolod Stakhov 2005-02-11 22:32:22 UTC
Here is some kind of patch:

--- eval.c.orig Wed Apr  7 00:06:51 2004
+++ eval.c      Sat Feb 12 01:20:11 2005
@@ -970,8 +970,10 @@
        static char stdpath[] = _PATH_STDPATH;
        struct jmploc loc, *old;
        struct strlist *sp;
+       struct cmdentry cent;
        char *path;
        int ch;
+       int vcmdflag = 0;

        for (sp = cmdenviron; sp ; sp = sp->next)
                setvareq(sp->text, VEXPORT|VSTACK);
@@ -979,11 +981,14 @@

        optind = optreset = 1;
        opterr = 0;
-       while ((ch = getopt(argc, argv, "p")) != -1) {
+       while ((ch = getopt(argc, argv, "pv")) != -1) {
                switch (ch) {
                case 'p':
                        path = stdpath;
                        break;
+               case 'v':
+                       vcmdflag = 1;
+                       break;
                case '?':
                default:
                        error("unknown option: -%c", optopt);
@@ -993,14 +998,32 @@
        argv += optind;

        if (argc != 0) {
-               old = handler;
-               handler = &loc;
-               if (setjmp(handler->loc) == 0)
-                       shellexec(argv, environment(), path, 0);
-               handler = old;
-               if (exception == EXEXEC)
-                       exit(exerrno);
-               exraise(exception);
+               if(!vcmdflag){
+                       old = handler;
+                       handler = &loc;
+                       if (setjmp(handler->loc) == 0)
+                               shellexec(argv, environment(), path, 0);
+                       handler = old;
+                       if (exception == EXEXEC)
+                               exit(exerrno);
+                       exraise(exception);
+               }
+               else{
+                       find_command(argv[0], &cent, 0, path);
+
+                       switch(cent.cmdtype) {
+                               case CMDNORMAL:
+                                       out1fmt("%s\n", padvance(&path, 
argv[0]));
+                                       break;
+                               case CMDBUILTIN:
+                                       out1fmt("%s\n", argv[0]);
+                                       break;
+                               default:
+                                       out1str("");
+                                       break;
+                       }
+                       flushall();
+               }
        }

        /*
Comment 2 Stefan Farfeleder freebsd_committer freebsd_triage 2005-09-10 11:02:47 UTC
Responsible Changed
From-To: freebsd-bugs->stefanf

I'll handle this.
Comment 3 Craig Rodrigues 2005-10-28 03:51:19 UTC
Stefan,

Are you still working on this?  I have a patch which implements
command -v and command -V in terms of the existing "type"
builtin command.

-- 
Craig Rodrigues        
rodrigc@crodrigues.org


Index: eval.c
===================================================================
RCS file: /home/ncvs/src/bin/sh/eval.c,v
retrieving revision 1.46
diff -u -u -r1.46 eval.c
--- eval.c	10 Sep 2005 08:25:28 -0000	1.46
+++ eval.c	28 Oct 2005 02:47:08 -0000
@@ -983,12 +983,23 @@
 
 	optind = optreset = 1;
 	opterr = 0;
-	while ((ch = getopt(argc, argv, "p")) != -1) {
+	while ((ch = getopt(argc, argv, "pv:V:")) != -1) {
 		switch (ch) {
 		case 'p':
 			path = stdpath;
 			break;
+		case 'v': {
+			char *new_argv[] = {"type", optarg, 0 };
+			return(typecmd_impl(2, new_argv, 0));
+			}
+			break;
+		case 'V': {
+			char *new_argv[] = {"type", optarg, 0 };
+			return(typecmd_impl(2, new_argv, 1));
+			}
+			break;
 		case '?':
+			error("missing argument: -%c", optopt);
 		default:
 			error("unknown option: -%c", optopt);
 		}
Index: exec.c
===================================================================
RCS file: /home/ncvs/src/bin/sh/exec.c,v
retrieving revision 1.26
diff -u -u -r1.26 exec.c
--- exec.c	13 Aug 2005 08:12:18 -0000	1.26
+++ exec.c	28 Oct 2005 02:47:08 -0000
@@ -705,11 +705,11 @@
 }
 
 /*
- * Locate and print what a word is...
- */
-
+ * Shared code for the following builtin commands:
+ *    type, command -v, command -V
+ */ 
 int
-typecmd(int argc, char **argv)
+typecmd_impl(int argc, char **argv, int typecmd)
 {
 	struct cmdentry entry;
 	struct tblentry *cmdp;
@@ -720,20 +720,30 @@
 	extern char *const parsekwd[];
 
 	for (i = 1; i < argc; i++) {
-		out1str(argv[i]);
+		if (typecmd)
+			out1str(argv[i]);
+
 		/* First look at the keywords */
 		for (pp = (char **)parsekwd; *pp; pp++)
 			if (**pp == *argv[i] && equal(*pp, argv[i]))
 				break;
 
 		if (*pp) {
-			out1str(" is a shell keyword\n");
+			if (typecmd)
+				out1str(" is a shell keyword\n");
+			else
+				out1fmt("%s\n", argv[i]);
+
 			continue;
 		}
 
 		/* Then look at the aliases */
 		if ((ap = lookupalias(argv[i], 1)) != NULL) {
-			out1fmt(" is an alias for %s\n", ap->val);
+			if (typecmd)
+				out1fmt(" is an alias for %s\n", ap->val);
+			else
+				out1fmt("alias %s='%s'\n", argv[i], ap->val);
+
 			continue;
 		}
 
@@ -756,29 +766,59 @@
 					name = padvance(&path, argv[i]);
 					stunalloc(name);
 				} while (--j >= 0);
-				out1fmt(" is%s %s\n",
-				    cmdp ? " a tracked alias for" : "", name);
+
+				if (typecmd) {
+					out1fmt(" is%s %s\n",
+					    cmdp ? " a tracked alias for" : "", name);
+				} else {
+					if (cmdp)
+						out1fmt("alias %s='%s'\n", argv[i], name);
+					else
+						out1fmt("%s\n", name);
+				}
 			} else {
-				if (access(argv[i], X_OK) == 0)
-					out1fmt(" is %s\n", argv[i]);
+				if (access(argv[i], X_OK) == 0) {
+					if (typecmd)
+						out1fmt(" is %s\n", argv[i]);
+					else
+						out1fmt("%s\n", argv[i]);
+				}
 				else
 					out1fmt(": %s\n", strerror(errno));
 			}
 			break;
 		}
 		case CMDFUNCTION:
-			out1str(" is a shell function\n");
+			if (typecmd)
+				out1str(" is a shell function\n");
+			else
+				out1fmt("%s\n", argv[i]);
 			break;
 
 		case CMDBUILTIN:
-			out1str(" is a shell builtin\n");
+			if (typecmd)
+				out1str(" is a shell builtin\n");
+			else
+				out1fmt("%s\n", argv[i]);
 			break;
 
 		default:
-			out1str(": not found\n");
+			if (typecmd)
+				out1str(": not found\n");
+			else
+				out1fmt("%s: not found\n", argv[i]);
 			error |= 127;
 			break;
 		}
 	}
 	return error;
 }
+
+/*
+ * Locate and print what a word is...
+ */
+int
+typecmd(int argc, char **argv)
+{
+	return typecmd_impl(argc, argv, 1);
+}
Index: exec.h
===================================================================
RCS file: /home/ncvs/src/bin/sh/exec.h,v
retrieving revision 1.12
diff -u -u -r1.12 exec.h
--- exec.h	6 Apr 2004 20:06:51 -0000	1.12
+++ exec.h	28 Oct 2005 02:47:08 -0000
@@ -63,5 +63,6 @@
 void addcmdentry(char *, struct cmdentry *);
 void defun(char *, union node *);
 int unsetfunc(char *);
+int typecmd_impl(int, char **, int);
 int typecmd(int, char **);
 void clearcmdentry(int);
Comment 4 Stefan Farfeleder freebsd_committer freebsd_triage 2005-10-28 19:43:34 UTC
State Changed
From-To: open->patched

A modified version of Craig's patch was committed to current.  Thanks!
Comment 5 Stefan Farfeleder freebsd_committer freebsd_triage 2005-12-26 18:21:25 UTC
State Changed
From-To: patched->closed

Also fixed in RELENG_{5,6}.