Using getopt triggers all cases in one - c

I'm trying to use getopt in order to run two different functions depending on the arguments given.
int main(int argc, char **argv) {
int i;
int identify;
while ((identify = getopt(argc, argv, "c:")) != -1) {
switch (identify) {
case 'c':
for (i = 2; i < argc; i++) {
readerC(argv[i]);
}
default:
for (i = 2; i < argc; i++) {
reader(argv[i]);
}
}
}
return(0);
}
The purpose is that, if the command included "-c", then it would run the "readerC" function. If there were no arguments passed, then it would read the "reader" function.
I've revised the above function multiple times, but I can't seem to be able to run the "reader" function when running the command with no arguments (there is no output). Previously, putting in -c would run the readerC command as required, but after messing around with it, it now runs the readerC function followed by the reader function.
I tried changing "default:" to "case ':'" and "case '?'" but that hasn't worked either. Any help would be greatly appreciated :)

You forgot to add break statements. Without a break, control simply falls through to the next case. Here is the code with break statements inserted into the relevant places:
while ((identify = getopt(argc, argv, "c:")) != -1) {
for (i = 2; i < argc; i++) {
switch (identify) {
case 'c':
for (i = 2; i < argc; i++) {
readerC(argv[i]);
}
break;
default:
for (i = 2; i < argc; i++) {
reader(argv[i]);
}
break; /* not really needed but for completeness */
}
}
}
Also, it seems like you are using the same i for all nested loops. Are you sure that's what you want? You might also want to have a look at the optarg variable which points to the argument corresponding to the currently parsed option.
Maybe also read about how getopt works again because it seems like you haven't quite understood it. These loops you wrote are somewhat strange.

The sequence for a main() program handling options is often:
Initialization
Option parsing
Do the real work
You should process the options with getopt(), then process the file arguments. You also need to include break in your switch statement as C does not break automatically between cases.
Hence, you might end up with:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void reader(const char *file);
static void readerC(const char *file);
int main(int argc, char **argv)
{
void (*function)(const char *arg) = reader;
int opt;
while ((opt = getopt(argc, argv, "c")) != -1)
{
switch (opt)
{
case 'c':
function = readerC;
break;
default:
fprintf(stderr, "Unrecognized option %c\n", optopt);
exit(1);
}
}
if (optind == argc)
{
fprintf(stderr, "Usage: %s [-c] file ...\n", argv[0]);
exit(1);
}
for (int i = optind; i < argc; i++)
{
(*function)(argv[i]); // The old-fashioned way
// function(argv[i]); // The more modern way
}
return(0);
}
If you're not comfortable with function pointers yet, then you can use a flag instead:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void reader(const char *file);
static void readerC(const char *file);
int main(int argc, char **argv)
{
int cflag = 0;
int opt;
while ((opt = getopt(argc, argv, "c")) != -1)
{
switch (opt)
{
case 'c':
cflag = 1;
break;
default:
fprintf(stderr, "Unrecognized option %c\n", optopt);
exit(1);
}
}
if (optind == argc)
{
fprintf(stderr, "Usage: %s [-c] file ...\n", argv[0]);
exit(1);
}
for (int i = optind; i < argc; i++)
{
if (cflag)
readerC(argv[i]);
else
reader(argv[i]);
}
return(0);
}

Related

warning when compiling with make

I wrote a program that copies the standard entry into the stdout as well as into a file. The program works but I have a problem, I receive a warning when compiling with make:
warning: implicit declaration of function ‘isprint’ [-Wimplicit-function-declaration]
if (isprint(optopt))
^~~
code:
Although it's not a big deal, I'd like it to stop displaying this warning. What would be the problem? I would also like a review of the code, what could I improve?
The program behaves like the tee -a file command.
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
static int append_mode = 0;
int parse_args(int argc, char *argv[])
{
char c;
while ((c = getopt(argc, argv, "a")) != -1) {
switch (c) {
case 'a':
append_mode = 1;
break;
case '?':
if (isprint(optopt))
fprintf(stderr, "Unkonw option `-%c'.\n", optopt);
else
fprintf(stderr,
"Unknown option character `\\x%x'.\n", optopt);
return 1;
default:
abort();
break;
}
}
return 0;
}
int main(int argc, char *argv[])
{
char buf[100];
size_t len;
char *file_mode;
int i;
FILE *files[20];
int num_files;
if (parse_args(argc, argv)) {
return 1;
}
file_mode = (append_mode ? "a" : "w");
num_files = argc - optind;
if (num_files > 0) {
if (files == NULL) {
fprintf(stderr, "Unable to allocate file buffer space\n");
return 1;
}
/* go through file arguments and either open for writing
or append based on the -a flag */
for (i = optind; i < argc; i++) {
FILE *pFile = fopen(argv[i], file_mode);
if (pFile == NULL)
{
fprintf(stderr, "Unable to open file %s for mode %s",
argv[i], file_mode);
goto main_cleanup;
}
files[i - optind] = pFile; /* mind the offset */
}
}
FILE *not_stdin = fopen("tee.c", "r");
while ((len = fread(&buf[0], 1, sizeof(buf), not_stdin)) > 0) {
fwrite(&buf[0], 1, len, stdout);
for (i = 0; i < num_files; i++) {
fwrite(&buf[0], 1, len, files[i]);
}
}
main_cleanup:
if (num_files > 0) {
for (i = 0; i < num_files; i++) {
fclose(files[i]);
}
}
return 0;
}
Mostly this warning appears when you are trying to use a function without including its required header.
To use isprint() add #include <ctype.h> to your included headers.

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.

