Weird issue with getopt and strings - c

I'm trying to parse some arguments using getopt() like this:
char *fileName = "medit.db";
char c = ' ';
while((c = getopt(argc, argv, "f")) != -1){
switch(c){
case 'f':
fileName = optarg;
printf("%s\n\n", fileName);
break;
}
}
The thing is when I go to the command line and write
./server -f test
It just gives me a null result but if I write it like this
./server -ftest
All together it works just fine.
Any reason why this code wouldn't work like it intended?
EDIT: As an experiment I tried to put the colon like this f: It works as intended.. Can anyone explain this?

Just like getchar, getopt returns an int. Pretty much everything said there applies here too.
Prototype from getopt(3):
int getopt(int argc, char * const argv[], const char *optstring);
To declare f as requiring an argument:
If such a character is followed by a colon, the option requires an argument, so getopt() places a pointer to the following text in the same argv-element, or the text of the following argv-element, in optarg.
I.e. the format is:
int c = ' ';
while((c = getopt(argc, argv, "f:")) != -1) {
switch(c){
case 'f':
fileName = optarg;
printf("%s\n\n", fileName);
break;
}
}

You need to put two colons AND you need to stop assigning optarg to fileName when an arg is not provided:
getopt(argc, argv, "f::")
case 'f':
if (optarg)//CHECK FIRST
fileName = optarg;
printf("%s\n\n", fileName);
break;
optstring is a string containing the legitimate option characters. If
such a character is followed by a colon, the option requires an
argument, so getopt() places a pointer to the following text in the
same argv-element, or the text of the following argv-element, in
optarg. Two colons mean an option takes an optional arg;

Related

Please explain colons within optional arguments and optarg as seen in the code below

All of the code below makes sense with the exception of how it is that colons are being used and optarg function as seen under case 's' and 'f'. I've attempted to google what exactly each means but I get lost in the jungle of computer science language that confuses me.
while ((c = getopt(argc, argv, "Ss:f:")) != -1) { //ask about colons in office hours
switch (c)
{
case 'S':
should_print_file_size = 1;
break;
case 's':
min_file_size = atoi(optarg);
break;
case 'f':
substring = optarg;
break;
default:
printf("Please only use S, s, or f arguments.");
return -1;
}
}
Please help!
I am assuming you mean this getopt in the while:
while ((c = getopt(argc, argv, "Ss:f:")) != -1)
As #Kaylum pointed out in the comments, the getopt manual states the following syntax:
int getopt(int argc, char * const argv[],
const char *optstring);
Where optstring argument is described as:
optstring is a string containing the legitimate option characters. If
such a character is followed by a colon, the option requires an
argument, so getopt() places a pointer to the following text in the
same argv-element, or the text of the following argv-element, in
optarg
So you can see in the following two cases for s and f:
case 's':
min_file_size = atoi(optarg); // <--- case 's' Takes optarg
break;
case 'f':
substring = optarg; // <--- case'f' takes optarg
break;
and from the while you have s: and f:. So imagine it being stated as something like this:
s: <argument> and f: <argument>

Is my understanding of how this C program works correct?

