Processing C command line arguments - c

How do I process multiple command line arguments like say in a package manager? I am giving a shot at writing a package manager and right now accepting the packages to install is giving me quite the headache. For example the user wants to install package x,y and z. Right now my code will send 3 different requests to a function. I would like to get all the package names in one go. So for example, the user wants to install package x,y and z, it would be processed and my code will send a request to a function stating that it needs x,y and z and the function will begin work immediately.
Here's my current implementation...
case 'S':
switch (argv[1][2]) {
case 'u':
id = 1;
alfred("system", "update", "", id);
break;
case 'r':
id = 1;
alfred("system", "reload", "", id);
break;
case 'i':
if (argc - 2 != 0) {
// Loop until packages are complete.
packages = 2; // Starting point of packages = argv[2][0]
srand(time(NULL)); // Seed for random number
id = rand(); // Generate random number for id
argc = argc - 2 + 1; // argc minus the number of packages and plus 1
/* This is a very inefficent loop! */
/* Must get all targets and feed it to alfred */
while (packages <= argc) {
alfred("system", "install", &argv[packages][0], id);
packages++;
}
} else {
printf("Unrecognized format. Execute alfred -h for more information.\n");
}
break;
default:
printf("Unrecognized format. Execute alfred -h for more information.\n");
break;
}
break;

You can also try libargtable.
I used it before to parse various arguments, it's very handy.

Have you try the getopt library?
http://www.gnu.org/software/libc/manual/html_node/Getopt.html

I think what you are looking for is getopt
the documentation says
The getopt() function parses the command-line arguments. Its arguments argc and argv are the argument count and
array as passed to the main() function on program invocation. An element of argv that starts with '-' (and is
not exactly "-" or "--") is an option element. The characters of this element (aside from the initial '-') are
option characters. If getopt() is called repeatedly, it returns successively each of the option characters from
each of the option elements.

Related

Dealing with non-option arguments in getopt before options are checked

