Parsing options in the same way they are passed to main

J

Jeff Rodriguez

If main is prototyped as:
int main(int argc, char *argv[]);

You will end up with a bunch of arguments in *argv, and the number in argc. Now
what I want to do is emulate that same action on a string. Say for example I have:

char *command = "./blah --arg1 --arg2 123 -x --arg2=w00t"

How do I acheive the same effect as in main()?


Jeff
 
B

Ben Pfaff

Jeff Rodriguez said:
If main is prototyped as:
int main(int argc, char *argv[]);

You will end up with a bunch of arguments in *argv, and the number in
argc. Now what I want to do is emulate that same action on a
string. Say for example I have:

char *command = "./blah --arg1 --arg2 123 -x --arg2=w00t"

How do I acheive the same effect as in main()?

It depends on the operating system, shell, and other things. If
all you want to do is to break apart the string into words at
white space, I suggest you just write code to do it. It's not
too hard.
 
J

Jeff Rodriguez

Ben said:
It depends on the operating system, shell, and other things. If
all you want to do is to break apart the string into words at
white space, I suggest you just write code to do it. It's not
too hard.

Indeed: (crude code)
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i;
char *string = NULL;
char *str = NULL;
int size = 0;

for ( i = 1; i < argc; i++ )
{
if ( strlen(argv) > size )
{
if ( (string = realloc(string, (size + strlen(argv)) * 2)) == NULL )
{
perror("Could not allocate memory");
exit(1);
}
}

string = strcat(string, argv);
string = strcat(string, " ");
}

printf("%s %s\n", argv[0], string);

str = strtok(string, " \t\n");
printf("%s\n", str);
while ( (str = strtok(NULL, " \t\n")) != NULL )
{
printf("%s\n", str);
}
return 0;
}

However you run into problems like:
../a.out --search="Hello World!"

I would prefer not to have to reinvent the wheel on this since main() already
implements it! There must be /some/ way that it's a built-in function!

Jeff
 
J

Jack Klein

Indeed: (crude code)
#include <stdio.h>
#include <string.h>
#include <unistd.h>

The header above is both non-standard and unneeded in your program.
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i;
char *string = NULL;
char *str = NULL;
int size = 0;

for ( i = 1; i < argc; i++ )
{
if ( strlen(argv) > size )
{
if ( (string = realloc(string, (size + strlen(argv)) * 2)) == NULL )
{
perror("Could not allocate memory");
exit(1);
}
}

string = strcat(string, argv);


This is quite likely to crash the first time, since the first block
that realloc returns is uninitialized and there's no telling if it
will contain a terminating '\0' at all, let alone in the first
element.
string = strcat(string, " ");
}

printf("%s %s\n", argv[0], string);

str = strtok(string, " \t\n");
printf("%s\n", str);
while ( (str = strtok(NULL, " \t\n")) != NULL )
{
printf("%s\n", str);
}
return 0;
}

However you run into problems like:
./a.out --search="Hello World!"

I would prefer not to have to reinvent the wheel on this since main() already
implements it! There must be /some/ way that it's a built-in function!

Jeff

No, main() does not already implement it. Either the host operating
system or the implementation's start-up code does. There is no C
standard library function to do so.

If you want source code, try Google.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
 
B

Ben Pfaff

Jeff Rodriguez said:
I would prefer not to have to reinvent the wheel on this since main()
already implements it! There must be /some/ way that it's a built-in
function!

main() doesn't implement this on any system I've used. For
example, under MS-DOS, the arguments are passed to the program as
a single string, and the C library startup routine carves it up
and passes it to main(). As another example, under UNIX-like
systems, the shell does the parsing into arguments and the
program receives them from the OS already in that form. In
neither case is there an easy way for the program to access the
functionality.
 
K

Kevin Goodsell

Jeff said:
if ( (string = realloc(string, (size + strlen(argv)) * 2)) ==
NULL )
{
perror("Could not allocate memory");
exit(1);
}


Aside from a few other errors that have already been pointed out, I
don't think you should be calling perror without first checking the
value of errno. I don't know off the top if my head whether realloc is
required to set errno, but my guess is that it is not. If it doesn't,
you may end up getting something like this:

Could not allocate memory: No error

Or something even more confusing, if errno has previously been set by a
different function.

This is also a guaranteed memory leak if realloc fails. The correct way
to use realloc is to store the result into a temporary pointer, and only
assign it to the destination pointer if it is non-null.

Finally, 1 is not a portable exit code. Consider using one of the
portable codes: 0, EXIT_SUCCESS, or EXIT_FAILURE.

-Kevin
 
J