execve() returning error for any command

I have to make a custom shell as a school project and I'm hitting a wall with this:
int exec_shell(char **argv) //
{
if (execve(argv[0], (char **)argv , NULL)==-1) //if an error occurs
{
printf("Commande invalide : %s\n", argv[0]);
fflush(stdout);//vide le buffer
exit(0);
return -1;
}
return 0;
}
It's supposed to be very simple - you put in a command in string form and exec calls the said command.
However, it always returns an error.
What am I doing wrong?
Here is the single warning:
primitives.c:25:30: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
Step : 1
/* myecho.c */
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
int j;
for (j = 0; j < argc; j++)
printf("argv[%d]: %s\n", j, argv[j]);
exit(EXIT_SUCCESS);
}
Compile Program : gcc myecho.c -o myecho
step : 2
/* execve.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
char *newargv[] = { NULL, "hello", "world", NULL };
char *newenviron[] = { NULL };
if (argc != 2) {
fprintf(stderr, "Usage: %s <file-to-exec>\n", argv[0]);
exit(EXIT_FAILURE);
}
newargv[0] = argv[1];
if (execve(argv[1], newargv , newenviron) == -1) //if an error occurs
{
printf("Commande invalide : %s\n", argv[0]);
fflush(stdout);//vide le buffer
exit(0);
return -1;
}
}
compile program : gcc execve.c -o execve
step: 3 //Final call
./execve ./myecho
step: observe output.
To pass an empty environment either define and pass
char * env[] = { NULL };
as third parameter to execve() like this
execve(argv[0], argv, env)
or use a compound literal by doing
execve(argv[0], argv, (char*[]){NULL})
Also the members of the exec*() family of functions only return on error, so the surrounding code might look like this:
int main(void)
{
...
execve(argv[0], argv, (char*[]){NULL});
perror("execve() failed");
return EXIT_FAILURE; /* include stdlib.h to have this macro available */
}
[RESOLVED] Hey, I would like to thank you all for your answer. It helped me figure out better what was wrong and I found an answer here.
The main issue was the path not being correct.

I'm getting a really odd timing error, where something is executing before I believe it should

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
int c, n, E, b, s, v, t, opt, valid = 0;
char current = '\0';
char previous = '\0';
FILE *fp;
/* -n numbers lines
* -E appends a dollar sign to line ends
* -b numbers only non-blank lines
* -s squeezes multiple blank lines down to 1
* -v displays control chars, excluding tab
* -t includes tab in the above
* -e is the same as -E and -v
*/
int setFlags(int argc, char *argv[]) {
int op;
while ((op = getopt(argc, argv, "nEbsvte")) != -1) {
switch (op) {
case 'n': {
n = 1;
break;
} case 'E': {
E = 1;
break;
} case 'b': {
b = 1;
break;
} case 's': {
s = 1;
break;
} case 'v': {
v = 1;
break;
} case 't': {
t = 1;
break;
} case 'e': {
E = 1;
v = 1;
break;
} case '?': {
//fprintf(stderr, "Option `-%c` is not valid.\n", optopt);
return EXIT_FAILURE;
} default: {
abort();
}
}
}
opt = optind;
if(n == 1) {
b = 0;
}
return EXIT_SUCCESS;
}
int checkFile(char *path) {
if (access(path, R_OK) == 0) {
return EXIT_SUCCESS;
} else {
fprintf(stderr, "cat: %s: %s\n", argv[i], strerror(errno));
errno = 0;
return EXIT_FAILURE;
}
}
int doPrint(char *path) {
if (strcmp(path, "stdin") == 0) {
fp = stdin;
} else {
if (checkFile(path) == 1) {
return EXIT_FAILURE;
} else {
fp = fopen(path, "r");
}
}
while ((c = fgetc(fp)) != EOF) {
putchar(c);
}
fclose(fp);
return EXIT_SUCCESS;
}
int main (int argc, char *argv[]) {
if (setFlags(argc, argv) == 1) {
fprintf(stderr, "The program has terminated with an error.\n"
"An invalid option was specified.\n");
return EXIT_FAILURE;
} else {
if ((argc - opt) == 0) {
doPrint("stdin");
} else {
for(int i = opt; i < argc; i++) {
doPrint(argv[i]);
}
}
}
}
I'm getting a really crazy bug, where my program outputs the error line in checkFile, before it finishes writing the contents of the file (always one chat before the end).
It's driving me insane, and no matter where I move that piece of code, it doesn't work as intended.
I'm sure the answer is probably trivial, but it has me stumped. I'd even thrown in sleeps and various other things just before output finished, and it would throw the error, THEN sleep, THEN print the final character.
Any help?
When using printf, stdout output is buffered by default. This means it can be interleaved with other output, often from stderr. stderr is unbuffered by default so that it's output is printed immediately as would normally be desired when an error occurs.
Interleaving can be fixed with judicious use of fflush or by turning off file buffering of stdout using setbuf. Be sure to read the man pages for setbuf as there are some caveats.
In this case, adding fflush(stdout) at the end of the doPrint function should fix the "problem".