I am writing a program and I want to get the non-option arguments before checking the flags.
For example, if the arguments are ./a.out -a -b 50 filename
I want to use filename and then do task_a() and/or task_b()depending on the flags. filename can be any argument in argv[]
int opt;
float value;
while ((opt = getopt(argc, argv, "ab:")) != -1)
case 'a':
task_a(filename);
break;
case 'b':
value = atof(optarg);
task_b(filename, value);
break;
So basically I need to extract filename before using it in my tasks. How can I implement this?
getopt will pass you the command line arguments in the order that it finds them on the command line. This is generally NOT the order that you want to process them.
Instead, have your program set its options first before performing the operations - so something like ( given #include <stdbool.h> ) :
// Set the defaults
bool doTaskA = false;
book doTaskB = false;
float brightness = -1;
int opt;
float value;
while ((opt = getopt(argc, argv, "ab:")) != -1) {
case 'a':
doTaskA = true;
break;
case 'b':
doTaskB = true;
brightness = atof(optarg);
break;
}
filename = ....,,
if (doTaskA) {
task_a(filename);
}
if (doTaskB) {
task_b(filename, brightness);
}
I chose the name “brightness” as a meaningful attribute name - naturally you should choose your own. As good practice, do NOT name your variables “optionA” or some such - variable names should describe what they ARE, not the specific implementation details.
Always imagine that your options can change letters over time (what is now option “a” might become option “R” in six months time) - such changes should not affect anything except the “getopt” arguments and “case” statements.
EDITED TO CHANGE :
I originally wrote this with “if (brightness >= 0.0)”, but decided that in general a separate doTaskB variable is cleaner, more explicit approach that does not limit the possible values of the argument.

What does "passing argument 2 of strcmp makes pointer from integer without a cast" error in C mean?

I am compiling my C code and am getting two errors:
warning:passing argument 2 of strcmp makes pointer from integer without a cast
and
warning: note: expected const char * but argument is of type int
This my main:
int main(int argc, char *argv[])
{
//check to make sure that the command line arguments are valid
if(argc!=3)
{
printf("invalid function call try again\n");
}
//else to choose the proper command
else
{
//reverse routine A
if(strcmp(argv[2],'a'||'A')==0) //line 138
{
reva(argv[1]);
}
//reverse routine B
else if(strcmp(argv[2],'b'||'B')==0) //line 143
{
revb(argv[1]);
}
//reverse routine C
else if(strcmp(argv[2],'c'||'C')==0) //line 148
{
revc(argv[1]);
}
//unacceptable command line argumant
else
{
printf("unacceptable command line argument for reverse routine try again:\n");
}
}
}
It means what it says. 'a'||'A' is an integer – specifically, it is the integer 1. The second argument of strcmp must be a string, not an integer.
You appear to intend to compare argv[2] to a and A. You need two different strcmp calls for that. Also, you need to use double, not single, quotes.
In "C", the '||' operator is a boolean 'or' operation, not a concatenation operation. Also, the use of apostrophes denotes a single character which basically 'char' type.
I think you want something like this (for line 148):
if (strcmp(argv[2], "C")==0 || (strcmp(argv[2], "c")==0) ...
or, if your C library supports it:
if (strcasecmp(argv[2], "C") == 0)
which is a case insensitive comparison.
I believe that your object here is to compare the command line argument (argv[2]) with the character (string) "C" or "c", ie you just if the user gave c or C at the command line.
The SO users have already offered an explanation. You need to use
(strcmp(argv[2], "C")==0 || (strcmp(argv[2], "c")==0)
in order to eliminate your warning.
However, this is not the optimal way to parse command line arguments in C. If your program is too complicated when parsing user input I would suggest to use the library "getopt". It is designed to help the user parse and analyze the input in a structured manner.
Here is a small code snipper
opt = getopt_long( argc, argv, optString, longOpts, &longIndex );
while( opt != -1 ) {
switch( opt ) {
case 'I':
globalArgs.noIndex = 1; /* true */
break;
case 'l':
globalArgs.langCode = optarg;
break;
case 'o':
globalArgs.outFileName = optarg;
break;
case 'v':
globalArgs.verbosity++;
break;
case 'h': /* fall-through is intentional */
case '?':
display_usage();
break;
case 0: /* long option without a short arg */
if( strcmp( "randomize", longOpts[longIndex].name ) == 0 ) {
globalArgs.randomized = 1;
}
break;
default:
/* You won't actually get here. */
break;
}
opt = getopt_long( argc, argv, optString, longOpts, amp;longIndex );
}
Please search some documents (or the linux man pages) for getopt and getopt_long. Here is an example from GNU.

Way to pass argv[] to CreateProcess()

My C Win32 application should allow passing a full command line for another program to start, e.g.
myapp.exe /foo /bar "C:\Program Files\Some\App.exe" arg1 "arg 2"
myapp.exe may look something like
int main(int argc, char**argv)
{
int i;
for (i=1; i<argc; ++i) {
if (!strcmp(argv[i], "/foo") {
// handle /foo
} else if (!strcmp(argv[i], "/bar") {
// handle /bar
} else {
// not an option => start of a child command line
break;
}
}
// run the command
STARTUPINFO si;
PROCESS_INFORMATION pi;
// customize the above...
// I want this, but there is no such API! :(
CreateProcessFromArgv(argv+i, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
// use startup info si for some operations on a process
// ...
}
I can think about some workarounds:
use GetCommandLine()
and find a substring corresponding to argv[i]
write something similar to ArgvToCommandLine() also mentioned in another SO question
Both of them lengthy and re-implement cumbersome windows command line parsing logic, which is already a part of CommandLineToArgvW().
Is there a "standard" solution for my problem? A standard (Win32, CRT, etc.) implementation of workarounds counts as a solution.
It's actually easier than you think.
1) There is an API, GetCommandLine() that will return you the whole string
myapp.exe /foo /bar "C:\Program Files\Some\App.exe" arg1 "arg 2"
2) CreateProcess() allows to specify the command line, so using it as
CreateProcess(NULL, "c:\\hello.exe arg1 arg2 etc", ....)
will do exactly what you need.
3) By parsing your command line, you can just find where the exe name starts, and pass that address to the CreateProcess() . It could be easily done with
char* cmd_pos = strstr(GetCommandLine(), argv[3]);
and finally: CreateProcess(NULL, strstr(GetCommandLine(), argv[i]), ...);
EDIT: now I see that you've already considered this option. If you're concerned about performance penalties, they are nothing comparing to process creation.
The only standard function which you not yet included in your question is PathGetArgs, but it do not so much. The functions PathQuoteSpaces and PathUnquoteSpaces can be also helpful. In my opinion the usage of CommandLineToArgvW in combination with the with GetCommandLineW is what you really need. The usage of UNICODE during the parsing of the command line is in my opinion mandatory if you want to have a general solution.
I solved it as follows: With your Visual Studio install you can find a copy of some of the standard code used to create the C library. In particular if you look in VC\crt\src\stdargv.c you will find the implementation of "wparse_cmdline" function which creates argc and argv from the result of GetCommandLineW API. I created an augmented version of this code which also created a "cmdv" array of pointers which pointed back into the original string at the place where each argv pointer begins. You can then act on argv arguments as you wish, and when you want to pass the "rest" on to CreateProcess you can just pass in cmdv[i] instead.
This solution has the advantages that is uses the exact same parsing code, still provides argv as usual, and allows you to pass on the original without needing to re-quote or re-escape it.
I have faced the same problems with you. The thing is, we don't need to parse the whole string, if we can separate the result of GetCommandLine(), then you can put them together afterwards.
According to Microsoft's documentation, you should consider only backslashes and quotes.
You can find their documents here.
And, then, you can call Solve to get the next parameter start point.
E.g.
"a b c" d e
First Part: "a b c"
Next Parameter Start: d e
I solved the examples in Microsoft documentation, so do worry about the compatibility.
By calling the Solve function recursively, you can get the whole argv array.
Here's the file test.c
#include <stdio.h>
extern char* Solve(char* p);
void showString(char *str)
{
char *end = Solve(str);
char *p = str;
printf("First Part: ");
while(p < end){
fputc(*p, stdout);
p++;
}
printf("\nNext Parameter Start: %s\n", p + 1);
}
int main(){
char str[] = "\"a b c\" d e";
char str2[] = "a\\\\b d\"e f\"g h";
char str3[] = "a\\\\\\\"b c d";
char str4[] = "a\\\\\\\\\"b c\" d e";
showString(str);
showString(str2);
showString(str3);
showString(str4);
return 0;
}
Running result are:
First Part: "a b c"
Next Parameter Start: d e
First Part: a\\b
Next Parameter Start: d"e f"g h
First Part: a\\\"b
Next Parameter Start: c d
First Part: a\\\\"b c"
Next Parameter Start: d e
Here's all the source code of Solve function, file findarg.c
/**
This is a FSM for quote recognization.
Status will be
1. Quoted. (STATUS_QUOTE)
2. Normal. (STATUS_NORMAL)
3. End. (STATUS_END)
Quoted can be ended with a " or \0
Normal can be ended with a " or space( ) or \0
Slashes
*/
#ifndef TRUE
#define TRUE 1
#endif
#define STATUS_END 0
#define STATUS_NORMAL 1
#define STATUS_QUOTE 2
typedef char * Pointer;
typedef int STATUS;
static void MoveSlashes(Pointer *p){
/*According to Microsoft's note, http://msdn.microsoft.com/en-us/library/17w5ykft.aspx */
/*Backslashes are interpreted literally, unless they immediately precede a double quotation mark.*/
/*Here we skip every backslashes, and those linked with quotes. because we don't need to parse it.*/
while (**p == '\\'){
(*p)++;
//You need always check the next element
//Skip \" as well.
if (**p == '\\' || **p == '"')
(*p)++;
}
}
/* Quoted can be ended with a " or \0 */
static STATUS SolveQuote(Pointer *p){
while (TRUE){
MoveSlashes(p);
if (**p == 0)
return STATUS_END;
if (**p == '"')
return STATUS_NORMAL;
(*p)++;
}
}
/* Normal can be ended with a " or space( ) or \0 */
static STATUS SolveNormal(Pointer *p){
while (TRUE){
MoveSlashes(p);
if (**p == 0)
return STATUS_END;
if (**p == '"')
return STATUS_QUOTE;
if (**p == ' ')
return STATUS_END;
(*p)++;
}
}
/*
Solve the problem and return the end pointer.
#param p The start pointer
#return The target pointer.
*/
Pointer Solve(Pointer p){
STATUS status = STATUS_NORMAL;
while (status != STATUS_END){
switch (status)
{
case STATUS_NORMAL:
status = SolveNormal(&p); break;
case STATUS_QUOTE:
status = SolveQuote(&p); break;
case STATUS_END:
default:
break;
}
//Move pointer to the next place.
if (status != STATUS_END)
p++;
}
return p;
}
I think it's actually harder than you think for a general case.
See What's up with the strange treatment of quotation marks and backslashes by CommandLineToArgvW.
It's ultimately up to the individual programs how they tokenize the command-line into an argv array (and even CommandLineToArgv in theory (and perhaps in practice, if what one of the comments said is true) could behave differently than the CRT when it initializes argv to main()), so there isn't even a standard set of esoteric rules that you can follow.
But anyway, the short answer is: no, there unfortunately is no easy/standard solution. You'll have to roll your own function to deal with quotes and backslashes and the like.

Extracting timings from a multidimensional array and writing to a file in c

I'm having trouble extracting the timings from a .srt (subtitle) file and writing it to another file called output.srt. When i run the following i get some funky stuff written onto the output file.
// where hr=hours,mn=minutes,sc=seconds,ms=mili seconds
#include <stdio.h>
#define LINES 50
#define CHARAC 80
int main(void){
FILE *in;
FILE *out;
char text[LINES][CHARAC];
char timings[LINES][CHARAC];
int i=0,lines=0,items=0;
int hr=0,mn=0,sc=0,ms=0,hr2=0,mn2=0,sc2=0,ms2=0;
in=fopen("file2.srt","r");
out=fopen("output.srt","w");
while (!feof(in)){
fgets(text[i],80,in);
items=sscanf(text[i],"%d:%d:%d,%d --> %d:%d:%d,%d ",&hr,&mn,&sc,&ms,&hr2,&mn2,&sc2,&ms2);
//------------------------------------->edited<----------------------------------
switch (items)
{
case 1: break;
case 8:
sprintf(timings[i],"%d:%d:%d,%d --> %d:%d:%d,%d",hr,mn,sc,ms,hr2,mn2,sc2,ms2);
break;
case 0: break;
}
//------------------------------------->edited<----------------------------------
++i;
}
lines=i;
for (int i=0;i<lines;i++){
fprintf(out,"%s\n",timings[i]);
}
fclose(in);
fclose(out);
return 0;
}
how do I go about extracting those first 10 timings?
If this is on windows (or MSDOS) the open modes need to be text:
in = fopen ("file2.srt", "rt");
out = fopen ("output.srt", "wt");
Secondly, the code isn't doing anything to react to the differently formatted lines. The first few data lines are:
1
00:00:30,909--> 00:00:32,775
Take a look at yourself.
2
00:00:34,066--> 00:00:37,681
Disconnect you from the seats,
lift yourself and take a look in the mirror.
So, naturally, the first sscanf isn't going to fill in most of the fields. Here's the output I got (for the corresponding lines):
1:0:0,0 --> 0:0:0,0
0:0:30,909 --> 0:0:32,775
0:0:30,909 --> 0:0:32,775
0:0:30,909 --> 0:0:32,775
2:0:30,909 --> 0:0:32,775
To fix this, you'll have to add logic which expects the proper number of elements, or at least reacts to them:
itms = sscanf(text[i],"%d:%d:%d,%d --> %d:%d:%d,%d ",&hr,&mn,&sc,&ms,&hr2,&mn2,&sc2,&ms2);
switch (itms)
{
case 1: // the first line
case 8: // the lines with the times
case 0: // the text lines
}
Edited to add a fixed version of your last edit:
#include <stdio.h>
#define LINES 50
#define CHARAC 80
int main(void){
FILE *in;
FILE *out;
char text[LINES][CHARAC];
char timings[LINES][CHARAC];
int i=0,lines=0,items=0;
int hr=0,mn=0,sc=0,ms=0,hr2=0,mn2=0,sc2=0,ms2=0;
in=fopen("file2.srt","rt");
out=fopen("output.srt","wt");
while (!feof(in))
{
if (!fgets(text[i],80,in))
break;
items = sscanf(text[i], "%d:%d:%d,%d --> %d:%d:%d,%d ", &hr,&mn,&sc,&ms,&hr2,&mn2,&sc2,&ms2);
switch (items)
{
case 1: break;
case 8:
sprintf(timings[i],"%d:%d:%d,%d --> %d:%d:%d,%d",hr,mn,sc,ms,hr2,mn2,sc2,ms2);
++i; // advance only when a valid line is seen
break;
case 0: break;
}
}
lines=i;
for (i=0; i<lines; i++){
fprintf(out,"%s\n",timings[i]);
}
fclose(in);
fclose(out);
return 0;
}
The first thing I notice is that you're not checking the result of the sscanf(). You should check the return code to make sure all eight items were scanned in, and only sprintf() to timings[] if the data was scanned correctly.
You need to check the return value from sscanf() to see if the line matched. I downloaded your sample file:
1
00:00:30,909--> 00:00:32,775
Take a look at yourself.
2
00:00:34,066--> 00:00:37,681
Disconnect you from the seats,
lift yourself and take a look in the mirror.
Only some of these lines will return 8 from sscanf() as the number of matches, so test for that. Of course, this will fail if text line matches that, too!
Better would be to look for a blank line (except for the first), then an integer on one line, then the timing. Match only if all three work.

getopt does not parse optional arguments to parameters

In C, getopt_long does not parse the optional arguments to command line parameters parameters.
When I run the program, the optional argument is not recognized like the example run below.
$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !
Here is the test code.
#include <stdio.h>
#include <getopt.h>
int main(int argc, char ** argv )
{
int getopt_ret, option_index;
static struct option long_options[] = {
{"praise", required_argument, 0, 'p'},
{"blame", optional_argument, 0, 'b'},
{0, 0, 0, 0} };
while (1) {
getopt_ret = getopt_long( argc, argv, "p:b::",
long_options, &option_index);
if (getopt_ret == -1) break;
switch(getopt_ret)
{
case 0: break;
case 'p':
printf("Kudos to %s\n", optarg); break;
case 'b':
printf("You suck ");
if (optarg)
printf (", %s!\n", optarg);
else
printf ("!\n", optarg);
break;
case '?':
printf("Unknown option\n"); break;
}
}
return 0;
}
Although not mentioned in glibc documentation or getopt man page, optional arguments to long style command line parameters require 'equals sign' (=). Space separating the optional argument from the parameter does not work.
An example run with the test code:
$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!
The man page certainly doesn't document it very well, but the source code helps a little.
Briefly: you're supposed to do something like the following (though this may be a little over-pedantic):
if( !optarg
&& optind < argc // make sure optind is valid
&& NULL != argv[optind] // make sure it's not a null string
&& '\0' != argv[optind][0] // ... or an empty string
&& '-' != argv[optind][0] // ... or another option
) {
// update optind so the next getopt_long invocation skips argv[optind]
my_optarg = argv[optind++];
}
/* ... */
From among the comments preceding _getopt_internal:
...
If getopt finds another option character, it returns that character,
updating optind and nextchar so that the next call to getopt can
resume the scan with the following option character or ARGV-element.
If there are no more option characters, getopt returns -1.
Then optind is the index in ARGV of the first ARGV-element
that is not an option. (The ARGV-elements have been permuted
so that those that are not options now come last.) <-- a note from me:
if the 3rd argument to getopt_long starts with a dash, argv will not
be permuted
...
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
so the following text in the same ARGV-element, or the text of the following
ARGV-element, is returned in optarg. Two colons mean an option that
wants an optional arg; if there is text in the current ARGV-element,
it is returned in optarg, otherwise optarg is set to zero.
...
... though you have to do some reading between the lines. The following does what you want:
#include <stdio.h>
#include <getopt.h>
int main(int argc, char* argv[] ) {
int getopt_ret;
int option_index;
static struct option long_options[] = {
{"praise", required_argument, 0, 'p'}
, {"blame", optional_argument, 0, 'b'}
, {0, 0, 0, 0}
};
while( -1 != ( getopt_ret = getopt_long( argc
, argv
, "p:b::"
, long_options
, &option_index) ) ) {
const char *tmp_optarg = optarg;
switch( getopt_ret ) {
case 0: break;
case 1:
// handle non-option arguments here if you put a `-`
// at the beginning of getopt_long's 3rd argument
break;
case 'p':
printf("Kudos to %s\n", optarg); break;
case 'b':
if( !optarg
&& NULL != argv[optind]
&& '-' != argv[optind][0] ) {
// This is what makes it work; if `optarg` isn't set
// and argv[optind] doesn't look like another option,
// then assume it's our parameter and overtly modify optind
// to compensate.
//
// I'm not terribly fond of how this is done in the getopt
// API, but if you look at the man page it documents the
// existence of `optarg`, `optind`, etc, and they're
// not marked const -- implying they expect and intend you
// to modify them if needed.
tmp_optarg = argv[optind++];
}
printf( "You suck" );
if (tmp_optarg) {
printf (", %s!\n", tmp_optarg);
} else {
printf ("!\n");
}
break;
case '?':
printf("Unknown option\n");
break;
default:
printf( "Unknown: getopt_ret == %d\n", getopt_ret );
break;
}
}
return 0;
}
I recently came across this issue myself. I arrived at a similar solution to the one Brian Vandenberg and Haystack suggested. But to improve readability and avoid code duplication, you can wrap it all up in a macro like below:
#define OPTIONAL_ARGUMENT_IS_PRESENT \
((optarg == NULL && optind < argc && argv[optind][0] != '-') \
? (bool) (optarg = argv[optind++]) \
: (optarg != NULL))
The macro can be used like this:
case 'o': // option with optional argument
if (OPTIONAL_ARGUMENT_IS_PRESENT)
{
// Handle is present
}
else
{
// Handle is not present
}
break;
If you are interested, you can read more about how this solution works in a blog post I wrote:
https://cfengine.com/blog/2021/optional-arguments-with-getopt-long/
This solution is tested and is – at the time of this writing – currently used in CFEngine.
I also ran into the same problem and came here. Then I realised this .
You don't have much of a use case of "optional_argument" . If an option is required you check from program logic, if an option is optional then you need not do anything because at getopt level all options are optional , they are not mandatory, so there is no use case of "optional_argument". Hope this helps.
ps: for the above example i think the correct options are
--praise --praise-name "name" --blame --blame-name "name"

Resources