S2argv-execs: something was missing in libc

Da raspibo.
Jump to navigation Jump to search

Posix standard provides a number of functions to execute a file. It is the exec family.

Some of them can take a variable number of args, others use an array of strings. The formers have a name having an 'l' in the suffix, the latters a 'v'.

But what do you do if you have a string including all the arguments separated by spaces. This is the case, for instance, when the command has been provided by the user or read from a file.

It happened to me dozens of times.

There are two naive solutions. strtok and system. strtok can be usen to tokenize the string, system delegates the problem to a shell. Both solutions are unsitisfactory to me. system is unsafe (it is stated also on its man page!). strtok use spaces to split the string into pieces, modify the original string, do not support quotation or escape characters, It is also very annoying to use strtok to parse the arguments for an exec.

I needed a clean solution to parse the arguments from a string.

So I decided to write the libs2argv library. (200 lines of C source).

It is not a historical achievement in the story of computing, but it is something that was missing and I felt the need for.

The code has been released on GitHub under LGPL2.1+.

The API of the library is clean and straightforward.

There are two possible use cases.

s2argv

convert the string in a dynamically allocated argv using s2argv (string to argv): char **s2argv(const char *args, int *pargc);

The return value is a valid argv, if pargc is non-NULL, the number of args, i.e. the argc, is stored in the integer field pointed to by pargc. s2argv uses dynamic allocation and does not modify its input parameter args.

s2argv tokenize the arguments using spaces (blanks, tabs or newlines) but it also supports simple and double quotation to specify arguments including spaces, and the escape char \\.

Usually an argv is used as an argument to an execv. If the execv function succeeds, it is useless to deallocate the argv, all the memory of the calling program has been deallocated anyway. But sometimes if the execv fails the program has to do something else and the argv should be deallocated. For that the library provides the following function:

 void s2argv_free(char **argv);

So a code snippet to execute a file from a string (buf in this example) is the following:

                char **argv=s2argv(buf, NULL);
                execvp(argv[0], argv);
                s2argv_free(argv);
                printf("exec error\n");

execs and siblings

The library provides three functions:

      int execs(const char *path, char *args);
      int execsp(char *args);
      int execspe(char *args, char *const envp[]);

they have been designed as the "parse args from a string" counterpart of execv, execvp and execvde respectively.

These functions do not use dynamic allocation and modify their args parameter. So in case you need to reuse the args string in case of failure, backup the args string in advance or use s2argv instead.

The return value and error cases are the same as execve. execsp and execspe use the parsed argv[0] as the filename.

Here is a chunk of code using execsp:

           char buf[BUFLEN];
           printf("type in a command and its arguments, e.g. 'ls -l'\n");
           if (fgets(buf, BUFLEN, stdin) != NULL) {
                execsp(buf);
                printf("exec error\n");
           }