mohelt
mohelt

Reputation: 55

getopt() in C not working correctly after if statement

I would like to implement a copy of the Linux head commmand. If the user types in ./cprogram head -(option here) I would like for the option to appear but for some reason my code never enters the options switch statement. For example the command line code ./cprogram head -n never enters the case 'n': statement. The code was working before the if statement to check if the argv[1] is "head".

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    int b;
    int c;
    int nflag = 0;
    int Vflag = 0;
    int hflag = 0;
    int eflag = 0;
    int oflag = 0;
    char str[] = "head";
    if (strcmp(argv[1], str) == 0) {
        while ((c = getopt(argc, argv, "nVheo")) != -1) {
            switch (c) {
              case 'n':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    nflag++;
                    printf("n option\n");
                }
                break;
              case 'V':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    Vflag++;
                }
                break;
              case 'h':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    hflag++;
                }
                break;
              case 'e':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    eflag++;
                }
                break;
              case 'o':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    oflag++;
                }
                break;
              default:
                printf("invalid options\n");
                abort();
            }
        } 
    } else {
    }
}

I would greatly appreciate an expert eye to have a look and spot what I'm missing. Thanks in advance.

Upvotes: 3

Views: 749

Answers (2)

chqrlie
chqrlie

Reputation: 144780

For getopt() to skip argv[1] and parse options from the next element, you should set optind before calling getopt():

optind = 2;

Also note that you should also check if argc > 1 before comparing argv[1] with "head".

Here is a modified version:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
    int b;
    int c;
    int nflag = 0;
    int Vflag = 0;
    int hflag = 0;
    int eflag = 0;
    int oflag = 0;
    char str[] = "head";

    if (argc > 1 && strcmp(argv[1], str) == 0) {
        optind = 2;  // skip argv[1]
        while ((c = getopt(argc, argv, "nVheo")) != -1) {
            if (nflag | Vflag | hflag | eflag | oflag) {
                fprintf(stderr, "only one option\n");
                exit(1);
            }
            switch (c) {
              case 'n':
                nflag++;
                printf("n option\n");
                break;
              case 'V':
                Vflag++;
                break;
              case 'h':
                hflag++;
                break;
              case 'e':
                eflag++;
                break;
              case 'o':
                oflag++;
                break;
              default:
                fprintf(stderr, "invalid option `%c'\n", c);
                abort();
            }
        } 
        /* perform head on files starting at argv[optind] */
    } else {
        /* test some other command */
    }
}

Upvotes: 2

Craig Estey
Craig Estey

Reputation: 33601

Combining all the suggestions in the top comments:

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(int argc, char **argv)
{
    int b;
    int c;
    int nflag = 0;
    int Vflag = 0;
    int hflag = 0;
    int eflag = 0;
    int oflag = 0;
    char str[] = "head";

    if (argc < 2) {
        printf("not enough arguments\n");
        exit(1);
    }

    if (strcmp(argv[1], str) != 0) {
        printf("1st argument is not '%s'\n",str);
        exit(1);
    }

    // skip over the [matched] str
    --argc;
    ++argv;

    while ((c = getopt(argc, argv, "nVheo")) != -1) {
        if (nflag || Vflag || hflag || eflag || oflag) {
            printf("only one option\n");
            exit(1);
        }

        switch (c) {
        case 'n':
            nflag++;
            printf("n option\n");
            break;

        case 'V':
            Vflag++;
            break;

        case 'h':
            hflag++;
            break;

        case 'e':
            eflag++;
            break;

        case 'o':
            oflag++;
            break;

        default:
            printf("invalid options\n");
            abort();
            break;
        }
    }

    return 0;
}

To test, invoke with these [a shell script test]:

./myprogram
./myprogram foo
./myprogram head
./myprogram head -n
./myprogram head -n -n

Here is the sh -x ./test output:

+ ./myprogram
not enough arguments
+ ./myprogram foo
1st argument is not 'head'
+ ./myprogram head
+ ./myprogram head -n
n option
+ ./myprogram head -n -n
n option
only one option

UPDATE:

The above code is a bit of a "cheat". It eliminates the str (e.g. "head") by moving it into argv[0] which is the program name (e.g. ./myprogram). The actual program name gets trashed.

A more correct way is to "slide" argv to eliminate the string and preserve the program name:

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(int argc, char **argv)
{
    int b;
    int c;
    int nflag = 0;
    int Vflag = 0;
    int hflag = 0;
    int eflag = 0;
    int oflag = 0;
    char str[] = "head";

    if (argc < 2) {
        printf("not enough arguments\n");
        exit(1);
    }

    if (strcmp(argv[1], str) != 0) {
        printf("1st argument is not '%s'\n",str);
        exit(1);
    }

    // skip over the [matched] str
    for (int avidx = 1;  avidx < argc;  ++avidx)
        argv[avidx] = argv[avidx + 1];
    --argc;

    while ((c = getopt(argc, argv, "nVheo")) != -1) {
        if (nflag || Vflag || hflag || eflag || oflag) {
            printf("only one option\n");
            exit(1);
        }

        switch (c) {
        case 'n':
            nflag++;
            printf("n option\n");
            break;

        case 'V':
            Vflag++;
            break;

        case 'h':
            hflag++;
            break;

        case 'e':
            eflag++;
            break;

        case 'o':
            oflag++;
            break;

        default:
            printf("invalid options\n");
            abort();
            break;
        }
    }

    return 0;
}

Upvotes: 2

Related Questions