Using getopt() and switch statements in C - c

I'm new to C and trying to use getopt combined with a switch statement. I think my problem is just a syntax one but I can't seem to figure it out. I need to run my program like this :
$/webserver -p 8080 -r /my/root/dir.
So I need to recognize the -p and get the 8080 and the recognize the -r and get the /my/root/dir. Currently my code finds the -p and then finds the -r and then detects an unknown option and exits. Here is the code. I have the puts() in there for testing.
#define USAGE_STRING "Usage: webserver -p <port> -r <rootdir>\n"
char *port;
char *rootdir;
// Process command line arguments. Uses GNU getopt() function.
void processargs(int argc, char **argv) {
int next_option;
do {
next_option = getopt(argc, argv, "pr:");
if (next_option != -1) {
switch (next_option)
{
case 'p': /* -p -- port option */
puts("p");
port = optarg;
printf("%s", port);
case 'r': // -r -- root directory option
puts("r");
rootdir = optarg;
printf("%s", rootdir);
default:
puts("unknown");
/* Unknown option detected. Print it to standard
output, and exit with exit code zero (normal termination). */
fprintf(stderr, USAGE_STRING);
exit(1);
}
}
} while (next_option != -1);
printf("%s", port);
if (port == NULL) {
fprintf(stderr, USAGE_STRING);
exit(1);
}
if (rootdir == NULL) {
puts("unknown");
fprintf(stderr, USAGE_STRING);
exit(1);
}
}
Currently when I run the above command I get this output (using the puts for testing).
p
r
unknown
Usage: webserver -p <port> -r <rootdir>
Thanks for any help!

I believe you want to use break at the end of each case, else execution falls through to the next case block.

A C switch statement requires a break at the end of each case, otherwise the code in the following case is executed. So this:
switch (next_option)
{
case 'p': /* -p -- port option */
puts("p");
port = optarg;
printf("%s", port);
case 'r': // -r -- root directory option
puts("r");
rootdir = optarg;
printf("%s", rootdir);
default:
puts("unknown");
/* Unknown option detected. Print it to standard output, and exit with exit code zero
(normal termination). */
fprintf(stderr, USAGE_STRING);
exit(1);
}
Must be changed to this:
switch (next_option)
{
case 'p': /* -p -- port option */
puts("p");
port = optarg;
printf("%s", port);
break;
case 'r': // -r -- root directory option
puts("r");
rootdir = optarg;
printf("%s", rootdir);
break;
default:
puts("unknown");
/* Unknown option detected. Print it to standard output, and exit with exit code zero
(normal termination). */
fprintf(stderr, USAGE_STRING);
exit(1);
}

First you should be using getopt_long() I believe getopt() is depreciated. You are also missing break in your switch statement. With out break each case beneath that one will be executed. While you could use a do-while loop it's simpler to just use a while loop.
Here's a simple command line parser you can build on.
static const char *optstring = "p:r:";
static struct option longopts[] =
{
{ "port", required_argument, NULL, 'p' },
{ "root", required_argument, NULL, 'r' },
{ NULL, 0, NULL, 0 }
};
int parseInput(int argc, char * const argv[])
{
int ch;
/* Main Loop ie the Parser */
while ((ch = getopt_long(argc, argv, optstring, longopts, NULL)) != -1)
{
switch(ch)
{
case 'p':
printf("arg: %s\n", optarg);
break;
case 'r':
printf("arg: %s\n", optarg);
break;
default:
printf("Unsupported option\n");
return -1;
}
}
return 0;
}

Related

C program help to open a file in Unix/Linux using getopt and command line argument

