getopt isn't working for one argument - c

This is just a simple program I wrote in order to get some practice with getopt, and structs.
typedef struct {
int age;
float body_fat;
} personal;
typedef struct {
const char *name;
personal specs;
} person;
int main(int argc, char *argv[])
{
char c;
person guy;
while((c = getopt(argc, argv, "n:a:b:")) != -1)
switch(c) {
case 'n':
guy.name = optarg;
break;
case 'a':
guy.specs.age = atoi(optarg);
break;
case 'b':
guy.specs.body_fat = atof(optarg);
break;
case '?':
if(optopt == 'a') {
printf("Missing age!\n");
} else if (optopt == 'b') {
printf("Missing body fat!\n");
} else if (optopt == 'n') {
printf("Missing name!\n");
} else {
printf("Incorrect arg!\n");
}
break;
default:
return 0;
}
printf("Name: %s\nAge: %i\nFat Percentage: %2.2f\n",
guy.name, guy.specs.age, guy.specs.body_fat);
return 0;
}
Everything works just fine except for the 'b' option. For some reason specifying that one doesn't change anything. It always returns as 0.0. I don't get why this would be if the other args work just fine.

Your example is missing the header files which would declare the corresponding prototypes. Adding these
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
makes it work for me. I also changed the type for c to int (a char will not hold a -1), and did a
memset(&guy, 0, sizeof(guy));
just to ensure it was a known value. Compiler warnings are your friend. I used this (a script named gcc-normal) to apply warnings:
#!/bin/sh
# $Id: gcc-normal,v 1.4 2014/03/01 12:44:54 tom Exp $
# these are my normal development-options
OPTS="-Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wconversion"
${ACTUAL_GCC:-gcc} $OPTS "$#"
Though the RCS-identifier is fairly recent, it is an old script which I use in builds.

Related

Command-line parsing: How to parse a string as an argument

I am attempting to parse a command line argument, which in turn will execute an associated case within a switch statement. When I parse an integer argument (as seen in the code below), the associated case executes correctly. When I attempt to parse a string such as "CPU", I do not get the correct output.
Functioning code (parsing an integer e.g. an argument of 4 gives athe correct output of hello):
#include <stdio.h>
int main(int argc, char *argv[]) {
char execution_mode = atoi (argv[1]);
switch (execution_mode)
{
case (4) :
printf("Hello");
getchar();
break;
case (8) :
printf("Goodbye");
getchar();
break;
default:
printf("Error! execution mode is not correct");
getchar();
break;
}
return 0;
}
My attempt at parsing a string e.g. the argumentCPU:
#include <stdio.h>
int main(int argc, char *argv[]) {
typedef enum MODE { CPU, OPENMP } MODE;
MODE execution_mode = (char)argv[1];
switch (execution_mode)
{
case (CPU) :
printf("Hello");
getchar();
break;
case (OPENMP) :
printf("Goodbye");
getchar();
break;
default:
printf("Error! execution mode is not correct");
getchar();
break;
}
return 0;
}
You cannot convert a string to an enumerate like this. What you're doing is just converting the pointer to the string to char. Which fails.
One alternative (besides comparing first argument with strcmp) to avoid this would be to give a character value to your enumerates:
typedef enum { CPU='C', OPENMP='O' } MODE;
and now you can pick the first letter of the first argument and convert it:
MODE execution_mode = (MODE)argv[1][0];
The letters must be of course all different. And check argc>1 to see if argv[1] is valid, of course
If you want full string match, you have no other choice than using strcmp:
const char *execution_mode = argv[1];
if (strcmp(execution_mode,"CPU")==0)
{
// do something
}
else if (strcmp(execution_mode,"OPENMP")==0)
{
// do something else
}
With the help of the users who have answered this question, I have found a working solution by using strcmp as seen below. I have also added some error checking to ensure enough arguments have been enterred on the command-line.
#include <stdio.h>
int main(int argc, char *argv[]) {
//Ensure there are enough arguments
if (argc < 2)
{
printf("Error: not enough arguments");
exit(1);
}
typedef enum MODE { CPU, OPENMP, CUDA, ALL } MODE;
MODE execution_mode = (MODE)argv[1];
//Compare string with command-line arguments
if (strcmp("CPU", execution_mode) == 0)
{
//selects CPU case
execution_mode = CPU;
}
else if (strcmp("OPENMP", execution_mode) == 0)
{
//selects OPENMP case
execution_mode = OPENMP;
}
else
{
printf("invalid arg");
}
//Switch statement
switch (execution_mode)
{
case (CPU) :
printf("CPU MODE SELECTED");
getchar();
break;
case (OPENMP) :
printf("OPENMP MODE SELECTED");
getchar();
break;
default:
printf("Error: execution mode is not correct");
getchar();
break;
}
return 0;
}

