Parsing Multiple Arguments with getopt()

65 Views Asked by At

I am working on a simple decimal to hex/binary calculator that will take command line arguments to specify which to convert to, -x for hex and -b for binary. I am using getopt() to parse in values.

I am having a couple issues namely how to work with multiple arguments after one option. My code (as of now) is as follows

int main(int argc, char *argv[]){  
    int opt;
    int num;
    while((opt = getopt(argc, argv, ":x:b:")) != -1){
        switch(opt){
            case 'x':
                num = atoi(optarg);
                dec_to_hex(num);
                break;
            case 'b':
                num = atoi(optarg);
                dec_to_bin(num);
                break;
            case'?':
                print_usage();
                break;
            case ':':
                print_usage();
                break;
            default:
                print_usage();
        }

    }
    return 0;
}

For example, if I call ./a.out -b 1234 4321 I want to call my dec_to_bin fucntion first one 1234, then on 4321. I would also like to print usage if no/incorrect options are given. If I call ./a.out 1234 or ./a.out -x -p 1234 it should print usage.

I have tried working with a while loop in the switch statement cases like this,

case 'x':
    while(optind < argc){
        num = atoi(argv[optind++]);
        dec_to_hex(num);
    }

But that still will only print the first conversion. I also thought the return of '?', ':' or defualt would cover my incorrect/no options given. I am pretty stumped and would love any help. Thanks!

1

There are 1 best solutions below

1
larsks On BEST ANSWER

Options conventionally only take zero or one arguments. For your code, you really want the option to set the mode in which your program operates; for that you don't need arguments to the options. After deciding on the operating mode, you can iterate over all the non-option arguments, performing the appropriate conversion.

Something like this:

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

#define CONV_DEC_TO_HEX 0
#define CONV_DEC_TO_BIN 1

char *prog_name;

void dec_to_hex(int num) {
    printf("0x%x\n", num);
}

void dec_to_bin(int num) {
    printf("0b%b\n", num);
}

void print_usage(FILE *out) {
    fprintf(out, "usage: %s [-xb] arg1 [...argn]\n", prog_name);
}

int main(int argc, char *argv[]){  
    int opt;
    int num;
    int conv = -1;

    prog_name = argv[0];

    while((opt = getopt(argc, argv, "hxb")) != -1){
        switch(opt){
            case 'x':
                conv = CONV_DEC_TO_HEX;
                break;
            case 'b':
                conv = CONV_DEC_TO_BIN;
                break;
            case 'h':
                print_usage(stdout);
                exit(0);
            default:
                print_usage(stderr);
                exit(2);
        }
    }

    if (conv == -1) {
        fprintf(stderr, "ERROR: please select one of -x or -b\n");
        print_usage(stderr);
        exit(2);
    }

    for (int i=optind; i<argc; i++) {
        int arg = atoi(argv[i]);
        switch(conv) {
            case CONV_DEC_TO_HEX:
                dec_to_hex(arg);
                break;
            case CONV_DEC_TO_BIN:
                dec_to_bin(arg);
                break;
        }
    }

    return 0;
}

With no arguments:

$ ./calc
ERROR: please select one of -x or -b
usage: ./calc [-xb] arg1 [...argn]

With invalid arguments:

$ ./calc -z
./calc: invalid option -- 'z'
usage: ./calc [-xb] arg1 [...argn]

With valid arguments:

$ ./calc -x 123 234 345
0x7b
0xea
0x159
$ ./calc -b 123 234 345
0b1111011
0b11101010
0b101011001

And like all good programs, asking for help (with ./calc -h) outputs to stdout instead of stderr.