How to invoke the help function with optparse-c - c

I am trying to use the optparse-c library. This library seems to me quite useful. What I find not
so clear is how to invoke the help without calling prog -h.
I can do it if I remove the keyword static from the function signature static void printopts(FILE *f) in
optparse.c, then I could do:
int main(int argc, char **argv)
{
int i;
opt_basename(argv[0], '/');
if (opt_parse("usage: %s [options] arg1 arg2", options, argv) != 2) {
printopts(stderr);
exit(EXIT_FAILURE);
}
...
However, it seems to me the author of this library had some other idea how to invoke the help function. I would be happy if someone could help me figure this out.
I also tried this:
char h[] = "-h";
opt_basename(argv[0], '/');
if (opt_parse("usage: %s [options] arg1 arg2", options, argv) != 2) {
argc++;
strcpy(argv[1], h);
opt_parse("usage: %s [options] arg1 arg2", options, argv);
exit(EXIT_FAILURE);
}

Oh well, after a lot of poking and looking into the source code, I found out the not trivial (as I am still learning C) solution:
if (opt_parse("usage: %s [options] arg1 arg2", options, argv) != 2) {
opt_help(0, (void *)0);
exit(EXIT_FAILURE);
}

Related

Implementing parsing with the ls command in C

I'm trying to implement the ls command in C with a few parameters like -a, -l... or -la, but I'm having issues with the parsing, when I use the input I get Segmentation Fault, this is an example of the -a parameter:
int comparator(char *av) {
int i = 0;
if (my_strcmp((av[i]), "-a") == 0)
return 0;
else
return 1;
}
int my_ls_a(char *path) {
int comp = comparator(path);
DIR *pdirec = opendir(".");
struct dirent *direc;
direc = readdir(pdirec);
while (direc != NULL || comp == 0) {
my_printf("%s ", direc->d_name);
direc = readdir(pdirec);
}
if ((path = readdir(pdirec)) == NULL)
my_printf("\n");
if (pdirec == NULL)
return (84);
closedir(pdirec);
return (0);
}
And this is my main:
int main(int ac, char *av[]) {
if (ac == 1)
my_ls_a(av[0]);
return 0;
}
I already have all the #include in a .h by the way.
When I only use the main function it works but not when I add the parameter -a.
It's probably better to use getopt() for parameter parsing instead of writing your own parser.
You have undefined behavior in the function comparator in my_strcmp((av[i]), "-a") because av is defined as a char * so you are passing a character where my_strcmp probably expects a pointer.
You should compile with -Wall -Werror to avoid such silly mistakes.
It is unclear why you pass only a single argument to my_ls_a. You should pass both ac and the argument array av and iterate on the arguments to parse the options.

CUnit testing void functions

Hi Im new to Cunit tests and testing in general. I cant find a way to test this void functon. Wondering how to properly do it or how to refactor it.
Any tips on how to properly test void functions? Thanks.
void validate_arg(int argc, char *argv[], struct Statistics *stats) {
if (argc == 4 && strcmp(argv[2], "-G") == 0) {
FILE *file = fopen(argv[3], "w");
stop_prog(write_stats(file, stats), err1);
fclose(file);
}
free(stats);
}
The Cunit test I wrote but I cant assert anything since its a void and I cant test if my file to write was created since since it gets deallocated inside the if.
void test_validate_args_0(void) {
struct Stats *stats = malloc(sizeof(struct Stats));
char* argv[] = {"TEST", "dummy", "-G", NULL};
CU_ASSERT_EQUAL(validate_arg(4, argv, stats), 0);
free(stats);
}

Using the TCL interpreter repeatedly inside a C program

I wish to use a c program repeatedly run the TCL interpreter many times. For reasons that are complicated, I need this to be a pure C program and not something that is embedded as a shared object. As an example, I wish to run this simple tcl program, tryMe.tcl, twice:
prtstr "Test from tryMe.tcl"
The prtstr is a TCL function that I have written that for right now just writes to stdout. Below is the c code that is attempting to interpret the tryMe.tcl program twice.
I compile the program below like this under linux:
$ gcc -c try.c; gcc -o try try.o -ltcl;
and run it like this:
$ ./try tryMe.tcl
And I get zero output. What am I doing wrong? Also are there steps required to reset the tcl interpreter so that it will be fresh each time.
#define _GNU_SOURCE
#include <tcl/tcl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int PrintStrObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *str;
int len;
Tcl_Obj *objPtr;
int i;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "value");
return TCL_ERROR;
}
objPtr = objv[1];
str = Tcl_GetStringFromObj(objPtr, &len);
if (str[0] == '\0')
return TCL_ERROR;
printf("len: %d, str: %s\n", len, str);
return TCL_OK;
}
int Tcl_AppInit(Tcl_Interp* interp)
{
if (Tcl_Init(interp) == TCL_ERROR)
return TCL_ERROR;
Tcl_CreateObjCommand(interp,"prtstr", PrintStrObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}
int main(int argc, char *argv[])
{
char *cmd = NULL;
Tcl_Interp * interp = Tcl_CreateInterp();
Tcl_AppInit(interp);
asprintf(&cmd, "%s -x -y -z", argv[1]);
Tcl_Eval(interp, cmd);
free(cmd);
asprintf(&cmd, "%s -q -r -s 2", argv[1]);
Tcl_Eval(interp, cmd);
exit(0);
}
Thanks very much!
You should check out the Tcler's Wiki on this, as the pattern of embedding a Tcl interpreter in your application is a known and supported one. It includes a worked example that you can adapt (and no, I didn't write it; I prefer to extend standard Tcl interpreters).
The main problem you've got is that you're not calling Tcl_FindExecutable(). In modern Tcl, that initialises a number of key subsystems in the library (including its high-performance memory allocator!) so it's slightly vital. In your case, you've got a real argv available so you can use argv[0] with it:
Tcl_FindExecutable(argv[0]);
// NULL would also work as an argument, in a pinch at least
Once you've done that, you can call other Tcl API functions, specifically Tcl_CreateInterp().
You have a minor problem in that you are not testing the results of calls for failure. In C, this is essential as you don't have exceptions to do the heavy lifting of error handling for you.
Thanks for the pointer to TCLer's Wiki. That helped. I didn't understand that the script in Tcl_Eval(interp,script) was not a file name but a character string containing a tcl program. So this program uses TCL_Evalfile() I also wanted to be able to pass command line arguments to the tcl program. I found out how but diving into the TCL source for Tcl_MainEx(). Below is a program that does what I want. Also I discovered that calling Tcl_EvalFile more than one does retains the state so if I want a fresh value of the interpreter, I'll have to delete the old one and create a new one everytime.
#include <tcl/tcl.h>
#include <stdio.h>
#include <stdlib.h>
int PrintStrObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *str;
int len;
Tcl_Obj *objPtr;
int i;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "value");
return TCL_ERROR;
}
objPtr = objv[1];
str = Tcl_GetStringFromObj(objPtr, &len);
if (str[0] == '\0')
return TCL_ERROR;
printf("len: %d, str: %s\n", len, str);
return TCL_OK;
}
int Tcl_AppInit(Tcl_Interp* interp)
{
if (Tcl_Init(interp) == TCL_ERROR)
return TCL_ERROR;
Tcl_CreateObjCommand(interp,"prtstr", PrintStrObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}
int main(int argc, char **argv)
{
char *script = argv[1];
Tcl_Obj *argvPtr;
Tcl_FindExecutable(script);
Tcl_Interp *interp = Tcl_CreateInterp();
if (interp == NULL) {
fprintf(stderr,"Cannot create TCL interpreter\n");
exit(-1);
}
if (Tcl_AppInit(interp) != TCL_OK)
return TCL_ERROR;
Tcl_SetVar2Ex(interp, "argv0", NULL, Tcl_NewStringObj(script,-1), TCL_GLOBAL_ONLY);
argc -= 2;
argv += 2;
Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewIntObj(argc), TCL_GLOBAL_ONLY);
argvPtr = Tcl_NewListObj(0, NULL);
while (argc--)
Tcl_ListObjAppendElement(NULL, argvPtr, Tcl_NewStringObj(*argv++, -1));
Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY);
if (Tcl_EvalFile(interp, script) != TCL_OK)
return TCL_ERROR;
exit(0);
}