Using getopt with no arguments

By default I want the program will read input from stdin and send it's output to stdout.
The -f option will cause the
program to read the input from a text file and the -o option will cause the program to write the
output to a file instead of stdout.
The –c if specified will display the output in a CSV format (either in stdout or to a file depending if -o is specified).
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
int main(int argc, char * argv[]){
char opt;
char *filename_in, *filename_out;
int i, flagC=0,flagF=0,flagO=0;
while((opt = getopt(argc,argv,"cf:o:")) != -1){
switch(opt){
case 'c':
flagC=1;
break;
case 'o':
flagO=1;
filename_out = optarg;
break;
case 'f':
flagF=1;
filename_in = optarg;
openFile(filename_in);
break;
case '?':
printf("Opt ?");
return 0;
default:
printf("Opt Default");
return 0;
}
}
if((flagC==0) && (flagO==0) && (flagF==0)){
// puts("Please enter a string:");
}
for (i = optind; i < argc; i++)
printf("%s ", argv[i]); //prints anything without argument and space between
return 0;
}
Is there a better way to read input from stdin aside from checking each flag I created is set to 0?
char input[100];
if((flagC==0) && (flagO==0) && (flagF==0)){
// puts("Please enter a string:");
// scanf("%s", &input);
}

switch case for parsing options

