Ho scritto nel mio poco tempo a disposizione, un po' di fretta, un command-line parser da integrare in un altro progetto che sto scrivendo sempre a tempo perso.
Se (non sentitevi in obbligo) qualcuno se la sente di testarlo (a tempo perso) segnalandomi bug(s) ve ne sarei grato.
Saluti,
Max
/**
* Copyright (C) 2011, Max Cavallo <ixamit_at_gmail_dot_com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
opt process a command-line. Every option has long and short form.
mix and join different option styles like:
opt -bcd
opt -b -c -d
opt --background -c --debug
opt -c--debug --background
...
opt ARGV -olog
opt ARGV -o log
opt --log-file=log ARGV
opt ARGV --log-file = log
...
opt --execute-command:system -db argv1 --tries 20
opt --tries='20' -dbe system argv1
...
opt -bcd -t 3 --dns-timeout=10 --retry-connrefused -o log localhost -e dir
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef _WIN32
#ifndef snprintf
#define snprintf _snprintf
#endif
#endif
#define countof(x) (sizeof (x) / sizeof ((x)[0]))
#define OPT_SPACE '\40'
#define OPT_SEPARATOR '\43'//'\35'
#define OPT_UNRECOGNIZED_OPTION "unrecognized option"
#define OPT_REQUIRED_ARGUMENT "option requires an argument"
#define OPT_INVALID_PARAMETER "invalid parameter"
#define OPT_INVALID_ASSIGNEMENT "invalid assignement"
#define OPT_OUT_OF_MEMORY "out of memory"
static struct _opt_var *opt_first_var=NULL;
static struct _opt_var *opt_last_var =NULL;
void print_help ();
struct _opt_var
{
char *key;
char *value;
struct _opt_var *next;
};
struct _opt
{
/**
long && short names are refered to option name witch called
from the command line. prefix are respectivly '--' & '-'
*/
char *long_name;
char short_name;
/**
assignement char per value. It is used by enum type.
refered to OPT_ASSIGN
*/
#define OPT_ASSIGN "= :\0"
enum {
opt_equal,
opt_space,
opt_colon,
opt_null,
} assign;
/**
meaning 'type_of_data'.
opt_bool == boolean (assign == opt_null)
opt_value == numeric (assign == opt_equal | opt_space | opt_colon)
opt_string == alpha (assign == opt_equal | opt_space | opt_colon)
opt_callback == function pointer callback (opt_null)
*/
enum {
opt_callback,
opt_bool,
opt_value,
opt_string,
} type;
/**
real name for option witch you referenced or function pointer callback
*/
void *data;
/**
short help descriptive
-used only for automatic helper-
*/
char *hlp_des;
/**
extra argument assignement help, ie:
{"tries",'t' ...... "number"}
it will show:
-t, --tries=number ......
-used only for automatic helper-
*/
char *hlp_arg;
};
/**
=============================================
put your options arguments here; you need it!
=============================================
*/
#define OPT_PRG_NAME "test"
static char opt_synopsis[]="Usage: " OPT_PRG_NAME " [option]... [argv]...";
static struct _opt opt_options[]=
{
{"help",'h',opt_null,opt_callback,(void *)print_help,"Print this help",NULL},
{"background",'b',opt_null,opt_bool,"background","Go to background immediately after startup",NULL},
{"color",'c',opt_null,opt_bool,"color","Change color",NULL},
{"debug",'d',opt_null,opt_bool,"debug","Turn on debug output",NULL},
{"tries",'t',opt_equal,opt_value,"tries","Set number of retries to number","number"},
{"dns-timeout",0,opt_equal,opt_value,"dns_timeout","Imposta il timeout per la risoluzione del DNS","SECONDI"},
{"retry-connrefused",0,opt_null,opt_bool,"retry_connrefused","riprova anche se la connessione è rifiutata",NULL},
{"log-file",'o',opt_equal,opt_string,"log_file","Log all messages to logfile","logfile"},
{"execute-command",'e',opt_colon,opt_string,"execute_command","A command thus invoked will be executed","command"},
};
/**
=============================================
*/
/**
This is the automatic default help.
It's generated by reading the struct opt_options[]; you should easly use that function
or different by changing pointer in the help field declared above.
*/
void print_help ()
{
char buff[40];
int i,j;
printf ("%s\n\n",opt_synopsis);
for (i=0;i<countof(opt_options);i++)
{
snprintf (buff,sizeof(buff)," %c%c%c --%s%c%s",
(opt_options[i].short_name==' ' || opt_options[i].short_name==0) ? ' ' : '-',
(opt_options[i].short_name==' ' || opt_options[i].short_name==0) ? ' ' : opt_options[i].short_name,
(opt_options[i].short_name==' ' || opt_options[i].short_name==0) ? ' ' : ',',
opt_options[i].long_name,
OPT_ASSIGN[opt_options[i].assign],
opt_options[i].hlp_arg);
printf ("%s",buff);
for (j=strlen(buff);j<sizeof(buff);j++)
printf (" ");
printf ("%s\n",opt_options[i].hlp_des);
}
printf ("\n");
}
/**
dump data to stream
*/
void opt_out (FILE *stream,char *p, int n)
{
for (;n;n--)
fprintf (stream,"%c",*(p++));
}
/**
This is init function.
It return an allocated opt string with all argv separated by OPT_SEPARATOR
ie: foo bar 'foo bar' ---> foo#bar#foo bar\0
return word's number or error
*/
int opt_init (int argc,char **argv,char **opt)
{
int i,err=0,l=0,wc=argc-1;
opt_first_var=opt_last_var=NULL;
*opt=NULL;
/** possibly incongruences type assignement */
for (i=0;!err && i<countof(opt_options);i++)
{
switch (opt_options[i].type)
{
case opt_callback:
if (opt_options[i].assign != opt_null)
err=opt_options[i].type+1;
break;
case opt_bool:
if (opt_options[i].assign != opt_null)
err=opt_options[i].type+1;
break;
case opt_value:
if (opt_options[i].assign != opt_equal &&
opt_options[i].assign != opt_space &&
opt_options[i].assign != opt_colon)
err=opt_options[i].type+1;
break;
case opt_string:
if (opt_options[i].assign != opt_equal &&
opt_options[i].assign != opt_space &&
opt_options[i].assign != opt_colon)
err=opt_options[i].type+1;
break;
}
}
if (err)
{
fprintf (stderr,"opt_init::opt_options[%d] "OPT_INVALID_ASSIGNEMENT"\n",i);
return -1;
}
/**
TODO more fields control
*/
if (wc)
{
for (--argc;argc>0;argc--)
l+=strlen(argv[argc]);
l+=(wc-1);
if ((*opt=malloc(l+1))==NULL)
{
fprintf (stderr, OPT_OUT_OF_MEMORY"\n");
return -1;
}
memset (*opt,OPT_SEPARATOR,l); (*opt)[l]='\0';
for (i=0,argc=1;argc<=wc;argc++)
{
l=strlen(argv[argc]);
memcpy (*opt+i,argv[argc],l);
i+=(l+1);
}
}
return (wc);
}
/**
Just save the key && the value into a linked list.
*/
int set_opt (char *key,char *value, int l_value)
{
struct _opt_var *node;
int l_key=(key) ? strlen(key): 0;
node=(struct _opt_var *)malloc (sizeof(struct _opt_var));
node->key=(l_key) ? (char *) malloc (l_key+1) : (char *)0;
node->value=(l_value) ? (char *) malloc (l_value+1) : (char *)0;
if (!node)
{
if (node->value) free (node->value);
if (node->key) free (node->key);
if (node) free (node);
fprintf (stderr,OPT_OUT_OF_MEMORY"\n");
return -1;
}
if (l_key)
memcpy (node->key, key,l_key+1);
if (l_value)
{
memcpy (node->value, value, l_value);
node->value[l_value]='\0';
}
node->next=NULL;
if (!opt_first_var) opt_first_var=node;
if (opt_last_var) opt_last_var->next=node;
opt_last_var=node;
return 0;
}
/**
Search opt[ion] in opt_options[]
*/
int opt_search_short (char opt)
{
int i;
for (i=0;i<countof(opt_options);i++)
if (opt_options[i].short_name==opt)
return i;
fprintf (stderr,OPT_UNRECOGNIZED_OPTION " '-%c'\n",opt);
return -1;
}
/**
Search opt[ion] in opt_options[]
*/
int opt_search_long (char *opt)
{
char *end;
int i,l;
for (i=0;i<countof(opt_options);i++)
{
l=strlen(opt_options[i].long_name);
if ((strncmp (opt_options[i].long_name,opt,l))==0)
{
if (opt[l]==OPT_SEPARATOR || strchr(OPT_ASSIGN,(int)opt[l]))
return i;
}
}
for (l=0,end=opt;*end && *end!=OPT_SEPARATOR;end++,l++);
fprintf (stderr,OPT_UNRECOGNIZED_OPTION " '--");
opt_out (stderr,opt,l);
printf ("'\n");
return -1;
}
/**
Main parsing option_line.
*/
int opt_index (char *options_line)
{
char *opt=options_line,*key,*value;
int is_option,is_option_long, is_option_short;
int i,index,l_value=0,err=0;
int (*checkfunc) (int)=NULL;
if (!opt)
err=-1;
is_option=is_option_long=is_option_short=0;
while (!err && *opt)
{
/**
init flags
removing prefix dash
*/
index=0; key=value=NULL; l_value=0;
if (*opt==OPT_SEPARATOR) {is_option=is_option_long=is_option_short=0;opt++;}
if (*opt=='-') {is_option=1;is_option_short=1;is_option_long=0;opt++;}
if (*opt=='-') {is_option_long=1;is_option_short=0;opt++;}
/** option is short */
if (is_option_short)
{
if ((index=opt_search_short (*opt))>=0)
{
key=(char *)opt_options[index].data;
/** move next argument */
for (++opt;*opt && (*opt==OPT_SEPARATOR || *opt==OPT_SPACE);opt++);
}
else
{
/** unrecognized short option */
err=-1;
break;
}
}
/** option is long */
if (is_option_long)
{
if ((index=opt_search_long (opt))>=0)
{
key=(char *)opt_options[index].data;
/** move next argument */
opt+=strlen(opt_options[index].long_name);
for (;*opt && (*opt==OPT_SEPARATOR || *opt==OPT_SPACE);opt++);
if (*opt && *opt==OPT_ASSIGN[opt_options[index].assign])
for (opt++;*opt && (*opt==OPT_SEPARATOR || *opt==OPT_SPACE);opt++);
}
else
{
/** unrecognized long option */
err=-1;
break;
}
}
/** execute callback function */
if (is_option && opt_options[index].type==opt_callback)
{
void (*func) (void) = (void (*) (void)) opt_options[index].data;
func ();
continue;
}
/** getting value || ARGV */
if ( !is_option ||
opt_options[index].type==opt_value ||
opt_options[index].type==opt_string )
{
value=opt;
for (l_value=0;*opt && *opt!=OPT_SEPARATOR;opt++,l_value++);
}
/** check 4 value */
if (is_option && value)
{
if (l_value==0)
{
fprintf (stderr,"\'%s\' "OPT_REQUIRED_ARGUMENT"\n",opt_options[index].long_name);
err=-1;
break;
}
switch (opt_options[index].type)
{
case opt_string:
checkfunc=isprint;
break;
case opt_value:
checkfunc=isdigit;
break;
default:
break;
}
if (opt_options[index].type!=opt_callback)
{
for (i=0;i<l_value;i++)
{
if (checkfunc(value[i])==0)
{
fprintf (stderr,"\'--%s\' "OPT_INVALID_PARAMETER" \"",opt_options[index].long_name);
opt_out (stderr,value,l_value);
printf ("\"\n");
err=-1;
break;
}
}
}
}
if (!err)
{
err=set_opt (key,value, l_value);
}
if (value==NULL && *(opt-1)==OPT_SEPARATOR) opt--;
}
return err;
}
/**
Get the ARGC parsed
*/
int opt_get_argc ()
{
struct _opt_var *p;
int argc;
for (argc=0,p=opt_first_var;p;p=p->next)
if (!p->key) argc++;
return argc;
}
/**
Get the ARGV[n] parsed
return value || NULL
*/
char *opt_get_argv (int n)
{
struct _opt_var *p;
int argc;
for (argc=0,p=opt_first_var;p;p=p->next)
{
if (!p->key)
{
if (argc++==n) return (p->value);
}
}
return NULL;
}
/**
Get the option
key=field (char *)data in opt_options[]
return:
NULL no_key found
(char *) to saved value
*/
char *opt_get (char *key)
{
struct _opt_var *p;
for (p=opt_first_var;p;p=p->next)
{
if ((p->key) && (strcmp(p->key,key)==0))
{
return (p->value) ? p->value : "1";
}
}
return NULL;
}
/**
free all memory
(all but not opt)
*/
void opt_free ()
{
struct _opt_var *p,*next=NULL;
for (p=opt_first_var;p;p=next)
{
if (p->key) free (p->key);
if (p->value) free (p->value);
next=p->next;
free (p);
}
}
/**
just the opt_test;
dumping some values
*/
void opt_test ()
{
struct _opt_var *p;
int i,argc;
printf ("OPTIONS:\n");
if (!opt_first_var) printf ("[none]\n");
for (p=opt_first_var;p;p=p->next)
if (p->key)
printf ("%s=%s\n",(p->key)?p->key:"ARGV",p->value);
printf ("\n");
argc=opt_get_argc();
printf ("THERE %s %d ARGC\n",(argc==1)?"IS":"ARE",argc);
if (!argc) printf ("[none]\n");
for (i=0;i<argc;i++)
printf ("ARGV[%d]=%s\n",i,opt_get_argv(i));
printf ("\n");
printf ("CAN EASILY SET YOUR ENVIROMENT:\n");
printf ("background=%s\n",opt_get("background"));
printf ("color=%s\n",opt_get("color"));
printf ("debug=%s\n",opt_get("debug"));
printf ("tries=%s\n",opt_get("tries"));
printf ("log_file=%s\n",opt_get("log_file"));
printf ("execute_command=%s\n",opt_get("execute_command"));
printf ("retry_connrefused=%s\n",opt_get("retry_connrefused"));
printf ("dns_timeout=%s\n",opt_get("dns_timeout"));
}
int main (int argc,char **argv)
{
char *options_line;
int err=0;
if (argc<2)
{
print_help ();
return -1;
}
/** need init opt */
if (opt_init (argc,argv,&options_line)>0)
{
/** the main parser */
if ((err=opt_index (options_line))==0)
{
/** free options_line */
if (options_line) free (options_line);
/** lets use it now */
opt_test ();
/** remeber to free opt enviroment */
opt_free ();
}
}
/** remember to check for errors */
printf ("\nEXIT with %d\n",err);
return err;
}