This is a quick tutorial that shows how to use ECA_Opt. I will start with a simple “Hello World!” program that takes a single option, and then add some more options and embellish it in ways that make it clear how to use most of the features of ECA_Opt.
You will need the current version of ECA_Opt which is contained in the header file eca_opt3.h and the source file eca_opt3.c.
If you don’t already have them, download those files and put them in a
directory somewhere. If you are not reading this file on fish-dna-math.homeunix.net
then those links won’t work, but know that the source and header file are in the src
directory of the ECA_Opt download archive. I am going to put those two files in a directory called: ECA_Opt_Tutorial
.
For the purposes of this tutorial, we’ll just assume those files are in
the current working directory so that we don’t have to do anything
fancy for the compiler to find them.
Some familiarity with the programming language C will be helpful too, since that is the language used.
We’ll start with a simple program that everyone is familiar with—your basic Hello World program:
#include <stdio.h> int main(void) { printf("Hello World!\n"); return(0); }
That is a pretty limited little program. It doesn’t interact with the user at all. We’ll add a simple feature in the next section.
Here we will expand the program a little bit so that if the user uses the command line option -N Fred
,
for example, it will print “Hello Fred!” instead of “Hello World!”.
However, we will leave the default behavior to be simply printing
“Hello World!”. Here is how we will do it with ECA_Opt:
main
must be declared with parameters (int argc, char *argv[])
. This is pretty standard. When the system enters function main
in this case, the variable argc
within main
will hold the number of command line arguments, and argv
is an array of strings, each element holding one of the command line arguments.GetHelloOpts
.
Within in this function is where all the ECA_Opt related code will go.
Since this is where we parse the command line, we will have to pass argc
and argv
into this function, and we will pass in some other output parameters which we will use to get input back out of the function. GetHelloOpts
can be a function that returns any arbitrary type or void
. NOTE! The variables that contain the number of command line arguments and the arguments MUST be named argc
and argv
in the function within which the command line gets parsed. The source file hello_single_option.c shows how this is done. A listing of the file appears below. Things in UPPERCASE turquoise are ECA_Opt macros. The relevance of everything is explained in the comments in the source code.
#include <stdio.h> #include <string.h> /* we might need some string functions */ #include "eca_opt3.h" /* gotta include this header file */ void GetHelloOpts( int argc, char *argv[], char *Name /* output parameter for returning the name */ ) { int Name_f=0; /* Serves as a counter for the number of times the "name" option is invoked on the command line. Must be initialized to 0 */ DECLARE_ECA_OPT_VARS /* macro that declares necessary variables for ECA_Opt This must occur after all other variable declarations in the function and before any other code gets executed in the function */ /* This is a good place to set some default values for variables in your program */ sprintf(Name,"World"); /* <-- Default name is World, so it says Hello World */ BEGIN_OPT_LOOP /* macro that prepares a loop for cycling over command line arguments, and does a bunch of other stuff too related to outputting documentation */ /* OPTION is a macro that takes parameters. It is a macro that returns a 0 or a 1. It gets executed on each loop through the "OPT_LOOP" and tokens on the command line are compared to it. In this case, if the token is a -N or a --name, then OPTION returns a 1. In such a case, the code within the "if" block below it executes. This is where we put in code to extract the name following -N or --name off the command line. In general the macro OPTION and its derivatives should be tested by "if".*/ if( OPTION( Whom To Welcome, /* ENGLISH_NAME */ Name_f, /* USAGE_COUNTER */ N, /* SHORT -- i.e. short option tag. will be "-N" */ name, /* LONG -- i.e. long option tag. will be "--name" */ S, /* ARGS -- i.e. what type of argument does it take (a string--S) */ name of person to greet, /* SHORT_COMMENT */ Name of person to greet. S should be a string which is a the name of a person that you want the program to say welcome to. If no name is given\054 then the program will default to Hello World! This option may only be issued a single time. /* LONG_COMMENT */ ) ) { if(ARGS_EQ(1)) { /* this little macro tests to make sure that there is, in this case only 1 argument to the -N option. If so, it returns 1. If not, it returns 0, prints an error informs ECA_Opt that program execution should terminate */ GET_STR(Name); /* if we get down here, we know that the option -N has only a single argument, so we grab that argument off the command line as a string and copy it to the variable Name */ } } END_OPT_LOOP /* Macro that closes the Main ECA_Opt Loop and does some error checking behind the scenes */ } int main(int argc, char *argv[]) /* notice the two arguments---no longer just void */ { char name_str[1000]; /* hope that no one enters a name longer than 1000 characters */ /* here we call GetHelloOpts. When it is through, name_str holds the name that we will greet with the program */ GetHelloOpts(argc, argv, name_str); printf("Hello %s!\n",name_str); return(0); }
The OPTION macro is the main tool used to establish and document a command line option. You can find it in eca_opt3.h
. To use it, it is often easiest to copy it from eca_opt3.h
and paste it into your document:
#define OPTION(ENGLISH_NAME,USAGE_COUNTER,SHORT,LONG,ARGS,SHORT_COMMENT,LONG_COMMENT)
then change the parameters to the values you wish them to be. The parameters are as follows:
-N
on the command line. The
“short” format of an option flag is always just a single dash followed
by a single letter. It has to be an alphabetical letter. IMPORTANT NOTE! You do not enclose the parameter value in any quotation marks!
It gets quoted using the string paste-izer. Please don’t pass in a
non-letter character for this parameter. That oughtn’t work.infile.txt
/Users/eriq/MyCode/
18
2.718
yippie
or it may be a quoted string like: “This is a quoted string argument”
1,5-10,17
C1{boing|bong|foo|bar|100}
. For example, suppose you have an option to your program called --looking-for
that wants to gather from the user whether they are looking for a
male/female who is tall/short with between a minimum age and a maximum
age that can be specified as a real number. Then you might do an ARGS string that looked like: Csex{male|female} Cheight{tall|short} R_min_age R_max_age
. Again, don’t quote these.
Note: this macro and the related ones make heavy use of the
preprocessor’s “string-pasteizer”, which means that if you are
supplying a string argument to it, you leave that string unquoted.
There is one nasty problem with this: if the string you are supplying
includes a comma, it is going to completely screw up the macro (the
preprocessor will interpret the comma as something that is separating
macro parameters). The handy way around this is to use the
backslash-preceded ascii code for the comma: \054. For example, if you
were documenting an option in the LONG field and you wanted to say,
“This option is sometimes useful, sometimes not.” You would supply the
unquoted string: This option
is sometimes useful\054 sometimes not.
There are other times you may wish to use the octal code for
punctuation; sometimes it is necessary to put in an unmatched
parenthesis \050 or \051 or quotation marks: \042, etc.
Let’s compile it up into a.out
/ECA_Opt_Tutorial/--% gcc hello_single_option.c eca_opt3.c
(Note that the part in yellow-green is my command prompt).
Then we can run it with no options:
/ECA_Opt_Tutorial/--% a.out
Hello World!
Or we can pass it the -N/--name option:
/ECA_Opt_Tutorial/--% a.out -N Fred Hello Fred! /ECA_Opt_Tutorial/--% a.out --name Frank Hello Frank!
We can also give the --help option:
/ECA_Opt_Tutorial/--% a.out --help
which returns this:
Please Supply Program Name with SET_PROGRAM_NAME macro -- Please Supply Short Description with SET_PROGRAM_SHORT_DESCRIPTION macro --help short listing of options --help-full long listing of options --help-nroff long listing of options in nroff man-page format --help-xml output all options in XML format (in development) --version prints program version information --version-history prints history of different versions --command-file F inserts contents of file F into the command line -N , --name S name of person to greet
This shows all the options that are available to us. The first 7 are options that come automatically when you use ECA_Opt. The one that we have created is at the bottom. The SHORT_COMMENT documentation goes to the right of each option.
Notice the line:
Please Supply Program Name with SET_PROGRAM_NAME macro -- Please Supply Short Description with SET_PROGRAM_SHORT_DESCRIPTION macro
This tells us that we can (and should) supply a name and description for the program. We’ll explore that more in the section program_name_description_author_versions.
Let’s check out the --help-full
option, which will give us the LONG_COMMENT documentation:
/ECA_Opt_Tutorial/--% a.out --help-full
returns:
Please Supply Program Name with SET_PROGRAM_NAME macro -- Please Supply Short Description with SET_PROGRAM_SHORT_DESCRIPTION macro Author(s): --- Please Supply Author String with SET_PROGRAM_AUTHOR_STRING macro --- About the Program: --- Developer! Please Supply Long Description with SET_PROGRAM_LONG_DESCRIPTION macro --- In the following: "J" refers to an integer argument to an option "R" refers to a real number argument to an option "S" refers to a string argument to an option "F" refers to a file path argument to an option. For example, "datfile.txt" if the file is in the current working directory, or something like "~/eriq/Documents/data/datfile.txt" if you want to provide a complete file path. (Beware of spaces in file paths!) "D" refers to a directory path argument to an option. For example, "data_direcory/" if the directory is in the current working directory, or something like "~/eriq/Documents/data_directory/" if you want to provide a complete directory path. Note that the trailing slash should be optional, but currently is not. (ERIC ADD MACROS FOR GETTING FILES AND DIRECTORIES "G" refers to a string that gives a (possibly) discontinous range of nonnegative integers. For example: "1-5,7,9,10-15" specifies the integers 1 through 5, 7, 9, and 10 through 15. There can be no whitespace in the string specifying the range, and the numbers must all be increasing. Also, the string cannot start nor end with a comma or a dash. Finally, you should not use "-" to denote two ranges without placing any commas in between. "C" refers to a "constrained" string argument to an option, i.e., the argument is a string that may only be drawn from a small set of alternatives, as specified in the help-full description. **** Program-description and documentation parameters **** --help this returns a short list of all program options and associated arguments --help-full this returns a full list of all program options and associated arguments --help-nroff this returns a full list of all program options and associated arguments using the formatting styles in nroff that give you the look of a man page. View the formatted ouput by doing: 'prog --help-nroff | nroff -man | more' where prog is the name of the program. --help-xml This returns a list of all options in a simple XML format which is suitable for input to the guiLiner front end. --version prints program version information --version-history prints history of different versions --command-file F By using this option you can store command line arguments in the file named in F. You may have any kind of white space (including line endings) in the file. The line endings are treated as just another space. Comments may be included in the file by enclosing them within a pair of ampersands (the & character). Note that you must have a & at the beginning and at the end of the comment. You cannot use just a single & to comment to the end of a line. Your comments may spread over several lines---they will still be stripped from the resulting command line so long as the are enclosed in ampersands. This feature is helpful if you have long and complex command lines that you wish to store if it makes it easier to read the command line by breaking it across multiple lines or if you have certain clusters of options that you like to store together as a module. This option may be used up to 10000 times. Optional. -N , --name S Name of person to greet. S should be a string which is a the name of a person that you want the program to say welcome to. If no name is given, then the program will default to Hello World! This option may only be issued a single time.
The --help-nroff
option gives all the same info, but in Unix man page option. Try doing:
/ECA_Opt_Tutorial/--% a.out --help-nroff | nroff -man | less
If you have nroff
and less
on your system you should get some nice output.
Here, we will add some information to the program. This involves just adding a few lines immediately before the BEGIN_OPT_LOOP
macro:
[...] /* some information about the program, author, etc */ SET_PROGRAM_NAME("hello_single_option_MoreInfo"); /* Note: QUOTED string */ SET_PROGRAM_SHORT_DESCRIPTION("Illustrate ECA_Opt and program description"); /* QUOTED string */ SET_PROGRAM_LONG_DESCRIPTION(This is just a little program I whipped up to illustrate a few things about ECA_opt in the tutorial. I hope someone actually uses this code. It has proven useful for me. Note that if you want a comma\054 then you use the octal code 054 preceded by a backslash.); /* UN-QUOTED string! */ SET_PROGRAM_AUTHOR_STRING("Eric C. Anderson"); /* QUOTED string */ SET_VERSION("Version 1.0"); /* QUOTED string */ SET_VERSION_HISTORY("Version 1.0 written Nov. 8, 2007\nVersion 0.0 conceived days before.\nblah, blah..."); /*QUOTED string */ BEGIN_OPT_LOOP /* macro that prepares a loop for cycling over command line arguments, and does a bunch of other stuff too related to outputting documentation */ [...]
The source code file with these changes is in hello_single_option_moreinfo.c. Compile it up and see now a little difference when you do --help
or help-full
OK, now we are going to get a little more complex. Here is what we will add:
-l
or --language
that chooses the language from amongst english
, italian
, spanish
, or other
. If the user chooses other
then he will be required to specify what word or phrase should be used in place of “Hello” by using the --define-salutation
option. Note that the --define-salutation
option is not going to have a short option flag (like -d
).-N/--name
to be invoked multiple times, each time providing a different name. And, in fact, now we will require that the -N
option be issued at least once. It will be a required option.-r
or --repetitions
which takes integer arguments telling us how many times to replicate
the “Hello XXX” phrase (or its italian, spanish, or other equivalent)
for each person named with a -N/--name
option. This is a little tricky because the number of arguments required depends on how many times the -N
option has been issued. Therefore we will have to check to make sure that no more -N
options are issued on the command line after this -r
option, and we have to check that at least one -N
option has already been issued. This will not be a required option. By default, everything will get printed once.--sum-or-prod
that would be invoked like--sum-or-prod sum 12.4 34.2 12.2
or
--sum-or-prod product 234.3 343 9944.1 838.3 883.4
and which will return the sum or the product of the numbers following it on the command line. The interesting part here is that it can have a variable number of arguments so long as it has two or more arguments.
Here is what the whole source code file looks like. By reading the comments, you should gain more understanding of how the different macros are used:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include "eca_opt3.h" /* gotta include this header file */ /* some constants for declaring arrays */ #define MAXNAMES 1000 #define MAXNAMELENGTH 1000 void GetHelloOpts( int argc, char *argv[], char **name_array, /* output parameter for returning the names */ int *NumNames, /* output parameter for number of names used */ char *Salutation, /* output parameter for salutation. Depends on language or is defined by user */ int *Reps, /* array to output number of reps for each name */ int *SumProdInvoked, /* output parameter to announce if the --sum-or-prod option was invoked */ double *Result /* for returning the results of the sum-or-prod calculation */ ) { int i; int Name_f=0, /* Serves as a counter for the number of times the "name" option is invoked Must be initialized to 0 */ salutation_f=0, /* counter for --define-salutation option */ language_f=0, /* counter for -l option */ reps_f=0, /* counter for -r option */ sumprod_f=0; /* counter for --sum-or-prod option */ DECLARE_ECA_OPT_VARS /* This is a good place to set some default values for variables in your program */ *NumNames = 0; /* initialize this to 0 */ for(i=0;i<MAXNAMES;i++) { Reps[i]=1; } /* establish the default number of reps. */ *SumProdInvoked=0; /* some information about the program, author, etc */ SET_PROGRAM_NAME("hello_more_complex"); /* Note: QUOTED string */ SET_PROGRAM_SHORT_DESCRIPTION("Illustrate ECA_Opt and program description"); /* QUOTED string */ SET_PROGRAM_LONG_DESCRIPTION(This is just a little program I whipped up to illustrate a few things about ECA_opt in the tutorial. This particular program has an example of a more complicated set of options\054 some of which are dependent on others\054, etc.); /* UN-QUOTED string! */ SET_PROGRAM_AUTHOR_STRING("Eric C. Anderson"); /* QUOTED string */ SET_VERSION("Version 1.0"); /* QUOTED string */ SET_VERSION_HISTORY("Version 1.0 written Nov. 8, 2007\nGrew out of simpler programs.\nblah, blah..."); /*QUOTED string */ BEGIN_OPT_LOOP /* use this to start a block of related options. Close the block with the CLOSE_SUBSET macro (below) */ OPEN_SUBSET(Options Dealing With the Greeting Part of The Program, /* Name of Subset for output in --help, --help-full, and --help-nroff */ Greeting Options, /* Name of subset for lumping them together in GuiLiner */ These options all have to do with the semi-Hello-World aspect of the program /* Description of subset. Not really used currently */ ) /* NOTE. These are all UNQUOTED strings. Beware of commas. */ /* here is the language option See that it is required. */ if(REQUIRED_OPTION( Language, language_f, l, language, C{english|italian|spanish|other}, /* using the C, or "constrained string" arg specifier. all the stuff in braces really only gets used in the --help-xml output for designing the GUI. */ select what language to use, Follow this option flag with one of: english\054 italian\054 spanish\054 or other. It will determine how the program says Hello. If you want the program to say hello in some other way\054 use other. Doing so will then make the --define-salutation option mandatory. ) ) { if(ARGS_EQ(1)) {char lang[1000]; GET_STR(lang); /* You pull the response off the command line as a string ECA_Opt does not have a facility for testing choices. That must be done by you! */ if(strcmp(lang,"english")==0) { sprintf(Salutation,"Hello"); } else if(strcmp(lang,"italian")==0) { sprintf(Salutation,"Buon Giorno"); } else if(strcmp(lang,"spanish")==0) { sprintf(Salutation,"Buenos Dias"); } else if(strcmp(lang,"other")==0) { sprintf(Salutation,"NeedsDef"); /* we will use this as a flag that tells us the user needs to define it */ } else { fprintf(stderr,"Unrecognized choice %s given in option -l/--language. Must be english, italian, spanish, or other. Exiting...\n",lang); exit(1); } } } /* Look! We used MULT_USE_REQ_OPTION instead of OPTION. Program now exits with error if this option is not used, and the option can be invoked MAX_USES times. */ /* Format for this macro: MULT_USE_REQ_OPTION(ENGLISH_NAME,USAGE_COUNTER,SHORT,LONG,ARGS,SHORT_COMMENT,LONG_COMMENT, MAX_USES) */ if( MULT_USE_REQ_OPTION( Whom To Welcome, /* ENGLISH_NAME */ Name_f, /* USAGE_COUNTER */ N, /* SHORT -- i.e. short option tag. will be "-n" */ name, /* LONG -- i.e. long option tag. will be "--name" */ S, /* ARGS -- i.e. what type of argument does it take (a string--S) */ name of person to greet, /* SHORT_COMMENT */ Name of person to greet. S should be a string which is a the name of a person that you want the program to say welcome to. If no name is given\054 then the program will default to Hello World! This option may be issued multiple times. Each time it adds another name to say hello to. It can be used no more than MAXNAME times., MAXNAMES /* MAX_USES -- the most times this option can be issued without causing ECA_Opt to shut it down and exit */ ) ) { if(ARGS_EQ(1)) { /* this little macro tests to make sure that there is, in this case only 1 argument to the -N option. If so, it returns 1. If not, it returns 0, prints an error informs ECA_Opt that program execution should terminate */ if(!HAS_BUT_SHOULD_NOT(reps_f,-r/--repetitions)) { /* check to make sure this isn't being given after the -r option. If so, this will cause program to exit with error. Note that you have to negate it with the ! */ name_array[*NumNames]=(char *)calloc(MAXNAMELENGTH, sizeof(char)); /* allocate some memory to store the name */ GET_STR(name_array[*NumNames]); /* get the name off the command line */ (*NumNames)++; /* increment the NumNames counter */ } } } if(OPTION( Number Of Reps, reps_f, r, repetitions, J1 J2 ..., Number of times to repeat salutation for each name, Number of times to repeat salutation for each name. This option takes an integer for every different name that you have put on the command line using the -N/--name option. Once you issue this option\054 you cannot issue any other -N options. The number of arguments must match the number of uses of the -N option. If not\054 the program will exit. ) ) { if(ALREADY_HAS(Name_f,"-N/--name")) { /* make sure the -N option has been issued at least once */ if(ARGS_EQ(*NumNames)) {int i; /* check to make sure you have the right number of arguments */ for(i=0;i<*NumNames;i++) { /* cycle over all those integer arguments */ Reps[i] = GET_INT; /* collect each integer off the command line with this macro */ } } } } /* format for this macro: COND_REQ_OPTION(ENGLISH_NAME,USAGE_COUNTER,SHORT,LONG,ARGS,SHORT_COMMENT,LONG_COMMENT, WHEN_REQUIRED, CODEPEND_STATEMENT) */ if(COND_REQ_OPTION( Custom Salutation, salutation_f, , /* note -- no SHORT name so you just leave it blank */ define-salutation, S, Customize the greeting., Customize the greeting. The string S will be used to replace Hello. This option may only be given after the -l/--language option\054 and then only if \042other\042 was chosen. If \042other\042 was chosen in the -l option\054 then this option becomes required., strcmp(Salutation,"NeedsDef")==0, /* WHEN_REQUIRED: Recall, we used the fact that Salutation=="NeedsDef" as a flag to this option is required */ when you have given the argument of \042other\042 to the -l/--language option /* CODEPEND_STATEMENT -- for error message saying it is required */ )) { if(ALREADY_HAS(language_f,-l/--language)) { /* this must come after the -l option */ if(strcmp(Salutation,"NeedsDef")==0) { /* make sure they chose the "other" choice in the --language option */ if(ARGS_EQ(1)) { GET_STR(Salutation); /* collect the desired salutation off the command line */ } } else { /* give an error message if they chose something other than "other" */ fprintf(stderr,"Error! You issued the --define-salutation option, but you did not choose the \"other\" choice in the -l/--language option. You must choose \042other\042 to use this option. Exiting...\n"); exit(1); } } } CLOSE_SUBSET; /* done with the greeting-related options */ OPEN_SUBSET(Totally unrelated option, Unrelated option, These options are totally unrelated ) if(OPTION( Sum or product, sumprod_f, , sum-or-prod, C{sum|product} R1 R2 ..., ask for the sum or the product of the following real numbers, Ask for the sum or the product of the following real numbers. C must be one of \042sum\042 or \042product\042. (no quotation marks\054 though!) R1 R2 ... are an arbitary number (but at least one) of real numbers that you want to have the sum or the product of)) { if(ARGS_GEQ(2)) { char temp[1000]; int n; /* if there are 2 or more arguments */ *SumProdInvoked=1; /* set a flag announcing that this option was correctly invoked */ GET_STR(temp); /* get the string that is the user's choice input */ n=COUNT_ARGS; /* count number of argument to this option that are now left on the command line */ if(strcmp(temp,"sum")==0) { /* if it wants the sum...*/ *Result=0.0; /* initialize to accumualate a sum */ for(i=0;i<n;i++) { *Result += GET_DUB; /* collect the next string off the command line, convert to a double, and add to Result */ } } else if(strcmp(temp,"product")==0) { /* if it wants the product...*/ *Result=1.0; /* initialize to accumualate a product */ for(i=0;i<n;i++) { *Result *= GET_DUB; /* collect the next string off the command line, convert to a double, and multiply to Result */ } } else { fprintf(stderr, "Unrecognized choice %s as first argument to option --sum-or-prod. Expecting \"sum\" or \"product\". Exiting...\n",temp); exit(1); } } } CLOSE_SUBSET; END_OPT_LOOP /* Macro that loses the Main ECA_Opt Loop and does some error checking behind the scenes */ } int main(int argc, char *argv[]) /* notice the two arguments---no longer just void */ { int i,j; char *name_array[MAXNAMES]; int NumNames; char Salut[1000]; int Reps[MAXNAMES]; double Res; int SumProdFlag; /* here we call GetHelloOpts. When it is through, name_str holds the name that we will greet with the program */ GetHelloOpts(argc, argv, name_array, &NumNames, Salut, Reps, &SumProdFlag, &Res ); /* now print all the stuff */ for(i=0;i<NumNames;i++) { for(j=0;j<Reps[i];j++) { printf("%s %s!\n",Salut, name_array[i]); } printf("\n"); } /* if indicated, print the sum-or-prod result */ if(SumProdFlag) { printf("\n\nThe Result of Summing or Multiplying is %f\n\n",Res); } return(0); }
You’ll notice a few new macros in there like ALREADY_HAS
, HAS_BUT_SHOULD_NOT
, COUNT_ARGS
, and GET_INT
. All these macros are documented in the Doxygen documentation for eca_opt.h
.