I am writing a simple program which takes the arguments form the user and process them.
I have the arguments in the argv which is two dimensional array. But when i ran the program, i get the garbage value and the segmentation fault error. I have tried with using argc as terminating condition and it works. But i want to do it using the pointer only.
What am doing wrong with pointer here.
#include<stdio.h>
int main( int argc, char *argv[])
{
while (++(*argv))
{
if ( **argv == '-' )
{
switch (*argv[1])
{
default:
printf("Unknown option -%c\n\n", (*argv)[1]);
break;
case 'h':
printf("\n option h is found");
break;
case 'v':
printf("option V is found");
break;
case 'd':
printf("\n option d is found");
break;
}
}
printf("\n outside while : %s", *argv);
}
}
program run as:
./a.out -h -v -d
Thank you
If you want to iterate through program arguments looking for the terminating null pointer, your outer cycle should be
while (*++argv)
not the
while (++*argv) // <- incorrect!
that you have in your code.
Your switch expression is written incorrectly. While your intent is clear, your implementation ignores operator precedence.
This
switch (*argv[1]) { // <- incorrect!
should actually be
switch ((*argv)[1]) {
The previous if
if (**argv == '-')
is fine, but since it is equivalent to
if ((*argv)[0] == '-') // <- better
maybe you should rewrite it that way as well, just for consistency with switch.
Your ultimate problem is operator precedence. Don't try to be clever when it's unnecessary. The * operator does not work as you think it does. I've rewritten your code using [0] instead, and now it works:
#include <stdio.h>
int main(int argc, char *argv[])
{
while ((++argv)[0])
{
if (argv[0][0] == '-' )
{
switch (argv[0][1]) {
default:
printf("Unknown option -%c\n\n", argv[0][1]);
break;
case 'h':
printf("\n option h is found");
break;
case 'v':
printf("option V is found");
break;
case 'd':
printf("\n option d is found");
break;
}
}
printf("\n outside while : %s", argv[0]);
}
}
argv is an array of strings. argv[0] is the program name which in your case is a.out. Your options start from argv[1]. So you need to iterate argv from 1 to argc-1 to get the options.
Also see here: Parsing Program Arguments

How to use switch and case for strings

Based on an answer of the following question: How do I transform an IF statement with 2 variables onto a switch function using C?
I want to develop SWITCH and CASE macros to use it (like switch and case) for strings.
Some thing like that:
char buf[256];
SWITCH (buf) {
CASE ("abcdef"):
printf ("A1!\n");
BREAK;
CASE ("ghijkl"):
printf ("B1!\n");
BREAK;
DEFAULT:
printf ("D1!\n");
BREAK;
}
what could be SWITCH and CASE and BREAK and DEFAULT here ?
If you really want it, well, here it is:
#include <string.h>
#include <stdio.h>
const char *kludge;
#define evilmacro(line) label##line
#define fakelabel(line) evilmacro(line)
#define SWITCH(str) while((kludge = (str)))
#define CASE(str) if(strcmp(kludge, str) == 0) { fakelabel(__LINE__)
#define BREAK break; /* out of while loop */ }
#define DEFAULT if(1) { fakelabel(__LINE__)
int main(int argc, char *argv[]) {
SWITCH (argv[1]) {
CASE ("abcdef"):
printf ("A1!\n");
BREAK;
CASE ("ghijkl"):
printf ("B1!\n");
BREAK;
DEFAULT:
printf ("D1!\n");
BREAK;
}
return 0;
}
Getting rid of the unused labels is left as an exercise for the reader :)
EDIT: fwiw, what I would really do is table driven code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SWITCH(S) char *_S = S; if (0)
#define CASE(S) } else if (strcmp(_S, S) == 0) {switch(1) { case 1
#define BREAK }
#define DEFAULT } else {switch(1) { case 1
int main()
{
char buf[256];
printf("\nString - Enter your string: ");
scanf ("%s", buf);
SWITCH (buf) {
CASE ("abcdef"):
printf ("B1!\n");
BREAK;
CASE ("ghijkl"):
printf ("C1!\n");
BREAK;
DEFAULT:
printf ("D1!\n");
BREAK;
}
}

Strange out of scope variable

I was looking up the getopt command and I discovered that using the function seems to inexplicably produce another variable called optarg. You can see an example of this in the following program I swiped from Wikipedia:
#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <unistd.h> /* for getopt */
int main (int argc, char **argv) {
int c;
int digit_optind = 0;
int aopt = 0, bopt = 0;
char *copt = 0, *dopt = 0;
while ( (c = getopt(argc, argv, "abc:d:012")) != -1) {
int this_option_optind = optind ? optind : 1;
switch (c) {
case '0':
case '1':
case '2':
if (digit_optind != 0 && digit_optind != this_option_optind)
printf ("digits occur in two different argv-elements.\n");
digit_optind = this_option_optind;
printf ("option %c\n", c);
break;
case 'a':
printf ("option a\n");
aopt = 1;
break;
case 'b':
printf ("option b\n");
bopt = 1;
break;
case 'c':
printf ("option c with value '%s'\n", optarg);
copt = optarg;
break;
case 'd':
printf ("option d with value '%s'\n", optarg);
dopt = optarg;
break;
case '?':
break;
default:
printf ("?? getopt returned character code 0%o ??\n", c);
}
}
if (optind < argc) {
printf ("non-option ARGV-elements: ");
while (optind < argc)
printf ("%s ", argv[optind++]);
printf ("\n");
}
exit (0);
}
Notice that optarg is now being used seemingly without be declared or initialized. Maybe this is just a common feature in C that I am unaware of, but I have been googling for a few hours and I don't know the name of what I am looking for. Any explanations would be nice.
The term is "global variable". If you declare a variable outside of a function, it's accessible inside functions:
int i = 7;
int main()
{
printf("%d\n", i); // prints 7
return 0;
}
In the case of optarg, the unistd.h header declares it as a global char * variable with external linkage:
extern char *optarg;
(see http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html).
From the man page
GETOPT(3) BSD Library Functions Manual GETOPT(3)
NAME
getopt -- get option character from command line argument list
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include <unistd.h>
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;
int
getopt(int argc, char * const argv[], const char *optstring);
These variables are declared in the unistd.h header file.
For future reference, if you find names but don't know where they are defined or declared, only run the C pre-processor, which is responsible for #include, and then search for the term using grep or just more.
For example
gcc -E foo.c >foo.i
would put the result of the C pre-processor into foo.i
You could then have a look at the file using more (using / to search)
The file will have references to the include file which includes the definition or declartion.
For example,
more foo.i
then
/optarg
shows the line
extern char *optarg;
by scrolling upwards (or reverse search ?# ) I could find
# 414 "/usr/include/unistd.h" 3 4
optarg is declared in <unistd.h>.
— Variable: char * optarg
This variable is set by getopt to point at the value of the option argument, for those options that accept arguments.
I find this at the Using Getopt websit

Resources