This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/baseband-devel@lists.osmocom.org/.
Christian Vogel vogelchr at vogel.cxHi everyone, I was trying to iterate on that subject again, but I think those are things more easily programmed than discussed. So, my first attempt looks like the attached commit to libosmocore, you can also download it at http://vogel.cx/git/0001-Modular-commandline-parsing.patch . It uses a few arrays and singly linked lists which get iterated way too often, but I think for commandline parsing it doesn't really matter. It's used like this: /* callback function, called for each option given */ int option_callback(struct cmdline_item *p, char *argument){ if(p->shortopt == 'v') verbose++; if(!strncmp(p->longopt,"port")) portnum = atoi(argument); } struct cmdline_group mygroup = { .name = "Program Options Group", .nitems = 2, .callback = option_callback, .items = { { 'v',"verbose", 0,"Add verbosity." }, { 0 ,"port", 1,"Specify tcp port." }, } }; /* somewhere during initialisation */ cmdline_register_group(&mygroup); --- configure.in | 1 + include/osmocore/cmdline.h | 37 +++++++++ src/Makefile.am | 2 +- src/cmdline.c | 184 ++++++++++++++++++++++++++++++++++++++++++ tests/Makefile.am | 2 +- tests/cmdline/Makefile.am | 5 + tests/cmdline/cmdline_test.c | 55 +++++++++++++ 7 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 include/osmocore/cmdline.h create mode 100644 src/cmdline.c create mode 100644 tests/cmdline/Makefile.am create mode 100644 tests/cmdline/cmdline_test.c diff --git a/configure.in b/configure.in index 30f9d9c..d9c0d30 100644 --- a/configure.in +++ b/configure.in @@ -114,4 +114,5 @@ AC_OUTPUT( tests/sms/Makefile tests/msgfile/Makefile tests/ussd/Makefile + tests/cmdline/Makefile Makefile) diff --git a/include/osmocore/cmdline.h b/include/osmocore/cmdline.h new file mode 100644 index 0000000..e322b45 --- /dev/null +++ b/include/osmocore/cmdline.h @@ -0,0 +1,37 @@ +#ifndef _OSMOCORE_CMDLINE_H +#define _OSMOCORE_CMDLINE_H + +struct cmdline_item { + char shortopt; /* option "-s" */ + char *longopt; /* option "--longopt" */ + int hasarg; /* option has args */ + char *helptext; /* annotate usage */ + union cmdline_item_data { /* random data you want to keep track of */ + char c; + int i; + void *ptr; + } data; +}; + +struct cmdline_group { + char *name; /* name of this group, for usage */ + /* if an option is called, this callback will trigger. + 1st function parameter is the cmdline_item. + 2nc function parameter is the option argument (hasarg != 0). + Return != 0 from the callback to signal an error! */ + int (*callback)(struct cmdline_item*,char *); + + int nitems; /* number of items */ + struct cmdline_group *next; /* used internally */ + struct cmdline_item items[]; /* items in this group */ +}; + +/* add cmdline_group to osmocore command line parser */ +extern void cmdline_register_group(struct cmdline_group *p); + +/* do the actual parsing */ +extern int cmdline_parse(int argc,char **argv,int *optind); + +extern void cmdline_usage(char *argv0); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 64310e0..e4fe4a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,7 +13,7 @@ libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \ tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \ write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \ logging.c gsm0808.c rate_ctr.c gsmtap_util.c \ - gprs_cipher_core.c crc16.c panic.c process.c gsm0480.c + gprs_cipher_core.c crc16.c panic.c process.c gsm0480.c cmdline.c if ENABLE_PLUGIN libosmocore_la_SOURCES += plugin.c diff --git a/src/cmdline.c b/src/cmdline.c new file mode 100644 index 0000000..ddf5747 --- /dev/null +++ b/src/cmdline.c @@ -0,0 +1,184 @@ +/* command line parsing for osmocom applications */ + +/* + * (C) 2010 Christian Vogel <vogelchr at vogel.cx> + * All Rights Reserved + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocore/cmdline.h> +#include <osmocore/talloc.h> + +#include <string.h> +#include <unistd.h> +#include <getopt.h> +#include <libgen.h> + +static struct cmdline_group *cmdline_groups = NULL; + +/* + * Find short / long option registered with us. shortopt may be \0 + * and longopt may be NULL, then we ignore this part of an item. + * + * Returns item found. If grpret != NULL the variable pointed to + * will be set to the specific group. + */ +static struct cmdline_item * +cmdline_find_item(char shortopt, + const char *longopt, + struct cmdline_group **grpret) +{ + struct cmdline_group *grp = cmdline_groups; + int i; + + while(grp){ + for(i=0;i<grp->nitems;i++){ + struct cmdline_item *item = &grp->items[i]; + if( (shortopt && shortopt== item->shortopt )|| + (longopt && !strcmp(longopt,item->longopt))){ + if(grpret) + *grpret=grp; + return item; + } + } + grp = grp->next; + } +} + +/* add cmdline_group to osmocore command line parser */ +void +cmdline_register_group(struct cmdline_group *p) +{ + /* insert group into chain */ + struct cmdline_group *ogrp,**last = &cmdline_groups; + struct cmdline_item *oitem,*item; + int i; + + /* just for debugging / finding duplicate options */ + for(i=0;i<p->nitems;i++){ + item = &p->items[i]; + oitem = cmdline_find_item(item->shortopt,item->longopt,&ogrp); + if(oitem){ + fprintf(stderr,"%s: duplicate option registered!", + __FUNCTION__); + if(item->shortopt) + fprintf(stderr," (short: \'%c\')", + item->shortopt); + if(item->longopt) + fprintf(stderr," (long: \"%s\")",item->longopt); + fprintf(stderr,"Old group: %s, this group: %s.\n", + p->name,ogrp->name); + } + } + + while(*last) // add as last group + last = &( (*last)->next ); + *last = p; +}; + +void cmdline_usage(char *argv0){ + struct cmdline_group *grp; + char buf[80]; + int i; + + if(!cmdline_groups){ + fprintf(stderr,"Usage: %s arguments\n",basename(argv0)); + return; + } + fprintf(stderr,"Usage: %s [option] arguments\n\n",basename(argv0)); + for(grp=cmdline_groups;grp;grp=grp->next){ + fprintf(stderr,"*** Options in group %s ***\n",grp->name); + for(i=0;i<grp->nitems;i++){ + struct cmdline_item *item = & grp->items[i]; + if(item->shortopt && item->longopt){ + sprintf(buf,"-%c|--%s", + item->shortopt,item->longopt); + } else if(item->shortopt) { + sprintf(buf,"-%c",item->shortopt); + } else { + sprintf(buf,"--%s",item->longopt); + } + if(item->hasarg) + strcat(buf," ARG"); + if(item->helptext) + fprintf(stderr," %-32s %s\n",buf,item->helptext); + else + fprintf(stderr," %s\n",buf); + } + } +} + +int cmdline_parse(int argc,char **argv,int *optind){ + struct cmdline_group *grp; + struct cmdline_item *item; + struct option *opts,*o; + char *optstr,*s; + int i=0,j; + int ret=0; + + /* how many items do we have? */ + for(grp=cmdline_groups;grp;grp=grp->next) + i += grp->nitems; + + /* allocate memory for getopt_long data structures */ + o = opts = talloc_array(NULL,struct option,i+1); + s = optstr = talloc_size(NULL,1+2*i); // worst case + + /* build struct option array and short option string */ + for(grp=cmdline_groups;grp;grp=grp->next){ + for(i=0;i<grp->nitems;i++){ + item = & grp->items[i]; + if(item->longopt){ + o->name = item->longopt; + o->has_arg = item->hasarg; + o->flag = NULL; + o->val = 0; + o++; + } + if(item->shortopt){ + *s++ = item->shortopt; + if(item->hasarg) + *s++ = ':'; + } + } + } + o->name = NULL; + o->has_arg = 0; + o->flag = NULL; + o->val = 0; + *s = '\0'; + + while(-1 != (i=getopt_long(argc,argv,optstr,opts,&j))){ + item = NULL; + if(i==':' || i=='?'){ + ret=-1; // getopt should write error message + goto out; + } + if(i==0) // long option + item = cmdline_find_item(0,opts[j].name,&grp); + else + item = cmdline_find_item(i,NULL,&grp); + if(grp->callback) + grp->callback(item,optarg); + } +out: + talloc_free(opts); + talloc_free(optstr); + return ret; +} + + diff --git a/tests/Makefile.am b/tests/Makefile.am index 0166b4f..ecdc8ad 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,5 @@ if ENABLE_TESTS -SUBDIRS = timer sms ussd +SUBDIRS = timer sms ussd cmdline if ENABLE_MSGFILE SUBDIRS += msgfile endif diff --git a/tests/cmdline/Makefile.am b/tests/cmdline/Makefile.am new file mode 100644 index 0000000..3b155e1 --- /dev/null +++ b/tests/cmdline/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +noinst_PROGRAMS = cmdline_test + +cmdline_test_SOURCES = cmdline_test.c +cmdline_test_LDADD = $(top_builddir)/src/libosmocore.la diff --git a/tests/cmdline/cmdline_test.c b/tests/cmdline/cmdline_test.c new file mode 100644 index 0000000..295b5aa --- /dev/null +++ b/tests/cmdline/cmdline_test.c @@ -0,0 +1,55 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#include <osmocore/cmdline.h> + +int option_callback(struct cmdline_item *p,char *argument){ + printf("Option specified:"); + if(p->shortopt) + printf(" -%c",p->shortopt); + if(p->shortopt && p->longopt) + printf(" or"); + if(p->longopt) + printf(" --%s",p->longopt); + if(p->hasarg) + printf(" argument=%s",argument); + printf("\n"); + return 0; +} + +struct cmdline_group group_quux = { + .name = "The QUUX group.", + .nitems = 3, + .callback = option_callback, + .items = { + // -s --long, hasarg, description + { 'a',"aaaah", 1,"The Aaaaaaah option."}, + { 'b',"bleech",0,"The Bleech option."}, + { 'c',"cccc", 1,"The CCCC option."} + } +}; + +struct cmdline_group group_foo = { + .name = "The FOO group.", + .callback = option_callback, + .nitems = 2, + .items = { + { 0 ,"foo", 0,"The foo option."}, + { 'v',NULL , 1,"The v option."} + } +}; + + +int main(int argc,char **argv){ + int optind; + + /* in layer23, these would each go into a different + part of the application, e.g. logging, gsm, ... */ + cmdline_register_group(&group_quux); + cmdline_register_group(&group_foo); + + /* in layer23, this would go to the "common" code */ + if(-1 == cmdline_parse(argc,argv,&optind)) + cmdline_usage(argv[0]); +} -- 1.6.3.3