I am trying to work on my college assignment that required programming in C in Unix. I have to take command line arguments and open a file with the name passed as an argument. I've been trying to find help, but couldn't find any resources to help me understand how to parse the argument as a string and open the required file. I'm seeking examples or links that point me in the right direction.
I'm including the short piece of code where I'm trying to parse the options using getopt(). What am I doing wrong?
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(int argc[], char *argv[])
{
int option;
while(option = getopt(argc, argv, "hi:o:") != -1)
{
switch (option){
case 'h':
printf("Usage : -i [input file name]\n-o [output file name]");
break;
case 'i':
printf("\n Input file is: %s",argv[1]);
break;
case 'o':
printf("\n Output file is: %s",argv[2]);
break;
}
}
return 0;
}
I keep getting "Unrecognized command line option error". Also, when I try to include a text file, I believe that the error says that the option is being parsed as an int, but the argument is a string.
P.S: I'm not really looking get any direct answers here. I want the community to help me learn in the best possible way.
As mentioned in the comments, you should be using optarg. Here is an example that is pretty comprehensive:
/*
example of command line parsing via getopt
usage: getopt [-dmp] -f fname [-s sname] name [name ...]
Paul Krzyzanowski
*/
#include <stdio.h>
#include <stdlib.h>
int debug = 0;
int
main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
int c, err = 0;
int mflag=0, pflag=0, fflag=0;
char *sname = "default_sname", *fname;
static char usage[] = "usage: %s [-dmp] -f fname [-s sname] name [name ...]\n";
while ((c = getopt(argc, argv, "df:mps:")) != -1)
switch (c) {
case 'd':
debug = 1;
break;
case 'm':
mflag = 1;
break;
case 'p':
pflag = 1;
break;
case 'f':
fflag = 1;
fname = optarg;
break;
case 's':
sname = optarg;
break;
case '?':
err = 1;
break;
}
if (fflag == 0) { /* -f was mandatory */
fprintf(stderr, "%s: missing -f option\n", argv[0]);
fprintf(stderr, usage, argv[0]);
exit(1);
} else if ((optind+1) > argc) {
/* need at least one argument (change +1 to +2 for two, etc. as needeed) */
printf("optind = %d, argc=%d\n", optind, argc);
fprintf(stderr, "%s: missing name\n", argv[0]);
fprintf(stderr, usage, argv[0]);
exit(1);
} else if (err) {
fprintf(stderr, usage, argv[0]);
exit(1);
}
/* see what we have */
printf("debug = %d\n", debug);
printf("pflag = %d\n", pflag);
printf("mflag = %d\n", mflag);
printf("fname = \"%s\"\n", fname);
printf("sname = \"%s\"\n", sname);
if (optind < argc) /* these are the arguments after the command-line options */
for (; optind < argc; optind++)
printf("argument: \"%s\"\n", argv[optind]);
else {
printf("no arguments left to process\n");
}
exit(0);
}
This example and more information is found here.

Getopt() Error Checking

I am trying to make it so that getopt can handle certain flags, but I am having trouble implementing the error handling using getopt. I want to prevent duplicate flags such as '-s 1 -s 1', and having too many arguments for a flag '-s 1 2' both of these should exit the program.
int opt; //command flags
while((opt = getopt(argc, argv, "s:f:")) != -1){
switch(opt){
case 's':
printf("%s\n", optarg);
printf("%i\n", optind);
break;
case 'f':
printf("%s\n", optarg);
printf("%i\n", optind);
break;
default:
//unknown command flags
fprintf(stderr, "Usage: fred [ -s symbol-table-file ] [ -f fred-program-file ]\n");
return EXIT_FAILURE;
}
}
Too many arguments (e.g., program -s f1 -f f2 hello). Duplicate flags (e.g., program -s f1 -s f2). Both should exit the program
For each option, you may maintain a flag to see if the option has already been encountered or not. Duplicate options are detected with the help of these flags.
int c, sflag=0, fflag=0;
while( (c=getopt(argc, argv, "s:f:"))!=-1 )
{
switch(c)
{
case 's':
if(sflag!=0)
{
//fprintf(stderr, "\n dup s option");
return 1; //duplicate s option
}
sflag++;
//printf("\ns flag: %s", optarg);
break;
case 'f':
if(fflag!=0)
{
//fprintf(stderr, "\n dup f option");
return 1; //duplicate f option
}
fflag++;
//printf("\nf flag: %s", optarg);
break;
case '?':
if(isprint(optopt))
{
fprintf(stderr, "Unknown option %c", optopt);
}
else
{
fprintf(stderr, "\nUnknown option character x%x", optopt);
}
return 1;
}
}
optind will have the index of the next argv[] which is still to be examined. After all the options have been taken care of, in your program there should be exactly one non-option argument.
So, optind must be equal to argc-1 for a correct usage of the command.
if(optind != argc-1) //exactly one non-option argument is expected
{
fprintf(stderr, "\nInvalid number of arguments.");
return 1;
}

