Ybarra
Ybarra

Reputation: 175

Parsing optional command line arguments in C

I have a program that takes in optional arguments. The necessary arguments are a file and integers (1 or more). The optional arguments are a mix of strings and integers.

So a correct input on the command line could be:

./main trace_file 8 12 # (only necessary arguments)

./main –n 3000000 –p page.txt trace_file 8 7 4 # (with optional arguments)

I need to get the integers after trace_file into an array. I'm having trouble figuring out how to do this when the optional arguments are enabled, because another integer is on the command line. A push in the right direction would be greatly appreciated, because I cannot figure out how to do this.

EDIT: so far, all I have for parsing the arguments is this:

for(j=2, k=0; j<argc; j++, k++) {
    shift += atoi(argv[j]);
    shiftArr[k] = 32 - shift;
    bitMaskArr[k] = (int)(pow(2, atoi(argv[j])) - 1) << (shiftArr[k]);
    entryCnt[k] = (int)pow(2, atoi(argv[j]));
}

But this will only work when no optional arguments are entered.

Upvotes: 7

Views: 7388

Answers (4)

Nav
Nav

Reputation: 1

If you can't use getopt() or some other function that does the hard work for you, then a possible strategy would be:

Create new variables myargc, myargv, and copy argc and argv into them.

Write functions that deal with paired arguments (like "-n 300000" or "-p page.txt". Pass myargc and myargv into these functions by reference. Each such function returns the value associated with the argument (e.g., 300000 or page.txt) if it was found, or an invalid value (e.g., -1 or "") if it wasn't. And in either case, the function removes both items from myargv and decrements myargc by 2.

If you also have arguments that are just individual flags, write functions for those that return boolean indicating whether the flag was found, remove the flag from myargv, and decrements myargc by 1. (You could deal with trace_file this way, even if it's not optional. I'm assuming that trace_file is just a flag, and not related to the 8 that follows it.)

Call the functions for each of your optional arguments first.

Upvotes: 0

Richard Schwartz
Richard Schwartz

Reputation: 14628

If you can't use getopt() or some other function that does the hard work for you, then a possible strategy would be:

  • Create new variables myargc, myargv, and copy argc and argv into them.

  • Write functions that deal with paired arguments (like "-n 300000" or "-p page.txt". Pass myargc and myargv into these functions by reference. Each such function returns the value associated with the argument (e.g., 300000 or page.txt) if it was found, or an invalid value (e.g., -1 or "") if it wasn't. And in either case, the function removes both items from myargv and decrements myargc by 2.

  • If you also have arguments that are just individual flags, write functions for those that return boolean indicating whether the flag was found, remove the flag from myargv, and decrements myargc by 1. (You could deal with trace_file this way, even if it's not optional. I'm assuming that trace_file is just a flag, and not related to the 8 that follows it.)

  • Call the functions for each of your optional arguments first. The stuff that's left in myargc and myargv after you've called them all should just be your required arguments, and you can process them as you normally would.

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 755026

I don't see any major problems if you use a reasonably POSIX-compliant version of getopt().

Source code (goo.c)

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

/*
   ./main trace_file 8 12 # (only necessary arguments)

   ./main –n 3000000 –p page.txt trace_file 8 7 4 # (with optional arguments)
 */

static void usage(const char *argv0)
{
    fprintf(stderr, "Usage: %s [-n number][-p pagefile] trace n1 n2 ...\n", argv0);
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
    int number = 0;
    char *pagefile = "default.txt";
    char *tracefile;
    int opt;

    while ((opt = getopt(argc, argv, "n:p:")) != -1)
    {
        switch (opt)
        {
        case 'p':
            pagefile = optarg;
            break;
        case 'n':
            number = atoi(optarg);
            break;
        default:
            usage(argv[0]);
        }
    }

    if (argc - optind < 3)
    {
        fprintf(stderr, "%s: too few arguments\n", argv[0]);
        usage(argv[0]);
    }

    tracefile = argv[optind++];
    printf("Trace file: %s\n", tracefile);
    printf("Page file:  %s\n", pagefile);
    printf("Multiplier: %d\n", number);
    for (int i = optind; i < argc; i++)
        printf("Processing number: %d (%s)\n", atoi(argv[i]), argv[i]);
    return 0;
}

Compilation

$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
>      -Wold-style-definition -Werror goo.c -o goo

Example runs

$ ./goo trace_file 8 12
Trace file: trace_file
Page file:  default.txt
Multiplier: 0
Processing number: 8 (8)
Processing number: 12 (12)
$ ./goo -n 3000000 -p page.txt trace_file 8 7 4
Trace file: trace_file
Page file:  page.txt
Multiplier: 3000000
Processing number: 8 (8)
Processing number: 7 (7)
Processing number: 4 (4)
$

Upvotes: 5

phil
phil

Reputation: 565

I've given this a bit of thought and there is no simple way for getopt to return multiple arguments. I did wonder about defining "y::",but have dismissed this. Options are

1) have 2 options y and Y use one for each int and a boolean flag to catch the exception where y is defined, but Y isn't.

2) leave complex options like y out of the getopt cycle. Once getopt has processed options and shuffled arguments. Pre process these arguments to capture the -y operands and code in the process arguments to skip -y and it's operands

3) it's more usual across *nix commands to provide multiple values as a single argument which itself is a comma separated list of values. You achieve this by adding "y:" to your getopt processing. The string it points too needs to be parsed into the 2 tokens A and B from string A,B

Upvotes: 0

Related Questions