1

I am writing a simple shell in C. It is actually coming along quite well with i/o redirection and such. One thing that I'd like to add is a way to switch between versions of the exec* functions. Right now I'm sticking to execlp(), execvp(), and execve().

I have all the arguments I want to pass in an array called argv. It is a null terminated array of null terminated strings, so it works fine with execv*, but I can't think of how to get it to work with execlp().

This is what I have now:

if     (strcmp(exec_opt, "vp") == 0)
  error = execvp(argv[0], argv);          /* Execute vp */
else if(strcmp(exec_opt, "lp") == 0)
  error = execlp(argv[0], "", argv);      /* Execute lp */
else if(strcmp(exec_opt, "ve") == 0)
  error = execve(argv[0], argv, environ); /* Execute ve */
else
{
  // throw errors about exec_opt
}

if(error != 0)
{
  // do something about it
}

In this configuration the compiler doesn't baff at the syntax, but it also doesn't work. I've also tried

 error = execlp(argv[0], (char*) argv);  /* As a single string */
 char* argv1 = argv[1];                  /* don't pass command itself */
 error = execlp(argv[0], argv1);         

Which do various odd but ultimately incorrect things. Is there a way for me to turn my array into a variable argument list? passing it directly (which makes the most type-sense, since variable argument lists are char* argv[]) yields a compiler error about casting incompatible pointers.

Huckle
  • 1,810
  • 3
  • 26
  • 40
  • 1
    I don't see the point in trying to use the execl* functions. They are only there to make it easier to call the functions when you don't have all the parameters in an array, but you do. – Vaughn Cato Apr 10 '12 at 01:32
  • @VaughnCato I realize this, but the point of selecting these functions at runtime is to illustrate how they vary from function to function. – Huckle Apr 10 '12 at 01:49

2 Answers2

1

You can't really use execlp() with the array. To use execlp(), you have to write out:

execlp(array[0], array[0], (char *)0);
execlp(array[0], array[0], array[1], (char *)0);
execlp(array[0], array[0], array[1], array[2], (char *)0);
execlp(array[0], array[0], array[1], array[2], array[3], (char *)0);
...

for each alternative number of arguments. This is why execvp() was added to the repertoire (it wasn't part of 7th Edition UNIX™ in 1978, but was part of SUS v2 in 1997). Now you just need execvpe(), which does not exist AFAIK (and I don't know why it isn't provided, either).


7th Edition UNIX did have excevp()

Dave said:

The 7th Edition manual does list execvp.

And ... it does, partially. I think we have an erratum to report since the manual actually contains:

NAME

execl, execv, execle, execve, execlp, execvp, exec, exece, environ – execute a file

SYNOPSIS

execl(name, arg0, arg1, ..., argn, 0)
char *name, *arg0, *arg1, ..., *argn;
execv(name, argv)
char *name, *argv[ ];
execle(name, arg0, arg1, ..., argn, 0, envp)
char *name, *arg0, *arg1, ..., *argn, *envp[ ];
execve(name, argv, envp);
char *name, *argv[ ], *envp[ ];
extern char **environ;

DESCRIPTION

So, execvp() is listed in the NAME section, but there is no synopsis for execvp() (which is what I looked at to come to the conclusion it was missing). There is a reference to execvp() on the next page:

Execlp and execvp are called with the same arguments as execl and execv, but duplicate the shell’s actions in searching for an executable file in a list of directories. The directory list is obtained from the environment.

So, I excuse myself because I scanned the SYNOPSIS and execvp() was omitted from the synopsis. But actually, the system call was present in 7th Edition Unix. I don't think anyone is about to re-release the manuals with the omission fixed.

My print copy (yes, I do have printed properly bound versions of both Volume 1 (ISBN 0-03-061742-1) and Volume 2 (ISBN 0-03-061743-X) of the manual from way back then; I obtained them circa 1989) has the same omission in the SYNOPSIS section.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • You could build `execvpe()` fairly easily by forking, fixing up the environment the way you want it, and then calling `execvp()` – Huckle Apr 10 '12 at 01:45
  • @Huckle: yes - I suppose so. And because the child is guaranteed single-threaded, there's no multi-threading problem unless you did something weird like place parts of the environment in shared memory and something modifies it before the child gets to `execve()`. – Jonathan Leffler Apr 10 '12 at 01:48
  • @Huckle why fork? No-one in the `exec` family forks. – Dave Apr 10 '12 at 05:03
  • @Dave: good point; I was assuming 'after the fork', rather than reading what was said. Nevertheless, Huckle's point that `execvpe()` is not all that hard to code is basically correct. Something like: `int execvpe(const char *cmd, char *const* argv, char *const* envp) { environ = envp; return execvp(cmd, argv); }`, which is not dramatically difficult. I stand by 'it would (occasionally) be nice if it was there already'; but it isn't a critical omission (unlike `execvp()` from 7th Edition Unix). – Jonathan Leffler Apr 10 '12 at 05:14
  • @JonathanLeffler for what it's worth, the [7th edition manual](http://plan9.bell-labs.com/7thEdMan/bswv7.html) does list `execvp`. – Dave Apr 10 '12 at 05:26
0

Try using avcall. I haven't used it myself, I just found an interesting mention of it here: Passing parameters dynamically to variadic functions

Community
  • 1
  • 1
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • This seems interesting, but it is a requirement to stick strictly to C and POSIX functions only for portability purposes. I guess I could do something similar by building a space-delimited string from the array, but I figured there might be a better way. – Huckle Apr 10 '12 at 01:48
  • That's interesting - thanks for the x-ref. I'm not sure it's a good solution to the problem compared with abandoning `execlp()` and simply using `execvp()`. – Jonathan Leffler Apr 10 '12 at 01:49