Jeff Rodriguez

Kevin said:
Jeff said:
if ( (string = realloc(string, (size + strlen(argv)) * 2)) ==
NULL )
{
perror("Could not allocate memory");
exit(1);
}



Aside from a few other errors that have already been pointed out, I
don't think you should be calling perror without first checking the
value of errno. I don't know off the top if my head whether realloc is
required to set errno, but my guess is that it is not. If it doesn't,
you may end up getting something like this:

Could not allocate memory: No error

Or something even more confusing, if errno has previously been set by a
different function.

This is also a guaranteed memory leak if realloc fails. The correct way
to use realloc is to store the result into a temporary pointer, and only
assign it to the destination pointer if it is non-null.

Finally, 1 is not a portable exit code. Consider using one of the
portable codes: 0, EXIT_SUCCESS, or EXIT_FAILURE.

-Kevin


Not that I typically do the above (note how I said crude code), but how would
this be a guaranteed memory leak? If realloc fails the program exits. I'm not
sure I understand how that would cause a memory leak.

Jeff
 
C

CBFalconer

Jeff said:
Indeed: (crude code)
#include <stdio.h>
#include <string.h>
#include <unistd.h>

*** Non-existent standard include ***
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i;
char *string = NULL;

*** Invading the systems name space. Use another id ***
char *str = NULL;
int size = 0;