C: getopt(): option does not seem to have effect

I am trying out the following C code (saved in file called testgetopt.c):
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
void usage(char *s)
{
fprintf(stderr, "Usage: %s [-d] -i input-TIFF-file -o output-raw-file\n", s);
fprintf(stderr, "Note: -d indicates debugging option\n");
}
int main(int argc, char **argv) {
char c;
size_t i;
char *itext = NULL, *otext = NULL;
FILE *fout;
short debug = 0;
if ((c = getopt(argc, argv, "di:o:")) == EOF) {
usage(argv[0]);
exit(1);
}
while ((c = getopt(argc, argv, "di:o:")) != EOF) {
switch (c) {
case 'd':
debug = 1;
break;
case 'i':
itext = optarg;
break;
case 'o':
otext = optarg;
break;
case '?':
if (optopt == 'i' || optopt == 'o')
fprintf (stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
usage(argv[0]);
exit(1);
default:
fprintf(stderr, "Both -i and -o are needed\n");
usage(argv[0]);
exit(1);
}
}
printf("debug = %i\n", debug);
if (debug)
printf ("input file = %s, output file = %s\n", itext, otext);
return EXIT_SUCCESS;
}
I compile using:
gcc testgetopt.c
But regardless of whether I include -d or not, I always get debug set at 0. The program should have -d set debug at 1, 0 otherwise.
What is being done wrong?
Here are the examples:
Let us first try with no -d
./a.out -i in.dat -o out.dat
debug = 0
Let us next try with -d included.
./a.out -d -i in.dat -o out.dat
debug = 0
Thank you in advance for any help and suggestions!
The problem is that you are calling getopt as a sanity check and then not using its result when it succeeds. That means the first option is always discarded. You can easily verify this by putting -d as a non-first option:
$ ./a.out -i in.dat -o out.dat -d
debug = 1
input file = (null), output file = out.dat
You can see that the -d did take effect this time but the first argument, -i, is not set.
There are several ways to fix this. The recommended way (IMHO) is to remove the first getopt call. Then change the sanity check to verify the actual options. That is, there should be code after the getopt loop which verifies that all mandatory options have been provided.
For example:
int main(int argc, char **argv) {
char c;
size_t i;
char *itext = NULL, *otext = NULL;
FILE *fout;
short debug = 0;
/* REMOVED THIS BLOCK OF CODE
if ((c = getopt(argc, argv, "di:o:")) == EOF) {
usage(argv[0]);
exit(1);
}
*/
while ((c = getopt(argc, argv, "di:o:")) != EOF) {
switch (c) {
case 'd':
debug = 1;
break;
case 'i':
itext = optarg;
break;
case 'o':
otext = optarg;
break;
case '?':
if (optopt == 'i' || optopt == 'o')
fprintf (stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
usage(argv[0]);
exit(1);
default:
fprintf(stderr, "Both -i and -o are needed\n");
usage(argv[0]);
exit(1);
}
}
/* ADD SOME ARGUMENT SANITY CHECKS */
if (!itext || !otext) {
printf("Missing mandatory arguments\n");
exit(1);
}
printf("debug = %i\n", debug);
if (debug)
printf ("input file = %s, output file = %s\n", itext, otext);
return EXIT_SUCCESS;
}
Because you are calling getopt twice, you need to reset optind = 1; between the calls.

C programming with getopt(): giving command line flags criteria

I am beginning to teach myself C. I have run into a few bumps here and there but right now I am stumped by getOpt(). The main thing thats giving me trouble is when I try to make certain flags dependent on other flags. For example I want these to work:
./myfile -a -b -c blue
But none of the other options to work without -a. Thus ./myfile -b -c purple would be invalid. Is it possible for getopt to handle this kind of "flag dependent" criteria? How would I go about doing that? Secondly, lets say that no matter which flags are passed, along with it must be a colour.
So ./myfile -a -b green and ./myfile red are both valid. I understand that this all lies within the options parameter of getOpt() (which is currently set up to look something like this "abc"), but how would I make one parameter required for each instance without doing "a:b:c:" since this would not include the mandatory colour if no flags are passed.
Here's the example (from the manpage) of getopt:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int
main (int argc, char *argv[])
{
int flags, opt;
int nsecs, tfnd;
nsecs = 0;
tfnd = 0;
flags = 0;
while ((opt = getopt (argc, argv, "nt:")) != -1)
{
switch (opt)
{
case 'n':
flags = 1;
break;
case 't':
nsecs = atoi (optarg);
tfnd = 1;
break;
default: /* '?' */
fprintf (stderr, "Usage: %s [-t nsecs] [-n] name\n", argv[0]);
exit (EXIT_FAILURE);
}
}
printf ("flags=%d; tfnd=%d; optind=%d\n", flags, tfnd, optind);
if (optind >= argc)
{
fprintf (stderr, "Expected argument after options\n");
exit (EXIT_FAILURE);
}
printf ("name argument = %s\n", argv[optind]);
/* Other code omitted */
exit (EXIT_SUCCESS);
}
Note you'll need to add some declarations, and a main() function to get that to work.
You can see the example n above is a flag, and works like your b option. The t option above takes a parameter and works like your c option. If you want to have an a option that is also a flag, you would make the getopt parameter "abf:" (i.e. add an a in without a colon), and a stanza to the switch like this:
case 'a':
aflag = 1;
break;
having first set aflag to 0. At the end you would check for the illegal condition where other options were passed without aflag being set.
So all in all, it would look like this:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int
main (int argc, char *argv[])
{
int flags, opt;
int nsecs, tfnd;
int aflag;
nsecs = 0;
tfnd = 0;
flags = 0;
aflag = 0;
while ((opt = getopt (argc, argv, "ant:")) != -1)
{
switch (opt)
{
case 'a':
aflag = 1;
break;
case 'n':
flags = 1;
break;
case 't':
nsecs = atoi (optarg);
tfnd = 1;
break;
default: /* '?' */
fprintf (stderr, "Usage: %s [-t nsecs] [-n] name\n", argv[0]);
exit (EXIT_FAILURE);
}
}
printf ("flags=%d; tfnd=%d; optind=%d\n", flags, tfnd, optind);
if (optind >= argc)
{
fprintf (stderr, "Expected argument after options\n");
exit (EXIT_FAILURE);
}
if (!aflag && (flags || tfnd))
{
fprintf (stderr, "Must specify a flag to use n or t flag\n");
exit (EXIT_FAILURE);
}
printf ("name argument = %s\n", argv[optind]);
/* Other code omitted */
exit (EXIT_SUCCESS);
}

getopt_long problem

i have a problem with parsing arguments from a program that i'm writing, the code is below:
void parse_args(int argc, char** argv)
{
char ch;
int index = 0;
struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "port", required_argument, NULL, 'p' },
{ "stop", no_argument, NULL, 's' },
{ 0, 0, 0, 0 }
};
while ((ch = getopt_long(argc, argv, "hp:s", options, &index)) != -1) {
switch (ch) {
case 'h':
printf("Option h, or --help.\n");
break;
case 's':
printf("Option s, or --stop.\n");
break;
case 'p':
printf("Option p, or --port.\n");
if (optarg != NULL)
printf("the port is %s\n", optarg);
break;
case '?':
printf("I don't understand this option!!!\n");
case -1:
break;
default:
printf("Help will be printed very soon -:)\n");
}
}
}
When i run my program i got some strange output :
./Server -p 80
Option p, or --port.
the port is 80
./Server -po 80
Option p, or --port.
the port is o
./Server -por 80
Option p, or --port.
the port is or
./Server -hoho
Option h, or --help.
Server: invalid option -- o
I don't understand this option!!!
I think the confusion stems from a misunderstanding of long get opt. Essentially it will do partial string match only when you use the -- form. When you only use - it will fallback to standard parsing so -por 80 is matched as -p or 80 (as in, the option is -p and the argument is or). Try the same thing with --po and --por. As for help, try --he or--hel

Resources