in the following program:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
char *delivery = "";
int thick = 0;
int count = 0;
char ch;
for (int i = 0; i < argc; i++) {
fprintf(stdout, "Argv[%i] = %s\n", i, argv[i]); /* printing to understand (ptu) */
}
while ((ch = getopt(argc, argv, "d:t")) != -1)
switch (ch) {
case 'd':
fprintf(stdout, "Optind in case 'd': %i\n", optind);
delivery = optarg;
break;
case 't':
fprintf(stdout, "Optind in case 't': %i\n", optind);
thick = 1;
break;
default:
fprintf(stderr, "Unknown option: '%s'\n", optarg);
return 1;
}
fprintf(stdout, "Argc: %i\n", argc); /* ptu */
fprintf(stdout, "Argv: %p\n", argv); /* ptu */
argc -= optind;
argv += optind;
fprintf(stdout, "Optind: %i. Argc after subtraction: %i, Argv after increment: %p\n", optind, argc, argv);
if (thick)
fprintf(stdout, "Thick crust!\n");
if (delivery[0])
fprintf(stdout, "To be delivered %s\n", delivery);
fprintf(stdout, "Ingredients:\n");
for (count = 0; count < argc; count++)
fprintf(stdout, "%s\n", argv[count]);
return 0;
}
When I run the above program with arguments shown below I get the following output:
[u#h c]$ ./prog -t -d yesterday anchovies goatcheese pepperoni
Argv[0] = ./prog
Argv[1] = -t
Argv[2] = -d
Argv[3] = yesterday
Argv[4] = anchovies
Argv[5] = goatcheese
Argv[6] = pepperoni
Optind in case 't': 2
Optind in case 'd': 4
Argc: 7
Argv: 0x7ffebee8e498
Optind: 4. Argc after subtraction: 3, Argv index: 0x7ffebee8e4b8
Thick crust!
To be delivered yesterday
Ingredients:
anchovies
goatcheese
pepperoni
I would like to know if my understanding of what's going on under the hood is accurate, specifically for the argument parsing steps in the program. Apologies for not sharing a more minimal reprex, but in this case I probably couldn't. I wouldn't have spammed stackoverflow if I could show this to a friend who understood C. So, please bear with me. Here goes nothing:
Defined main to accept command-line (cl) parameters. This requires two parameters:
integer argc which will contain the number of cl parameters including the name of the program, in this case it is 7
array of strings (i.e. array of char pointers), each element of which will point to the memory address of the first element of each string literal (stored in the CONSTANT memory block) passed in as a cl parameter to the program.
for loop (self explanatory)
On each run of the while loop getopt() will parse the argv[] array and assigns the next matching character from the optstring "d:t" to character variable ch, until it runs out of options (no pun intended) which is when it will return -1 and the control will exit the while loop.
at each such pass optind (which is initiated at 1 presumably because argv[0] is the program name) will be incremented to contain the index of the next element to be processed in argv... So in case 't', optind = <index of "-d" i.e. 2>, and in case 'd', optind = <index of "anchovies" i.e. 4> (this is because getopt() realizes from the ":" after 'd' in the optstring that -d will be followed by its optarg on the command line, thus optind is incremented to "4" here instead of being "3")
after -t and -d yesterday are processed getopt() can't find anything else in argv[] which matches elements in the optstring; thus it returns -1 and we break out of the while loop. optind remains set to 4 because getopt didn't find anything else after '-d' from the optstring.
We now decrement optind's value "4" from argc to ensure we skip past the option arguments (which we have already parsed) to the remaining three non-option arguments. We also increment argv—which initially pointed to memory location of argv[0] i.e. "./prog"—by <optind * sizeof(char pointer on a 64-bit machine); i.e. 4 * 8> which is why argv now points 32 bytes ahead in memory: 0x7ffebee8e4b8 - 0x7ffebee8e498 == 0x20. In other words, argv[0] points to "anchovies"
We then print stuff depending on values of thick, delivery and loop through the remaining non-option arguments to print them as well...
Yes, your understanding is correct.
I would offer two notes:
"String literal" refers to a string that is actually defined within your program, as characters between "" delimiters. The "CONSTANT block" isn't a standard concept but I suppose you mean a block of read-only memory which is loaded from the binary, since that's where string literals normally reside. The strings which the argv pointers point to are not of this kind; they can't be, because they are not known when the binary is created. Instead, they are located in some unspecified region of memory, and you may modify them in place if you wish (though this is likely to make your code confusing); e.g. argv[0][3] = 'x' would be legal. (C17 standard 5.1.2.2.1 (2)).
Some people might likewise find it confusing to modify the values of argc and argv within main, and would suggest that you assign the modified values to some other variables instead:
int remaining_argc = argc - optind;
char **remaining_argv = argv + optind;

I was writing this code for a pizza delivery system. When run from the command prompt it displayed an error. How should I run it?

I am building this pizza program using command-line arguments and interface. It should return the ingredients from the arguments.
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
char *delivery = "";
int thick = 0;
int count = 0;
char ch;
while(ch = getopt(argc, argv, "d:t") != EOF)
switch(ch)
{
case 'd' :
delivery = optarg;
break;
case 't' :
thick = 1;
break;
default:
fprintf(stderr, "Invalid option : %s", optarg);
return 1;
}
argc -= optind;
argv += optind;
if(thick)
puts("thick crust");
if(delivery[0])
printf("To be delivered %s", delivery);
puts("ingredients :");
for(count = 0; count<argc; count++)
{
puts(argv[count]);
}
return 0;
}
When running this program using command prompt in windows as :
program -d now -t
it was returning an error :
invalid option: now
How should I run this program and why is this error showing?
You've made five mistakes (two important, three less so) and one of the important mistakes is masking the other.
while(ch = getopt(argc, argv, "d:t") != EOF)
Important mistake #1: The operator precedence of = is lower than !=, so this assigns the result of the comparison to ch, not the return value of getopt as you expected. Less important mistake #1: getopt returns −1 when it reaches the end of the options. EOF is not necessarily equal to −1.
You should have written
while ((ch = getopt(argc, argv, "d:t")) != -1)
Now, when invoked as you described, in the first iteration the value returned from getopt will be 'd', which is not equal to EOF (nor to −1), so the value assigned to ch will be the number 1 (also known as Control-A, or U+0001 START OF HEADING). This number is not equal to either 'd' or 't' (the C standard guarantees 0 < 'a' < 'b' < 'c' < 'd' < ... < 'z', so even if we don't assume ASCII, 1 could only ever be equal to 'a') so the default branch of the switch is taken, and we do this:
fprintf(stderr, "Invalid option : %s", optarg);
return 1;
And here is important mistake 2: you should print ch, not optarg, when you get an invalid option. ch is the option; optarg is the argument to the option, if any. If you had printed ch you would have realized that the value in ch was not what you expected it to be.
The other less important mistakes are
char ch;
This should be int ch; Like in a getchar loop, the value returned from getopt when it reaches the end of the arguments is outside the range of char.
fprintf(stderr, "Invalid option : %s", optarg);
printf("To be delivered %s", delivery);
Both of these need a \n at the end of the string to be printed.
In the original form of your question, your code was badly indented, but I suspect that was because you used 4 spaces for a single level of indentation and an 8-space-wide hard TAB for a second level; it is a long-standing bug in the Stack Overflow interface that this kind of code gets mangled when pasted into a question. So it's not your fault. (You should indent with spaces only, though.) The more serious style problem with your code is that some of your single-statement loops have curly braces around them and some of them don't. Whether or not one should put curly braces around single-statement blocks in C is one of the oldest holy wars; I personally think both sides are wrong, but never mind that; the important thing you should learn is, pick one style or the other and stick to it consistently throughout your code.

How to pass escape sequence like tab and newline char as command line argument in C programming getopt_long?

I almost reached end of my code, after a lot of search I didn't find solution no where, I want to supply escape sequence like '\t', '\n' to my program like how awk and perl program takes, and finally I want to use them as printf or sprintf format string
This is what I tried so far, please note I need to have variable delim and rs should be pointer.
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
int main (int argc, char **argv)
{
int c;
char *delim = ",", *rs = "\n\r";
while (1)
{
static struct option long_options[] =
{
{"delim", required_argument, 0, 'd'},
{"row_sep", required_argument, 0, 'r'},
{0, 0, 0, 0}
};
int option_index = 0;
c = getopt_long (argc, argv, "df",
long_options, &option_index);
if (c == -1)
break;
switch (c)
{
case 0:
if (long_options[option_index].flag != 0)
break;
printf ("option %s", long_options[option_index].name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
break;
case 'd':
delim = optarg;
break;
case 'r':
rs = optarg;
break;
case '?':
break;
default:
abort ();
}
}
/* Print any remaining command line arguments (not options). */
if (optind < argc)
{
printf ("non-option ARGV-elements: ");
while (optind < argc)
printf ("%s ", argv[optind++]);
putchar ('\n');
}
/* Test input argument */
printf("This is test%ssome text%s",delim,rs);
exit (0);
}
When I compile and execute I get output like this
$ gcc argument.c
$ ./a.out --delim="\t"
This is test\tsome text
$ ./a.out --delim="\t" --row_sep="\n"
This is test\tsome text\n
I expect it to print tab and newline instead of '\t' and '\n' as original
Kindly someone help me.
Some code somewhere has to translate backslash-t and backslash-n into tab and newline. You can have the shell do it (if it is Bash or supports ANSI C quoting):
./a.out --delim=$'\t'
./a.out --delim=$'\t' --row_sep=$'\n'
Or use the printf command (distinct from, though related to, the printf() function); this avoids using any Bashisms:
./a.out --delim="$(printf '\t')"
./a.out --delim="$(printf '\t')" --row_sep="$(printf '\n')"
Or, indeed, you can simply type the characters at the command line. Entering tab needs you to type Control-VControl-I to avoid filename completion.
$ ./a.out --delim='^V^I'
$ ./a.out --delim='^V^I' --row_sep='
> '
This is less clear, though; I'd use one of the previous two mechanisms in preference.
Or you can do it in your program. That's a little harder, but not much. I have a rather comprehensive function, cstrlit_chr() that does most of that job (it doesn't handle Unicode escapes like \u0123 or \U00012345), but it isn't standard and it is in a file that's 238 lines long with the comments and test code, etc (just over 100 non-blank, non-comment lines of C code in the function, which could be compressed a bit if I wanted to spend the time on it), so it's a bit big to add it here.
Can you please show me cstrlit_chr() example for the same?
The header documenting the interface says:
/* Convert C String Literal in (str..end] (excluding surrounding quotes) */
/* to string, returning length of string, or -1 if conversion error, or */
/* -2 if there is not enough room for the output */
extern int cstrlit_str(const char *str, const char *end, char *buffer, size_t buflen);
/* Convert C Character Literal in (str..end] (excluding surrounding quotes) */
/* to character, returning converted char or -1 if string is invalid. */
/* If non-null, eptr is set to first non-converted (or non-convertible) character */
extern int cstrlit_chr(const char *str, const char *end, char const ** const eptr);
/* Convert character to C Character Literal. */
/* buffer[0] = '\0' if there isn't enough room in buffer */
extern void chr_cstrlit(unsigned char c, char *buffer, size_t buflen);
/* Convert string to C String Literal */
extern void str_cstrlit(const char *str, char *buffer, size_t buflen);
So, cstrlit_chr() is one of a set of four functions. However, it is pretty easy to use:
const char *endptr;
int c = cstrlit_char(argv[i], argv[i]+strlen(argv[i]), &endptr);
If argv[i] contains backslash and t, then c will be assigned the value of '\t' (which is usually control-I or 9). If it contains backslash and n, then c will be assigned the value of '\n' (which is usually control-J or 10).
The value in endptr tells you what's the next character to be interpreted.

Pass multiple arguments in C command line

How am i able to pass multiple arguments in a C program like this, using different switches
program -d <argument1> -p <argument2>
I'm using getopt to enable me to pass arguments.
int main(int argc, char **argv)
{
while(1)
{
unsigned int c = getopt(argc, argv, "-dD:hHgGp:");
if( c == -1 ) break;
switch( c )
{
case 'D':
case 'd':
printf("\nd=");
strcpy(D,optarg);
printf(D);
break;
case 'g':
case 'G':
printf("g");
break;
case 'p':
printf("\nPath=");
strcpy(pathFile,optarg);
printf(pathFile);
break;
case 'H':
case 'h':
usage(); //For help
return 0;
default:
return 0;
}
}
}
EDIT: The code here is a dummy code which I use for testing. It returns the argument that is passed as a string.
Is it just a case of you forgetting the “:” after the “d” in getopt arguments?
unsigned int c = getopt(argc, argv, "-d:D:hHgGp:");
It seems rather odd to write this:
while (1)
{
unsigned int c = getopt(argc, argv, "-dD:hHgGp:");
if( c == -1 ) break;
The return value of getopt() is an int; why would you save it in an unsigned int?
int opt;
while ((opt = getopt(argc, argv, "-dD:hHgGp:")) != -1)
{
switch (opt)
{
case ...
}
}
If you're going to make options case-insensitive (not a good idea, IMO), then be consistent about it and handle P: too. Also, as first noted by kmkaplan's answer, you have D: and d being handled by the same switch; they should both be followed by a colon for sanity's sake: "-d:D:hHgGp:P:" would at least be self-consistent.
Also, under most circumstances, you don't need to copy the argument string (optarg) anywhere; you simply save a pointer to its current value in a convenient variable. If you do copy the argument string, you must check the length of the argument to ensure you are not overflowing buffers.
The first character of the option string is not normally a dash; it isn't a standard behaviour. The Mac OS X documentation for getopt() does note that it is a GNU extension and advises against ever starting an option string with a dash (and the option string should only contain a dash for backwards compatibility, not in new code — again, on Mac OS X or BSD). Under GNU getopt(), the leading dash means that non-option arguments are reported as if they were options. As long as you're aware that you're using a GNU getopt() extension, there's no harm in doing so.

Resources