I'm writing a program which needs to be able to parse command line arguments, and I would like to use getopt. I have no issues with getting it to work with the regular arguments, however I need it to be able to pick up an argument that is not specified with a flag. For example if I ran: ./prog -a a1 -b b2 foo I would need to be able to get a1,a2 and foo. Right now it handles everything but the unspecified argument. Here is what I have:
while((command = getopt(argc, argv, "a:b:c:")) != -1){
switch(command){
case('a'):
input = fopen(optarg, "r");
if(input == NULL){
printf("Error opening file, exiting\n");
exit( -1 );
}
break;
case('b'):
output = fopen(optarg, "r");
if(output == NULL){
printf("Error opening file, exiting\n");
exit( -1 );
}
break;
case('c'):
keyword = optarg;
break;
case('?'):
if((optopt == 'a') || (optopt == 'b') || (optopt == 'c')){
fprintf(stderr, "Error, no argument specified for -%c\n", optopt);
exit( -1 );
} else
extra = optarg; // This is how I thought I needed to do it
break;
default:
fprintf(stderr,"Error in getopt");
break;
}// switch
} // while
Thanks!
After the loop, the optind variable will be the index to the next non-option argument.
So do e.g.
if (optind < argc)
{
printf("Have extra arguments: ");
for (int i = optind; i < argc; ++i)
printf("%s ", argv[i]);
printf("\n");
}
to list all non-option arguments.
Related
I have a task and I need your advice
I run my program with arguments, like
./program.x input.txt output.txt
So in my program I check that I use properly arguments
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n",argv[0]);
exit(1);
}
if( (wz= fopen(argv[1],"r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if( (wc= fopen(argv[2],"w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
I also use assert to check that file is ok, they told us that we must use assert too
assert((wz = fopen(argv[1] ,"r")));
assert((wc = fopen(argv[2] ,"w")));
But I dont know I should first put assert, or first checking number of arguments?
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n",argv[0]);
exit(1);
}
if( (wz= fopen(argv[1],"r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if( (wc= fopen(argv[2],"w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
assert((wz = fopen(argv[1] ,"r")));
assert((wc = fopen(argv[2] ,"w")));
or
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n",argv[0]);
exit(1);
}
if( (wz= fopen(argv[1],"r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if( (wc= fopen(argv[2],"w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
assert((wz = fopen(argv[1] ,"r")));
assert((wc = fopen(argv[2] ,"w")));
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n",argv[0]);
exit(1);
}
if( (wz= fopen(argv[1],"r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if( (wc= fopen(argv[2],"w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
assert(expression) is a macro defined by #include <assert.h>.
If the NDEBUG macro is defined before the #include <assert.h> line then the macro call assert(expression) expands to the void expression ((void)0). Otherwise, it will write an error message to the standard error stream and call abort().
Because assert(expression) might not generate any code, it should not be called if the expression has any side effects. One example of a side effect is assigning a value to a variable (or other lvalue). Another example of a side effect is opening file. The call assert((wz = fopen(argv[1] ,"r"))); does both of those things. The correct way to use assert in this case, is to do perform the operation before the assert call and only use assert to check the result. For example:
wz = fopen(argv[1], "r");
assert(wz != NULL);
The expression wz != NULL has no side effects (assuming wz has not been declared with the volatile type qualifier), so is safe.
OP is using assert as part of an exercise. It should be noted that assert is typically used only to check things that are not expected to fail, and the message it writes to the standard error stream is meant to help the developer discover bugs in the code. However, calling fopen on a command line argument is something that is quite likely to fail, and should normally be handled in a way that is useful to the user of the program, rather than the developer.
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.
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;
}
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.
Something goes wrong into the while loop but I really don't get it why.It was supposed to print the encrypted or decrypted result of a file into an output file with the argument (progname.exe -e input.txt)
The problem is that there is no output file created with the encrypted or decrypted code
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int c;
FILE *fp;
FILE *output;
if (argc < 2) {
exit(EXIT_FAILURE);
} else if ((fp = fopen(argv[1], "r")) == NULL) {
exit(EXIT_FAILURE);
}
if (strncmp (argv[1], "-d", 2) == 0) {
output = fopen (argv[5], "w+");
while (fscanf(fp,"%x",&c) != EOF) {
c = c >> 2;
c = c-200;
printf("%c", c);
}
}
if (strncmp (argv[1], "-e", 2) == 0) {
output = fopen (argv[5], "w+");
while ((c = getc(fp)) != EOF) {
c = c+200;
c = c << 2;
printf("%04x ", c);
}
}
putchar('\n');
return EXIT_SUCCESS;
}
The order/number of program parameters doesn't seem to make sense.
The program seems to expect the input file as it's first parameters (argv[1]):
} else if ((fp = fopen(argv[1], "r")) == NULL) {
exit(EXIT_FAILURE);
}
If that first parameter isn't an existing file, the program will exit without writing any output.
Later the same parameter is compared to "-d" and "-e" to check for decryption/encryption:
if (strncmp (argv[1], "-d", 2) == 0) {
Probably you meant to use the program parameter after the input file name, argv[2]? The program won't write any output file if that parameter isn't -d of -e.
Additionally the fifth parameter argv[5] is taken as the output file name. If that isn't a valid file name, no output file will be created either.
The problem in you code mostly is improper use of command line arguments.
The argc contains the argument count so if you have ls -a -l file then the argc will contain value 4, with this information you can parse the command line arguments inside your program using switch case easily based on the number of arguments passed, alternatively you can look into the command line parser libraries.
getopt() / getopt_long() (#include <unistd.h> from the POSIX C Library), which can solve simple argument parsing tasks, the getopt built-in of bash is based on Getopt from the GNU libc.
Argp (#include <argp.h> from the GNU C Library).
Here is an example with switch case using you code,
it can take command line arguments line.
Output to file:
prog.exe -e input.txt output.txt
prog.exe -d output.txt input2.txt
Output to stdout:
prog.exe -e input.txt
prog.exe -d output.txt
for other arguments it will print an invalid argument error and returns.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
int c;
FILE *input = NULL;
FILE *output = NULL;
int encryption = 1; // Flag to set encryption/decryption.
switch ( argc ) {
case 3:
if ( strchr(argv[1],'-') != NULL ) {
if ( argv[1][1] == 'e' ) encryption = 1;
else if ( argv[1][1] == 'd' ) encryption = 0;
else {
fprintf(stderr,"Error: invalid Option %s\n",argv[1]);
exit(EXIT_FAILURE);
}
if ((input = fopen( argv[2], "r")) == NULL) {
fprintf(stderr,"Error: Cannot Open %s\n",argv[2]);
exit(EXIT_FAILURE);
}
output = stdout;
} else {
fprintf(stderr,"Error: Invalid arguments\n");
exit(EXIT_FAILURE);
}
break;
case 4:
if ( strchr(argv[1],'-') == NULL ||
strchr(argv[2],'-') != NULL ||
strchr(argv[3],'-') != NULL ) {
fprintf(stderr,"Error: invalid argument\n");
exit(EXIT_FAILURE);
} else {
if ( argv[1][1] == 'e' ) encryption = 1;
else if ( argv[1][1] == 'd' ) encryption = 0;
else {
fprintf(stderr,"Error: invalid Option %s\n",argv[1]);
exit(EXIT_FAILURE);
}
if ((input = fopen( argv[2], "r")) == NULL) {
fprintf(stderr,"Error: Cannot Open %s\n",argv[1]);
exit(EXIT_FAILURE);
}
if ((output = fopen( argv[3], "w+")) == NULL) {
fprintf(stderr,"Error: Cannot Open %s\n",argv[3]);
exit(EXIT_FAILURE);
}
}
break;
default:
fprintf(stderr,"Error: Invalid arguments\n");
exit(EXIT_FAILURE);
}
// Do encryption and decryption based on the flag.
if ( encryption ) {
while ( fscanf(input,"%c",(char*)&c) == 1 ) { // Do encryption
c = c + 200;
c = c << 2;
fprintf(output,"%x ", c);
}
} else {
while ( fscanf(input,"%x ",&c) == 1 ) { // Do decryption
c = c >> 2;
c = c - 200;
fprintf(output,"%c", c);
}
}
return EXIT_SUCCESS;
}