Pass arguments into C program from command line

So I'm in Linux and I want to have a program accept arguments when you execute it from the command line.
For example,
./myprogram 42 -b -s
So then the program would store that number 42 as an int and execute certain parts of code depending on what arguments it gets like -b or -s.
You could use getopt.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main (int argc, char **argv)
{
int bflag = 0;
int sflag = 0;
int index;
int c;
opterr = 0;
while ((c = getopt (argc, argv, "bs")) != -1)
switch (c)
{
case 'b':
bflag = 1;
break;
case 's':
sflag = 1;
break;
case '?':
if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr,
"Unknown option character `\\x%x'.\n",
optopt);
return 1;
default:
abort ();
}
printf ("bflag = %d, sflag = %d\n", bflag, sflag);
for (index = optind; index < argc; index++)
printf ("Non-option argument %s\n", argv[index]);
return 0;
}
In C, this is done using arguments passed to your main() function:
int main(int argc, char *argv[])
{
int i = 0;
for (i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
More information can be found online such as this Arguments to main article.
Consider using getopt_long(). It allows both short and long options in any combination.
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
/* Flag set by `--verbose'. */
static int verbose_flag;
int
main (int argc, char *argv[])
{
while (1)
{
static struct option long_options[] =
{
/* This option set a flag. */
{"verbose", no_argument, &verbose_flag, 1},
/* These options don't set a flag.
We distinguish them by their indices. */
{"blip", no_argument, 0, 'b'},
{"slip", no_argument, 0, 's'},
{0, 0, 0, 0}
};
/* getopt_long stores the option index here. */
int option_index = 0;
int c = getopt_long (argc, argv, "bs",
long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
break;
switch (c)
{
case 0:
/* If this option set a flag, do nothing else now. */
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 'b':
puts ("option -b\n");
break;
case 's':
puts ("option -s\n");
break;
case '?':
/* getopt_long already printed an error message. */
break;
default:
abort ();
}
}
if (verbose_flag)
puts ("verbose flag is set");
/* 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');
}
return 0;
}
Related:
Which command line commands style do you prefer?
What is the general syntax of a Unix shell command?
Take a look at the getopt library; it's pretty much the gold standard for this sort of thing.
Instead of getopt(), you may also consider using argp_parse() (an alternative interface to the same library).
From libc manual:
getopt is more standard (the
short-option only version of it is a
part of the POSIX standard), but using
argp_parse is often easier, both for
very simple and very complex option
structures, because it does more of
the dirty work for you.
But I was always happy with the standard getopt.
N.B. GNU getopt with getopt_long is GNU LGPL.
Other have hit this one on the head:
the standard arguments to main(int argc, char **argv) give you direct access to the command line (after it has been mangled and tokenized by the shell)
there are very standard facility to parse the command line: getopt() and getopt_long()
but as you've seen the code to use them is a bit wordy, and quite idomatic. I generally push it out of view with something like:
typedef
struct options_struct {
int some_flag;
int other_flage;
char *use_file;
} opt_t;
/* Parses the command line and fills the options structure,
* returns non-zero on error */
int parse_options(opt_t *opts, int argc, char **argv);
Then first thing in main:
int main(int argc, char **argv){
opt_t opts;
if (parse_options(&opts,argc,argv)){
...
}
...
}
Or you could use one of the solutions suggested in Argument-parsing helpers for C/UNIX.

Resources