I want the put a sub-option in a string so that I can use it as a file name for reading a file:
char *nvalue = NULL;
char *dvalue = NULL;
char *input = NULL;
char inputfilename[] = "\"";
int ar;
int main(int argc, char *argv[])
{
while ((ar = getopt(argc, argv, "hn:d:i:")) != -1)
switch (ar)
{
case 'h':
printf("something");
break; /* added */
case 'n':
nvalue = optarg;
if (isdigit(nvalue))
stop = atoi(nvalue);
else
printf("something\n");
break; /* added */
case 'd':
dvalue = optarg;
if (!strcmp(dvalue, "FCFS") || !strcmp(dvalue, "SSTF") ||
!strcmp(dvalue, "C-SCAN") || !strcmp(dvalue, "LOOK"))
;
else
printf("Invalid type of disk scheduling policy entered.\n");
break; /* added */
case 'i':
input = optarg;
strcpy(inputfilename, optarg);
printf("Filename :%s\n", inputfilename);
break;
}
/* ... */
}
So on the command line if I input:
./foobar -i hello
then I should be able to read the file with:
FILE *file = fopen(inputfilename, "r" );
any suggestions? answers?
thanks!
There are a number of problems with your code. I'm ignoring the absence of header files (assuming your code uses the correct ones so all functions have a prototype in scope before use). I'm also reformatting your code ruthlessly but without further comment.
char *nvalue = NULL;
char *dvalue = NULL;
char *input = NULL;
char inputfilename[] = "\"";
This allocated an array of two bytes as inputfilename. I hate to think what's going to happen when you use it.
int ar;
There is every reason why this variable should be local to the main function and no reason visible for it to be a global variable. Unless you have a header declaring them, the other variables should also be static - assuming that you need to access their values outside of main() without a convenient way to pass them as locals. Avoid global variables whenever possible.
int main(int argc, char *argv[])
{
while ((ar = getopt(argc, argv, "hn:d:i:")) != -1)
{
switch (ar)
{
case 'h':
printf("something");
Oops; no break, so the code drops through to the case 'n': code. C is not Pascal.
case 'n':
nvalue = optarg;
if (isdigit(nvalue))
stop = atoi(nvalue);
You haven't shown a declaration for stop. Unless you really need the string, you can do without nvalue, avoiding a global variable, which is always desirable.
else
printf("something\n");
Another missing break; I'm not going to point it out again.
case 'd':
dvalue = optarg;
if (strcmp(dvalue, "FCFS") == 0 ||
strcmp(dvalue, "SSTF") == 0 ||
strcmp(dvalue, "C-SCAN") == 0 ||
strcmp(dvalue, "LOOK" == 0)
{
I'd suggest a comment such as /* Nothing - dvalue is OK */. Or inverting the condition using De Morgan's theorem:
if (strcmp(dvalue, "FCFS") != 0 &&
strcmp(dvalue, "SSTF") != 0 &&
strcmp(dvalue, "C-SCAN") != 0 &&
strcmp(dvalue, "LOOK" != 0)
You might even decide to encapsulate that test into a function which tests the value against each element of an array of codes.
}
else
printf("Invalid type of disk scheduling policy entered.\n");
It would be courteous to provide a list of the acceptable values - which suddenly becomes another reason for having an array of valid values which you can use to generate the list. Very often, error messages should be reported on stderr (using fprintf()) rather than stdout.
case 'i':
input = optarg;
This assignment is sufficient.
strcpy(inputfilename, optarg);
Unless the user types a single-character file name, you've just overflowed the inputfilename array. You really didn't need to make a copy of the argument unless you're going to modify the name (for example, to add or change the extension on the name).
//strcat(inputfilename,"\"");
printf("Filename :%s\n", inputfilename);
}
You didn't include a default clause. Since ar will be assigned the value ? when the user-provided option is not recognized, this is normally your cue to provide a simple usage message and exit.
Related
I have this c program which use getopt_long to parse - and -- options, but when i parse -- options the optarg does not work for the program. how can i assing the optarg value for the varibale if -- is parsed for the program.
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include "file_handle.c"
//#include "songs.c"
#include "getoptions.c"
//Mix_Music *music = NULL;
int main(int argc, char *argv[])
{
//init();
int f = 0;
int d = 0;
char *file = NULL;
char *directory = NULL;
const struct option long_options[] = {
/*
{"verbose",no_argument,&verbose_flag,1}
*/
{"help", no_argument, 0, 'h'},
{"file", required_argument, &f, 'f'},
{"directory", required_argument, &d, 'd'},
{0, 0, 0, 0}
};
int opt;
if (argc <= 1)
{
fprintf(stderr, "No arguments provided!\nTry 'cplay --help' for more information.\n");
}
else
{
while ((opt = getopt_long(argc, argv, "d:f:h", long_options, NULL)) != -1)
{
printf("optarg %s\n",optarg);
switch(opt){
case 'h':
//print_help();
break;
case 'f':
puts("test f");
file = optarg;
break;
case 'd':
directory = optarg;
break;
default:
break;
}
}
}
if(f && d){
printf("OPTION -f, --file and -d, --directory are given\n");
printf("%s\n",directory);
int valid = 0;
char response;
while(!valid)
{
printf("Enter one option [f\\d]: ");
scanf(" %c",&response);
if(response == 'd' || response == 'f'){
valid = 1;
}
else
printf("Wrong Input given press f for single file and d for directory option\n");
}
}
printf("%d\n %d",f,d);
}
Re: "the switch statement is only checking for the -h or -d or -f options."
From the GNU manual:
When getopt_long encounters a long option, it takes actions based on
the flag and val fields of the definition of that option. The option
name may be abbreviated as long as the abbreviation is unique.
If flag is a null pointer, then getopt_long returns the contents of
val to indicate which option it found.
If flag (The third member of the struct option) is NULL, then getopt_long returns the contents of val (the fourth member of struct option) to indicate which option it found.
So if the long option was --help, and the corresponding val member was assigned h, then getopt_long would return h.
Some remarks:
Typically, this condition:
if(f && d)
shouldn't be reached. Keep flags and check if the f flag is already given before assigning true to the d flag.
bool f_flag = false;
bool d_flag = false;
Then check for the f_flag:
...
case 'd':
if (f_flag) {
/* Both file and directory flags are
* present. Print usage message and exit.
*/
} else {
strcpy (directory, optarg);
break;
}
Given two pointers p and q, the statement:
p = q;
doesn't copy the contents of the memory pointed to by q to the contents of the memory pointed to by p. It copies the pointer values, such that both p and q now point to the same memory, and any change to the memory via p is reflected when q is used.
So, given:
file = optarg;
and
directory = optarg;
these statements copies the pointer values, not the pointed to memory. This could be a problem if a subsequent operation modified argv, because it would change the memory pointed to by optarg and file / directory too.
Instead, copy the memory to the pointers with strcpy():
strcpy (file, optarg);
strcpy (directory, optarg);
This is my first post on this site so please forgive any formatting errors.
I am coding a small program to get simple information from a file like amount of words, sorting it etc. However I started simply by using getopt since I want to use the command line to parse my commands. This is what I have so far.
#include <stdio.h>
#include <stdlib.h>
int value = 0;
int main(int argc, char **argv) {
extern char *optarg;
extern int optind;
int c, err = 0;
int cflag = 0, sflag = 0, fflag = 0;
char *substring;
// usage from instructions in case of error
static char usage[] = "usage: mywords [-cs] [-f substring] filename";
// only f flag requires additional input
while ((c = getopt(argc, argv, "csf:")) != -1)
switch (c) {
case 'c':
cflag = 1;
break;
case 's':
sflag = 1;
break;
case 'f':
fflag = 1;
printf("Before assigning to substring\n");
substring = optarg;
printf("After\n");
break;
case '?':
err = 1;
break;
}
if (fflag = 1) {
printf("%c ", &substring);
}
}
I will then input something like this to the command line after I run my makefile (which runs successfully with no errors):
mywords -f test
and ideally the output would be
test
The printf statements I have in the fflag are simple test prints to see if I was even reaching there. Thank you for your help in advance.
EDIT
It was fixed. David Collins had the right answer. Thank you so much everyone
I have noticed three issues in you code. (There may be other small issues but addressing the following should get you up and running I think.)
Testing for equality
You should use if (fflag == 1) instead of if (fflag = 1).
Pointer de-referencing
If you want to retrieve the value of the character pointed to by substring, you would use *substring instead of &substring.
But you probably don't want to do this. See below.
printf() format specifiers
When printing a string / character array, use the %s specifier instead of %c. (%c is for single characters). So you should have
if (fflag == 1) {
printf("%s ", substring);
}
Or - more simply - just
if (fflag) {
puts(substring)
}
since any non-zero value evaluates to true in C (and you initialize fflag to zero at the start of your program).
When I am writing a piece of code, say something like this:
int i_flag;
char *s;
while ((c = getopt (argc, argv, "i::o:")) != -1) // I know "i::" is a GNU extension
switch (c) {
case 'i':
i_flag = 1;
if (optarg != NULL)
str-i = optarg;
break;
case 'o':
id = strtol (optarg, &s, 0);
if (id < 0 || id > 5 || *s) {
fprintf(stderr, "Invalid ID: %s\n", optarg);
print_usage(); // this function exit()'s the program
}
break;
default:
print_usage();
}
My question is that I know optarg can be NULL in the 'i' case, but can it be NULL in the 'o' case? I think it should not be NULL at all, but it does not seem to be guaranteed in POSIX.
I have this question since a very smart static analyzer looks at if (optarg != NULL) and says "Oh, so optarg can be NULL and you didn't check it in case 'o'.
Update: Fix int i-flag;.
Yes, optarg will be non-null. If a argument is omitted for an option that requires one, getopt will return '?'.
I need help to display name to command line like this (I don't know how to explain) in C
$:Enter your name: Test
$:Test>
But when you continue press enter it still showing Test>
$:Test>
$:Test>
So how do we get argv[0] and do something like this (Sorry that I cannot explain probably)
Thank you
command line arguments are stored in char **argv, and there are argc of them.
int main(int argc, char **argv)
{
int i=0;
for(i=0; i< argc; i++)
printf("argument number %d = %s\n", i, argv[i]);
return 0;
}
argv[0] is the name of the program being executed, so argc is always at least == 1 ( or more)
If you had rather shell-like program in mind, maybe the following couldbe of use:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define BUFSIZE 64
int main() {
char prompt[BUFSIZE];
char command[BUFSIZE];
char *prefix = "$:";
char *suffix = ">";
printf("%s%s%s", prefix, "Enter your name:", suffix);
fgets(prompt, BUFSIZE, stdin);
prompt[strlen(prompt)-1] = '\0'; // get rid of the \n
while (true) {
printf("%s%s%s", prefix, prompt, suffix);
fgets(command, BUFSIZE, stdin);
if (strncmp(command,"Quit",4) == 0)
break;
}
return 0;
}
Whenever possible, you should use getopt() so that the order of your parameters doesn't matter. For example, suppose you wanted to take an integer parameter for the size, an integer for the mode of execution, and a toggle to indicate whether to run in "quiet mode" or not. Further suppose that "-h" should print help and exit. Code like this will do the trick. The "s:m:hq" string indicates that "-s" and "-m" provide parameters, but the other flags don't.
int main() {
// parse the command-line options
int opt;
int size = DEFAULT_SIZE, mode = DEFAULT_MODE, quiet = 0;
while ((opt = getopt(argc, argv, "s:m:hq")) != -1) {
switch (opt) {
case 's': size = atoi(optarg); break;
case 'm': mode = atoi(optarg); break;
case 'q': quiet = 1; break;
case 'h': usage(); return 0;
}
}
// rest of code goes here
}
Of course, you should add error checking in case optarg is null.
Also, if you're using C++, "string(optarg)" is an appropriate way for your case statement to set a std::string to hold a value that is stored as a char* in argv.
I'm trying to scan the command line for certain letters, symbols and values. I want to scan for "-w", a number, and "-s". I got a response in my last question, I was told to use getopt() and after a little bit of Googling, I think I might get it, but I'm not sure.
This is what I think I'm doing:
int c = 0;
int b = argv[2];
while((c = getopt(argc, argv, "-w", "-s", b))
I think I'm scanning argc for "-w", "-s" and the argv[2] value (which is the number). But I do not know if I am using it correctly?
Your while loop should be
while ((c = getopt(argc, argv, "w:s:"))
: the colon means it needs an argument. now convert optarg to an integer or whatever you wanted.
int number = 0;
int sflag = 0;
int opt;
while ((opt = getopt(argc, argv, "w:s")) != -1)
{
switch (opt)
{
case 's':
sflag = 1;
break;
case 'w':
number = atoi(optarg);
break;
default:
/* Report usage and exit? */
break;
}
}
The conversion on the number is lazy; you can do more a more careful job calling a function that calls strtol(), for instance. You might need to have a wflag variable that you set analogously to the sflag variable so that you can distinguish -w 0 from 'no -w option specified on the command line'.