Search code examples
ccommand-lineargvgetoptargc

Command line parsing in argc/argv


I have the following code:

void parse(char *commandLine) {
    int rc = 0;
    int argc = 0;
    char *cmdLine;
    char *argv[MAX_ARGS];
    filename = NULL;
    stdoutFilename = NULL;
    stderrFilename = NULL;
    cmdLine = strdup(commandLine);
    char *param = strtok(cmdLine, " ");
    while (param && argc < MAX_ARGS) {
        argv[argc++] = param;
        param = strtok(NULL, " ");
        printf("%s\n", argv[argc-1]);
    }
    free(cmdLine);
    scanOptions(argc, argv);
    printf("Filename %s\n", filename);

...

and

void scanOptions(int argc, char *argv[]) {
    int c ;
    while ((c = getopt (argc, argv, "Df:e:o:")) != -1) {
        switch (c) {
            case 'D': __debug = 1; break;
            case 'f': filename = strdup(optarg); break;
            case 'o': stdoutFilename = strdup(optarg); break;
            case 'e': stderrFilename = strdup(optarg); break;
            default: fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
        }
    }
}

filename, stdoutFilename and stderrFilename are global variables. If I call the parse method as:

parse("-ftest/testfile.txt") the variable filename is not set and the call to 
printf("Filename %s\n", filename); prints "Filename (null)".

What's wrong with that?


Solution

  • There's a few things wrong, that may or may not be the cause of your problem:

    Use of freed memory

    free(cmdLine);
    scanOptions(argc, argv);
    

    You can't free the cmdLine here, since your strtok() calls will assign pointers inside cmdLine to your argv. free() it after scanOptions() , though if you save any optarg pointers directly, they will point into space that you have free()'d - you use strdup() so youre safe in your case.

    Resetting getopt()

    If you have called getopt previously, you need to reset some of its variables so it can scan again, (see the getopt manpage for an explanation). You need to do:

    optind = 0;
    

    Wrong index in argv The first index in argv is by convention the program name, not any program arguments. So make sure your argv[0] isn't any of your arguments. But it needs to be a valid string and not e.g. a NULL pointer.

    argv[1] should be the first argument.

    Add a sentiel to argv

    The traditional argv of main() ends with a NULL pointer, your emulated argv should too. After the while loop, do

    argv[argc] = NULL;