for ( i = 1; i < argc; i++ )
{
if ( strlen(argv) > size )
{
if ( (string = realloc(string, (size + strlen(argv)) * 2)) == NULL )


*** Harmless in this context, but an evil habit ***
Try something like:
if (tmp = realloc(old, (size + strlen(argv)) *
2))
old = tmp;
else {
/* failure */
}
 
N

nrk

Jeff said:
If main is prototyped as:
int main(int argc, char *argv[]);

You will end up with a bunch of arguments in *argv, and the number in
argc. Now what I want to do is emulate that same action on a string. Say
for example I have:

char *command = "./blah --arg1 --arg2 123 -x --arg2=w00t"

How do I acheive the same effect as in main()?


Jeff

A simple hack is to use another C program that simply prints out its command
line arguments to a known file. Then invoke said program with the system
call:

/* assume that our argv printer is called cmdline
and is accessible through a "system" call */
char *command = "./cmdline ./blah --arg1 --arg2 123 -x --arg2=w00t";

system(command);

/* now read the file produced by cmdline to get
argv parsed version of command */

-nrk.
 
N

nrk

nrk said:
Jeff said:
If main is prototyped as:
int main(int argc, char *argv[]);

You will end up with a bunch of arguments in *argv, and the number in
argc. Now what I want to do is emulate that same action on a string. Say
for example I have:

char *command = "./blah --arg1 --arg2 123 -x --arg2=w00t"

How do I acheive the same effect as in main()?


Jeff

A simple hack is to use another C program that simply prints out its
command line arguments to a known file. Then invoke said program with the
system call:

/* assume that our argv printer is called cmdline
and is accessible through a "system" call */
char *command = "./cmdline ./blah --arg1 --arg2 123 -x
--arg2=w00t";

system(command);

/* now read the file produced by cmdline to get
argv parsed version of command */

-nrk.

Just in case you're wondering what an "argv printer" might look like :)

#include <stdio.h>

int main(int argc, char **argv) {
while ( *argv ) {
printf("%s\n", *argv);
++argv;
}

return 0;
}

If you want one that ignores argv[0]:

#include <stdio.h>

int main(int argc, char **argv) {
if ( argc )
while ( *++argv ) printf("%s\n", argv);

return 0;
}

-nrk.
 
K

Kevin Goodsell

Jeff said:
Kevin said:
Jeff said:
if ( (string = realloc(string, (size + strlen(argv)) * 2))
== NULL )
{


Not that I typically do the above (note how I said crude code), but how
would this be a guaranteed memory leak? If realloc fails the program
exits. I'm not sure I understand how that would cause a memory leak.


Any time you fail to free memory that you've [m|c|re]alloc'ed, it is a
memory leak. Using realloc in this manner guarantees that you will not
be able to free the memory you've already allocated once realloc fails.

Some people don't bother freeing memory when the program is exiting.
This is not a very good idea. Some systems reclaim memory when a program
exits, but not all.

Also, what happens when the code is used in a different program, where
exiting on an allocation failure isn't an option?

-Kevin
 
K

Kevin Goodsell

CBFalconer said:
*** Invading the systems name space. Use another id ***

I could be wrong, but I think 'str' identifiers are reserved for use as
external identifiers. Since this is local it might shadow an external
declaration, but I don't think it will conflict.

Of course, avoiding reserved identifiers completely (even where they are
safe) is probably the best thing to do.

-Kevin
 
F

Floyd Davidson

Kevin Goodsell said:
Some people don't bother freeing memory when the program is
exiting. This is not a very good idea. Some systems reclaim
memory when a program exits, but not all.

Can you cite an example of a system where it makes any difference
at all?
 
K

Kevin Goodsell

Floyd said:
Can you cite an example of a system where it makes any difference
at all?

Sure. A TI-89 or TI-92+ calculator using TI-GCC. If you fail to free
memory, you don't get it back until you reset the whole thing, wiping
the entire memory.

There are a lot of systems out there other than desktop systems.

-Kevin
 
P

Peter Shaggy Haywood

Groovy hepcat Jeff Rodriguez was jivin' on Fri, 12 Dec 2003 20:36:37
-0700 in comp.lang.c.
Parsing options in the same way they are passed to main's a cool
scene! Dig it!
If main is prototyped as:
int main(int argc, char *argv[]);

You will end up with a bunch of arguments in *argv, and the number in argc. Now
what I want to do is emulate that same action on a string. Say for example I have:

char *command = "./blah --arg1 --arg2 123 -x --arg2=w00t"

How do I acheive the same effect as in main()?

First of all, don't try to do this with a string literal; use an
array.
Use strtok() to get each token in turn, and add each (pointer
returned from strtok()) to an array of pointers to char which has been
dynamically allocated with realloc(), until (and including) strtok()
returns a null pointer. For example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char **gettoks(char *s, int *num)
{
char **tok = NULL, **tmp;
char *t;
int n = 0;

t = strtok(s, " \t\v\r\n");

while(t)
{
n++;

tmp = realloc(tok, n * sizeof *tmp);
if(!tmp)
{
free(tok);
return NULL;
}
tok = tmp;

tok[n - 1] = t;

t = strtok(NULL, " \t\v\r\n");
}

tmp = realloc(tok, (n + 1) * sizeof *tmp);
if(!tmp)
{
free(tok);
return NULL;
}
tok = tmp;

tok[n] = NULL;

*num = n;
return tok;
}

int main(void)
{
char command[] = "./blah --arg1 --arg2 123 -x --arg2=w00t";
char **argv;
int argc, i;

argv = gettoks(command, &argc);
if(!argv)
{
fprintf(stderr, "Memory allocation error.\n");
return EXIT_FAILURE;
}

printf("%d args found:\n", argc);

for(i = 0; i < argc; i++)
{
printf("\t%d: \"%s\"\n", i, argv);
}

free(argv);
return 0;
}

--

Dig the even newer still, yet more improved, sig!

http://alphalink.com.au/~phaywood/
"Ain't I'm a dog?" - Ronny Self, Ain't I'm a Dog, written by G. Sherry & W. Walker.
I know it's not "technically correct" English; but since when was rock & roll "technically correct"?
 
G

goose

Not that I typically do the above (note how I said crude code), but how would
this be a guaranteed memory leak? If realloc fails the program exits. I'm not
sure I understand how that would cause a memory leak.

there are a few ways:
1. the system it runs on may not guarantee that it cleans up after
processes.
2. the process that is currently running where realloc fails
could have been a forked-type process, and the other party
of the fork may have to end before the memory is released.
since there is no guarantee that the other process *will*
end, the memory is gone forever.
3. the error code of 1 ("exit (1)") may be interpreted by the
caller (OS) as "please restart from main without resetting
anything": the only portable codes to use are EXIT_FAILURE
and EXIT_SUCCESS.
:)

hth
goose,
 
D

Dave Thompson

I could be wrong, but I think 'str' identifiers are reserved for use as
external identifiers. Since this is local it might shadow an external
declaration, but I don't think it will conflict.
Standard library function names, plus the extension ranges beginning
with mem str wcs is to, are reserved as external names always, and as
file-scope identifiers *and macros* (unless #undef'ed) if the relevant
header is #include'd, and in this case string.h was.

That said, I would bet 'string' in particular will never be used, at
least not for a function, partly because it describes a thing not an
operation, and partly to avoid conflict with C++'s std::string.
Of course, avoiding reserved identifiers completely (even where they are
safe) is probably the best thing to do.
Concur.

- David.Thompson1 at worldnet.att.net
 
K

Keith Thompson

3. the error code of 1 ("exit (1)") may be interpreted by the
caller (OS) as "please restart from main without resetting
anything": the only portable codes to use are EXIT_FAILURE
and EXIT_SUCCESS.

And 0.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top