Define custom command-line parameters in C?

I need to pass a 'key' as a parameter from terminal. It should run as ./a.out -k100101001
where 10011001 is the key and -k is the flag to specify it.
If I need to pass a filename along with the key it should run as ./a.out -k10011001 -iparameter.txt where parameter.txt is the filename and -i is the flag to specify that.
NOTE: I have several parameters to pass and parameter values are followed after the flag without space(-iparameter.txt), plus I don't know the order of the flags so doing something like this won't help.
int main(int argc, char **argv) {
if (argc == 2) {
if (!strcmp(argv[1], "-k")) something();
if (!strcmp(argv[1], "-i")) something();
}
Any suggestion for C? I'm using Ubuntu to run my program.
Thanks.
Using a loop through argv should do it.
int main(int argc, char *argv[])
{
int numArg;
for (numArg = 1; numArg < argc; numArg++)
{
if (argv[numArg][0] == '-')
{
switch (argv[numArg][1])
{
case 'k' : somethingAboutKey(argv[numArg] + 2); // The parameter's value is passed directly to the function
break;
case 'i' : somethingAboutFile(argv[numArg] + 2);
break;
}
}
}
}

gcc reports "will never be executed" about the line: while(fgets(line, MAX_LINE, stdin) != NULL)

I'm looking for an explanation why gcc are giving this warning for me.
I'm compiling with the gcc-3 on cygwin with the -Wunreachable-code flag, and the gcc says this warning for me:
main.c:223: warning: will never be executed
That's this line: while(fgets(line, MAX_LINE, stdin) != NULL) {
This code is inside a if(exp) { } block where exp is dynamically set according to command-line-arguments(parsed by getopt() ), look at the code part:
if(mystruct.hastab) {
the default value is 0. But it's become 1 if -t flag is passed to application, as you can see at following:
struct mystruct_t {
//...
int hastab;
} mystruct;
int main(int argc, char *argv[])
{
int opt;
memset(&mystruct, 0, sizeof(mystruct));
while((opt = getopt(argc, argv, optString)) != -1) {
switch(opt) {
case 't':
mystruct.hastab = 1;
break;
//....
}
}
proc();
return 0;
}
void
proc(void)
{
char *buf, *tmpbuf, line[MAX_LINE + 1], *p, *fullfilename;
if(mystruct.hastab) {
while(fgets(line, MAX_LINE, stdin) != NULL) {
//...
}
} else {
//...
}
}
So, there's a reason for the code be executed. As happens.
Sounds like gcc is convinced hastab is never set, or that it is only set after the code it's warning about. It certainly seems like gcc is wrong, but since you only gave us snippets it is hard to be sure. I don't think anyone can help you further unless we see a complete program we can compile ourselves.
Here's another possibility: the problem is with macros. Here's a simple example that demonstrates your error:
#include <string.h>
int s;
int main(int argc, char *argv[]) {
memset(&s, 0, sizeof(s));
}
When I compile this, I get:
$ gcc -Wunreachable-code tmp.c
tmp.c: In function ‘main’:
tmp.c:4: warning: will never be executed
The error is not particularly enlightening. However, if you run the preprocessor, look what memset expands to:
$ gcc -E tmp.c
...
int s;
int main(int argc, char *argv[]) {
((__builtin_object_size (&s, 0) != (size_t) -1) ? __builtin___memset_chk (&s, 0, sizeof(s), __builtin_object_size (&s, 0)) : __inline_memset_chk (&s, 0, sizeof(s)));
}
I suspect because of the constant size of s, only one of the branches of the ?: is ever executed, and this is what gcc is complaining about. In your case, it's probably that fgets is a macro. Run gcc -E, find your problem line in the output, and see if it's wonky (mine isn't, but I'm not running cygwin).
Moral of the story: preprocessors & macros suck.
If I had to guess I'd say gcc is considering that there are 2 cases where you can call proc() without having set mystruct.hastab to something other than 0.
The first case is if this evaulates to false on the first run through, since you will drop out of the loop without executing your switch statement:
while((opt = getopt(argc, argv, optString)) != -1) {
The second case is if opt is never 't':
switch(opt) {
case 't':
mystruct.hastab = 1;
So you will fall out of the loop without ever having set mystruct.hastab to a non-zero value.

Resources