Main Page | Compound List | File List | Compound Members | File Members

parse_options.c

Go to the documentation of this file.
00001 /*
00002  *  Copyright (c) KONINKLIJKE PHILIPS ELECTRONICS N.V. 2000-2006
00003  *  All rights reserved.
00004  *  For licensing and warranty information, see the file COPYING.LGPLv21.txt
00005  *  in the root directory of this package.
00006  *
00007  *  Philips Research Laboratories
00008  *  Eindhoven, The Netherlands
00009  *
00010  *  CVS id      :  $Id: parse_options.c,v 1.2 2006/02/06 12:57:49 kock Exp $
00011  *
00012  *  Origin      :  This file originates from package mtk at
00013  *                 http://sourceforge.net/projects/pfspd
00014  *
00015  */
00016 
00017 #include <stdlib.h>
00018 #include <stdarg.h>
00019 #include <stdio.h>
00020 #include <ctype.h>
00021 #include <string.h>
00022 #include <assert.h>
00023 
00024 #include "parse_options.h"
00025 
00026 #define MAX_LINE_LENGTH 4*1024
00027 #define MAX_OPTIONS     256
00028 #define MAX_FILENAME    256
00029 
00030 #define OPTION            0
00031 #define ENUM_OPTION       1
00032 #define ARGUMENT          2
00033 
00034 #define MIN_NAME_WIDTH    4
00035 #define MIN_TYPE_WIDTH    4
00036 #define MIN_DFLT_WIDTH    7
00037 
00038 #define MAX_NAME_WIDTH   16
00039 #define MAX_TYPE_WIDTH   16
00040 #define MAX_DFLT_WIDTH   16
00041 
00042 #define BUF_LEN        1024
00043 
00044 #define MAX_LINE_WIDTH   70
00045 
00046 #define MAX_ARGC        MAX_OPTIONS
00047 
00048 #define MIN(a,b) ((a)<(b)?(a):(b))
00049 #define MAX(a,b) ((a)>(b)?(a):(b))
00050 
00051 static int option_class(char *name)
00052 {
00053     int cl;
00054 
00055     switch(name[0]) {
00056     case '-':
00057         cl=OPTION;
00058         break;
00059     case '{':
00060         cl=ENUM_OPTION;
00061         break;
00062     default:
00063         cl=ARGUMENT;
00064         break;
00065     }
00066 
00067     return cl;
00068 } /* end of option_class */
00069 
00070 
00071 /*
00072  * Because parse_ini_file is called recursively,
00073  * we have to forward declare it here
00074  *
00075  */
00076 static int parse_ini_file(char *ini_filename, option_t options[], int option_found[], int nr_options);
00077 
00078 /*
00079  * Returns position of "s" in set "set"
00080  * Returns -1 if not found.
00081  */
00082 static int string2int(char *s, char *set)
00083 {
00084     char         *p;
00085     char         *p2;
00086     unsigned int len;
00087     int          pos;
00088     int          i;
00089 
00090     /* set = "{s1,s2,...,sn}" */
00091     p = set+1;
00092 
00093     pos = -1;
00094     i=0;
00095     do {
00096         p2 = strpbrk(p,",}");
00097         assert (p2 != NULL);
00098 
00099         len=p2-p;
00100         if ((strlen(s)==len) && !strncmp(s,p,len)) {
00101             pos = i;
00102         }
00103         p = p2+1;
00104         i++;
00105     } while( *p2 != '}' );
00106 
00107     return pos;
00108 } /* end of string2int */
00109 
00110 /*
00111  * Returns string at position "pos" in set "set"
00112  *
00113  */
00114 static char *int2string(int pos, char *set)
00115 {
00116     static char  tmp[128];
00117     char         *p;
00118     char         *p2;
00119     unsigned int len;
00120     int          i;
00121 
00122     /* set = "{s1,s2,...,sn}" */
00123     p = set+1;
00124 
00125     strcpy(tmp, "*ERROR*");
00126     i=0;
00127     do {
00128         p2 = strpbrk(p,",}");
00129         assert (p2 != NULL);
00130 
00131         len=p2-p;
00132 
00133         if (i==pos) {
00134             strncpy(tmp, p, len);
00135             tmp[len]='\0';
00136         }
00137 
00138         p = p2+1;
00139         i++;
00140     } while( *p2 != '}' );
00141 
00142     return tmp;
00143 } /* end of int2string */
00144 
00145 static int enum_index(char *list, char *name)
00146 {
00147     int  nr;
00148     char *p;
00149 
00150     /* list="{s1,s2,..,sn}" */
00151     if (list[0] == '{') {
00152         p = list+1;
00153         for(nr=0; p!=NULL; nr++) {
00154             if (!strncmp(p, name, strlen(name))) {
00155                 char c = p[strlen(name)];
00156 
00157                 if ((c==',') || (c=='}')) {
00158                     return nr;
00159                 }
00160             }
00161 
00162             p = strchr(p, ',');
00163             if (p!=NULL) {
00164                 p++;
00165             }
00166         }
00167     }
00168 
00169     return -1;
00170 } /* end of enum_index */
00171 
00172 /*
00173  * Store string "s" is string list "list".
00174  * Memory is automatically reallocated if necessary.
00175  *
00176  */
00177 static void add_string(char ***list, char *s)
00178 {
00179     char **p;
00180     int  i;
00181     int  size;
00182     int  new_size;
00183 
00184     /* Find first free entry */
00185     p=(*list);
00186     size=0;
00187     while((*p!=NULL) && (**p!='\0')) {
00188         p++;
00189         size++;
00190     }
00191 
00192     if (*p==NULL) {
00193         /* List full, reallocate twice the memory */
00194         new_size=2*(size+1);
00195         *list=(char **)realloc(*list, new_size*sizeof(char *));
00196         for(i=size; i<new_size-1; i++) {
00197             (*list)[i] = ""; /* Empty string marks a free entry */
00198         }
00199         (*list)[new_size-1] = NULL; /* NULL marks end of list */
00200         p=(*list)+size;
00201     }
00202 
00203     *p=s;
00204 } /* end of add_string */
00205 
00206 /*
00207  * Interprete value for option and store it in the associated variable.
00208  *
00209  * option:   the option
00210  * value:    value to be interpreted
00211  * location: A string that is used in case of an error. It denoted
00212  *           the location of the value (file:line for ini-files,
00213  *           empty for the cmd-line).
00214  * Return:   success: > 0, failure <=0. The absolute value gives the number of strings used.
00215  *
00216  */
00217 
00218 static int store_strlist(char ***var_p, char *value)
00219 {
00220     /* type="strlist" or "[strlist]" */
00221 
00222     char *tmp;
00223 
00224     tmp = (char *)malloc(strlen(value)+1);
00225     if (tmp == NULL) {
00226         fprintf( stderr, "Cannot allocate memory for string...\n");
00227         exit(1);
00228     }
00229 
00230     strcpy(tmp, value);
00231     add_string(var_p, tmp);
00232 
00233     return 2;
00234 } /* end of store_strlist */
00235 
00236 static int store_str(option_t option, char **var_p, char *value, char *location)
00237 {
00238     /* type="str" or "str{a,b,c}" */
00239 
00240     char *tmp;
00241     int  ret;
00242  
00243     if (value == NULL) {
00244         fprintf( stderr, "%sOption \"%s\" requires a string argument\n", location, option.name );
00245         return -1;
00246     }
00247 
00248     if (!strncmp(option.type, "str{", 4)) {
00249         ret = string2int(value, option.type+3);
00250         if (ret < 0) {
00251             fprintf( stderr, "%sOption \"%s\" does not expect argument \"%s\"\n",
00252                      location, option.name, value );
00253             return -2;
00254         }
00255     }
00256 
00257     tmp = (char *)malloc(strlen(value)+1);
00258     if (tmp == NULL) {
00259         fprintf( stderr, "Cannot allocate memory for string...\n");
00260         exit(1);
00261     }
00262 
00263     strcpy(tmp, value);
00264     *var_p = tmp;
00265 
00266     return 2;
00267 } /* end of store_str */
00268 
00269 static int store_enum(option_t option, int *var_p, char *value)
00270 {
00271     int i;
00272 
00273     assert(value != NULL);
00274 
00275     i = enum_index(option.name, value);
00276     assert(i>=0);
00277 
00278     *var_p = i;
00279 
00280     return 2;
00281 } /* end of store_enum */
00282 
00283 static int store_int_enum(option_t option, int *var_p, char *value, char *location)
00284 {
00285     /* type="int{a,b,c}" */
00286 
00287     int ret;
00288 
00289     if (value == NULL) {
00290         fprintf( stderr, "%sOption \"%s\" requires a string argument\n", location, option.name );
00291         return -1;
00292     }
00293 
00294     ret = string2int(value, option.type+3);
00295     if (ret < 0) {
00296         fprintf( stderr, "%sOption \"%s\" does not expect argument \"%s\"\n",
00297                  location, option.name, value );
00298         return -2;
00299     }
00300 
00301     *var_p = ret;
00302 
00303     return 2;
00304 } /* end of store_int_enum */
00305 
00306 static int store_int(option_t option, int *var_p, char *value, char *location)
00307 {
00308     /* type="int" or "int[min,max]" */
00309 
00310     int      min, max;
00311     int      base;
00312     long          i;
00313     unsigned long u;
00314     char     *no1;
00315     char     *no2;
00316     char     *comma;
00317     char     *bracket;
00318 
00319     if (value == NULL) {
00320         base=10;
00321         i=0;
00322         u=0;
00323     }
00324     else {
00325         /* Prefix "0x" or "0X" indicates hexadecimal notation.
00326          * Otherwise, we assume decimal notation.
00327          */
00328         base=((value[0]=='0') && (toupper(value[1])=='X')) ? 16 : 10;
00329         if (base == 10) {
00330             i=strtol(value, NULL, base);
00331             u=0;
00332         }
00333         else {
00334             u=strtoul(value, NULL, base);
00335             i=(long)u; /* in case of range checking, it must fit in a signed */
00336         }
00337     }
00338     
00339     if (option.type[3]=='[') {
00340         /* expected format: "int[%d,%d]" -- sscanf is not used due to purify errors */
00341         no1     = &option.type[4];   assert(no1 != NULL);
00342         comma   = strchr(no1, ',');  assert(comma != NULL);
00343         no2     = comma+1;           assert(no2 != NULL);
00344         bracket = strchr(no2, ']');  assert(bracket != NULL);
00345         min = atoi(no1);
00346         max = atoi(no2);
00347 
00348         if ((value == NULL) || (i<min) || (i>max)) {
00349             fprintf( stderr, "%sOption \"%s\" requires an integer argument with range [%d,%d]\n",
00350                      location, option.name, min, max);
00351             return -2;
00352         }
00353     }
00354     else {
00355         if (value == NULL) {
00356             fprintf( stderr, "%sOption \"%s\" requires an integer argument\n",
00357                      location, option.name);
00358             return -1;
00359         }
00360     }
00361     
00362     if (base == 10) {
00363         *var_p = i;
00364     }
00365     else {
00366         *(unsigned int *)var_p = u;
00367     }
00368 
00369     return 2;
00370 } /* end of store_int */
00371 
00372 static int store_double(double *var_p, char *value)
00373 {
00374     /* type="double" */
00375 
00376     double d;
00377 
00378     d = (value == NULL) ? (double)0.0 : atof(value);
00379     *var_p = d;
00380 
00381     return 2;
00382 } /* end of store_double */
00383 
00384 static int store_float(float *var_p, char *value)
00385 {
00386     /* type="float" */
00387 
00388     float f;
00389 
00390     f = (value == NULL) ? (float)0.0 : (float)atof(value);
00391     *var_p = f;
00392 
00393     return 2;
00394 } /* end of store_float */
00395 
00396 static int store_char(char *var_p, char *value)
00397 {
00398     /* type="char" */
00399 
00400     char c;
00401 
00402     c = (value == NULL) ? 0 : *value;
00403     *var_p = c;
00404 
00405     return 2;
00406 } /* end of store_char */
00407 
00408 static int store_bool(int *var_p, char *value)
00409 {
00410     /* type="bool" or "cmd" */
00411 
00412     *var_p = ((value != NULL) && !strcmp(value, "true"));
00413     return 1;
00414 } /* end of store_bool */
00415 
00416 static int store_value(option_t option, char *value, char *location)
00417 {
00418     int  ret;
00419     char *tmp;
00420     int  i;
00421     int  count;
00422 
00423     tmp   = strchr(option.type, '#');
00424     count = (tmp==NULL) ? 1 : atoi(tmp+1);
00425 
00426     if (strstr(option.type, "strlist")!=NULL) {
00427         assert(count==1);
00428         ret=store_strlist(option.var, value);
00429     }
00430     else if (!strncmp(option.type, "str", 3)) {
00431         assert(count==1);
00432         ret=store_str(option, option.var, value, location);
00433     }
00434     else if (!strcmp(option.type, "enum")) {
00435         assert(count==1);
00436         ret=store_enum(option, option.var, value);
00437     }
00438     else if (!strcmp(option.type, "char")) {
00439         assert(count==1);
00440         ret=store_char(option.var, value);
00441     }
00442     else if (!strcmp(option.type, "bool") || !strcmp(option.type, "cmd")) {
00443         assert(count==1);
00444         ret=store_bool(option.var, value);
00445     }
00446     else if (!strncmp(option.type, "int", 3) || !strncmp(option.type, "double", 6)  || !strncmp(option.type, "float", 5)) {
00447         tmp=value;
00448         ret=1;
00449         for(i=0; i<count; i++) {
00450             if (option.type[3] == '{') {
00451                 ret=store_int_enum(option, ((int *)option.var)+i, tmp, location);
00452             }
00453             else if (option.type[0] == 'i') {
00454                 ret=store_int(option, ((int *)option.var)+i, tmp, location);
00455             }
00456             else if (option.type[0] == 'd') {
00457                 ret=store_double(((double *)option.var)+i, tmp);
00458             }
00459             else {
00460                 ret=store_float(((float *)option.var)+i, tmp);
00461             }
00462 
00463             /* Error exit */
00464             if (ret < 0) {
00465                 return ret;
00466             }
00467 
00468             /* Shift pointer to next item from option values next */
00469             tmp=strchr(tmp, ',');
00470 
00471             if (((i<count-1)&&(tmp==NULL)) || ((i==count-1)&&(tmp!=NULL))) {
00472                 fprintf( stderr, "%sOption \"%s\" expects %d values\n",
00473                          location, option.name, count );
00474                 ret=-2;
00475                 break;
00476             }
00477 
00478             if (i<count-1) {
00479                 tmp++;
00480             }
00481         }
00482     }
00483     else {
00484         /* Assign a value to ret to avoid compiler warnings */
00485         ret=0;
00486 
00487         fprintf( stderr, "%sOption \"%s\" has unknown type \"%s\"\n",
00488                  location, option.name, option.type );
00489         exit(1);
00490     }
00491 
00492     return ret;
00493 } /* end of store_value */
00494 
00495 static char *get_value(option_t *option)
00496 {
00497     static char str[1024];
00498     int         i;
00499     char        *p;
00500     char        *p2;
00501     char        **q;
00502     void        *var;
00503 
00504     char *p3   = strchr(option->type, '#');
00505     int  count = (p3==NULL) ? 1 : atoi(p3+1);
00506 
00507     var = option->var;
00508 
00509 
00510     str[0]='\0';
00511     while(count--) {
00512         char *end = str+strlen(str);
00513 
00514         if (strstr(option->type, "strlist")) {
00515             /* String list */
00516             q=*(char ***)var;
00517             while(*q!=NULL) {
00518                 strcpy(end+strlen(end), *q);
00519                 q++;
00520                 if (*q!=NULL) {
00521                     strcpy(end+strlen(end), " ");
00522                 }
00523             }
00524         }
00525         else if (strstr(option->type, "str")) {
00526             /* String value */
00527             p = *((char **)var);
00528             if (strlen(p) == 0) {
00529                 strcpy(end, "\"\"");
00530             } else if (strchr(p, ' ') == NULL) {
00531                 strcpy(end, p);
00532             } else {
00533                 strcpy(end, "\"");
00534                 strcpy(end+1, p);
00535                 strcpy(end+1+strlen(p), "\"");
00536             }
00537         }
00538         else if (!strcmp(option->type, "enum")) {
00539             /* Enumerator option */
00540             p = option->name+1;
00541             for(i=0; i<*(int *)var; i++) {
00542                 p = strchr(p, ',')+1;
00543             }
00544             p2 = strchr(p, ',');
00545             if (p2==NULL) {
00546                 p2 = strchr(p, '}');
00547             }
00548             strncpy(end, p, p2-p);
00549             end[p2-p]='\0';
00550         }
00551         else if (!strncmp(option->type, "int{", 4)) {
00552             strcpy(end, int2string(*(int *)var, option->type+3));
00553             var = ((int *)var)+1;
00554         }
00555         else if (!strncmp(option->type, "int", 3)) {
00556             sprintf(end, "%d", *(int *)var);
00557             var = ((int *)var)+1;
00558         }
00559         else if (!strncmp(option->type, "double", 6)) {
00560             sprintf(end, "%6.4f", *(double *)var);
00561             var = ((double *)var)+1;
00562         }
00563         else if (!strncmp(option->type, "float", 5)) {
00564             sprintf(end, "%6.4f", *(float *)var);
00565             var = ((float *)var)+1;
00566         }
00567         else if (!strncmp(option->type, "char", 4)) {
00568             sprintf(end, "%c", *(char *)var);
00569             var = ((char *)var)+1;
00570         }
00571         else if (!strcmp(option->type, "bool")) {
00572             sprintf(end, "%s%s", *(int *)var ? "" : "-no", option->name);
00573         }
00574 
00575         if (count!=0) {
00576             strcpy(end+strlen(end), ",");
00577         }
00578     }
00579 
00580     return str;
00581 } /* end of get_value */
00582 
00583 /*
00584  * Interpret (name,value) and store the value in the associated variable.
00585  *
00586  * name:         Option name (in case of an argument, this is the argument itself)
00587  * value:        Value of the option (for options: may be NULL, for arguments: always NULL)
00588  * location:     A string that is used in case of an error. It denoted
00589  *               the location of the value (file:line for ini-files,
00590  *               empty for the cmd-line).
00591  * options:      The list of possible options/arguments
00592  * option_found: A list of booleans telling which option was already found
00593  *
00594  * Return:       success: > 0, failure <=0. The absolute value gives the number of strings used.
00595  *
00596  */
00597 static int parse_option(char *name, char *value, char *location, option_t options[], int option_found[],
00598                         int nr_options)
00599 {
00600     int nr;
00601     int err;
00602     int i;
00603 
00604     if ((name[0] != '-') || (name[1] == '\0')) {
00605         /* This is a normal argument (i.e. no option) */
00606         for(i=0; i<nr_options; i++) {
00607             if ((option_class(options[i].name) == ARGUMENT) && !option_found[i]) {
00608                 /* in case of an argument, the name itself is the value */
00609                 nr=store_value(options[i],name,location);
00610                 if (!strstr(options[i].type, "strlist")) {
00611                     option_found[i] = 1;
00612                 }
00613                 return 1;
00614             }
00615         }
00616 
00617         fprintf( stderr, "%sError: unexpected argument \"%s\"\n", location, name );
00618         return -1;
00619     }
00620     else if (!strcmp(name, "-ini")) {
00621         if (value != NULL) {
00622             err = parse_ini_file(value, options, option_found, nr_options);
00623             if (err) {
00624                 fprintf(stderr, "Problem parsing ini-file \"%s\"\n", value);
00625                 return -2;
00626             }
00627             else {
00628                 return 2;
00629             }
00630         }
00631         else {
00632             fprintf( stderr, "%sOption \"%s\" requires a filename argument\n", location, name);
00633             return -1;
00634         }
00635     }
00636     else {
00637         /* Check enumeration options (i.e. options[i].name == "{-a,-b,-c}") */
00638         for(i=0; i != nr_options; i++) {
00639             if (enum_index(options[i].name, name)>=0) {
00640                 /* in case of an enumerated options, the name itself is the value */
00641                 nr = store_value(options[i], name, location);
00642                 option_found[i] = 1;
00643                 return 1;
00644             }
00645         }
00646 
00647         /* Check boolean options */
00648         for(i=0; i != nr_options; i++) {
00649             if (!strcmp(options[i].type, "bool") || !strcmp(options[i].type, "cmd")) {
00650                 if (!strcmp(options[i].name, name)) {
00651                     value = "true";
00652                 }
00653                 else if (!strncmp(name, "-no-", 4) && !strcmp(options[i].name, name+3)) {
00654                     value = "false";
00655                 }
00656                 else {
00657                     /* Not a boolean option */
00658                     continue;
00659                 }
00660                 
00661                 nr = store_value(options[i], value, location);
00662                 option_found[i] = 1;
00663                 return nr;
00664             }
00665         }
00666 
00667         /* Check normal options */
00668         for(i=0; i != nr_options; i++) {
00669             if (!strcmp(options[i].name, name)) {
00670                 nr = store_value(options[i], value, location);
00671                 if (nr != 0) {
00672                     if (nr>0) {
00673                         option_found[i] = 1;
00674                     }
00675                     return nr;
00676                 }
00677             }
00678         }
00679     }
00680 
00681     fprintf( stderr, "%sError: unrecognized option \"%s\"\n", location, name );
00682     return -1;
00683 } /* end of parse_option */
00684 
00685 /*
00686  * Remove comment (starting with #); also remove \n
00687  *
00688  */
00689 static void remove_comment(char *s)
00690 {
00691     char *ch = s + strlen(s) - 1; /* skip \0 */
00692 
00693     /* remove trailing \n  */
00694     if (*ch == '\n')
00695         *ch = '\0';
00696 
00697     /* remove comment */
00698     ch = strchr(s, '#');
00699     if (ch != NULL)
00700         *ch = '\0';
00701 
00702 } /* end of remove_comment */
00703 
00704 /*
00705  * Remove tabs (turn into spaces)
00706  *
00707  */
00708 static void remove_tabs(char *s)
00709 {
00710     char *ch = s + strlen(s) - 1; /* skip \0 */
00711 
00712     /* turn each tab character into a single space */
00713     while (ch >= s) {
00714         if (*ch == '\t')
00715             *ch = ' ';
00716         ch--;
00717     }
00718 } /* end of remove_tabs */
00719 
00720 /*
00721  * Remove leading spaces (and tabs)
00722  *
00723  */
00724 static void remove_leading_spaces(char *s)
00725 {
00726     char *ch = s;
00727 
00728     /* find first non-space character */
00729     while (*ch == ' ')
00730         ch++;
00731 
00732     /* remove leading spaces */
00733     if (ch != s) {
00734         while (*ch != '\0')
00735             *s++ = *ch++;
00736         *s = '\0';
00737     }
00738 } /* end of remove_leading_spaces */
00739 
00740 /*
00741  * Remove trailing spaces
00742  *
00743  */
00744 static void remove_trailing_spaces(char *s)
00745 {
00746     char *end = s + strlen(s) - 1; /* skip \0 */
00747 
00748     /* remove trailing spaces */
00749     while ((end > s) && (*end == ' ')) {
00750         *end = '\0';
00751         end--;
00752     }
00753 } /* end of remove_trailing_spaces */
00754 
00755 /*
00756  * Remove spaces around equal sign
00757  *
00758  */
00759 static void remove_eqsign_spaces(char *s)
00760 {
00761     char *eqsign, *chleft, *chright;
00762 
00763     /* remove spaces before equal sign */
00764     eqsign = strchr(s, '=');
00765     if (eqsign != NULL) {
00766         /* find first non-space character after '=' */
00767         chright = eqsign + 1;
00768         while (*chright == ' ')
00769             chright++;
00770 
00771         /* find first non-space character before '=' */
00772         chleft = eqsign - 1;
00773         while ((*chleft == ' ') && (chleft >= s))
00774             chleft--;
00775 
00776         /* remove spaces */
00777         chleft++;
00778         *chleft++ = '=';
00779         while (*chright != '\0')
00780             *chleft++ = *chright++;
00781         *chleft = '\0';
00782     }
00783 } /* end of remove_eqsign_spaces */
00784 
00785 /*
00786  * Parse all lines of an ini-file.
00787  *
00788  * ini_filename: Name of the file
00789  * options :     The list of possible options/arguments
00790  * option_found: A list of booleans telling which option was already found
00791  * nr_options:   Length of the "options" array.
00792  *
00793  * Return:       success: zero, failure: non-zero
00794  *
00795  */
00796 static int parse_ini_file(char *ini_filename, option_t options[], int option_found[],
00797                           int nr_options)
00798 {
00799     char buf[MAX_LINE_LENGTH];
00800     char location[MAX_FILENAME];
00801     FILE *f;
00802     int  line;
00803     char *name, *value;
00804     char *p;
00805     char *name_ptr, *val_ptr;
00806     char name_val, val_val;
00807     int  nr;
00808     int  runin;         /* Still in runin; windows or standard not yet known */
00809     int  winfmt;        /* This is a windows format .ini file */
00810     int  arguments;     /* Windows parser working on arguments */
00811     char *eqsign;       /* Location of "=" in buf */
00812     char arg[MAX_FILENAME+40];  /* Standard option string from win .ini file */
00813 
00814     f = fopen(ini_filename, "r");
00815     if (f == NULL) {
00816         return -1;
00817     }
00818 
00819     line = 1;
00820     runin = 1;
00821     winfmt = 0;
00822     arguments = 0;
00823     while( fgets(buf, MAX_LINE_LENGTH-1, f), !feof(f) ) {
00824         sprintf(location, "%s:%d ", ini_filename, line++);
00825 
00826         remove_comment(buf);
00827         remove_tabs(buf);
00828         remove_leading_spaces(buf);
00829         remove_trailing_spaces(buf);
00830 
00831         if (runin) {
00832             /* Check whether this is a windows style .ini file */
00833             if (*buf != '\0') { /* skip empty lines */
00834                 /* Windows format determined by first non-empty line "[xxx]" */
00835                 if ((buf[0] == '[') && (buf[strlen(buf)-1] == ']')) {
00836                     winfmt = 1;
00837                 }
00838 
00839                 runin = 0;
00840             }
00841         }
00842 
00843         if (winfmt) {
00844             /*
00845              * Windows-style .ini file parser
00846              */
00847             remove_eqsign_spaces(buf);
00848 
00849             if (!strcmp(buf, "[arguments]")) {
00850                 arguments = 1;  /* following lines are mandatory arguments */
00851             } else if (*buf == '[') {
00852                 arguments = 0;  /* following lines are options */
00853                 if (strchr(buf, ']') == NULL) {
00854                     fprintf(stderr, "%sError: missing closing bracket\n", location);
00855                     fclose(f);
00856                     return 1;
00857                 }
00858                 if (*(strchr(buf, ']') + 1) != '\0') {
00859                     fprintf(stderr, "%sError: illegal syntax\n", location);
00860                     fclose(f);
00861                     return 1;
00862                 }            
00863             } else if (*buf != '\0') { /* skip empty lines */
00864                 eqsign = strchr(buf, '=');
00865                 if (eqsign == buf) {
00866                     fprintf(stderr, "%sError: missing option name\n", location);
00867                     fclose(f);
00868                     return 1;
00869                 }
00870                 if (eqsign == NULL) {
00871                     fprintf(stderr, "%sError: missing '='\n", location);
00872                     fclose(f);
00873                     return 1;
00874                 }
00875 
00876                 if (arguments) {
00877                     /* skip option name; use value as argument */
00878                     value = eqsign + 1; 
00879                     nr = parse_option(value, NULL, location, options, option_found, nr_options);
00880                     if (nr<=0) {
00881                         fclose(f);
00882                         return 1;
00883                     }
00884                 } else {
00885                     /* separate name and value */
00886                     *eqsign = '\0';
00887                     name    = buf;
00888                     value   = eqsign + 1; 
00889 
00890                     /* add name only if value is defined */
00891                     if (*value != '\0') {
00892                         /* prefix name with '-' */
00893                         strcpy(arg, "-");
00894 
00895                         /* check if value is boolean */
00896                         if (!strcmp(value, "true") || !strcmp(value, "false")) {
00897                             /* prefix "no-" for false value */
00898                             if (!strcmp(value, "false"))
00899                                 strcat(arg, "no-");
00900 
00901                             /* add name to arg */
00902                             strcat(arg, name);
00903 
00904                             /* add arg to Argv */
00905                             nr = parse_option(arg, NULL, location, options, option_found, nr_options);
00906                             if (nr<=0) {
00907                                 fclose(f);
00908                                 return 1;
00909                             }
00910                         }
00911                         else {
00912                             /* add name to arg */
00913                             strcat(arg, name);
00914 
00915                             if ((value[0] == '"') && (value[strlen(value)-1] == '"')) {
00916                                 /* Strip quotes from value */
00917                                 value++;
00918                                 value[strlen(value)-1] = '\0';
00919                             }
00920                             nr = parse_option(arg, value, location, options, option_found, nr_options);
00921                             if (nr<=0) {
00922                                 fclose(f);
00923                                 return 1;
00924                             }
00925                         }
00926                     }
00927                 }
00928             }
00929 
00930         } else {
00931             /*
00932              * Standard .ini file parser
00933              */
00934             name = buf;
00935             do {
00936                 name_ptr = val_ptr = name;
00937                 name_val = val_val = *name;
00938 
00939                 value = NULL;
00940                 while((*name) && isspace((int)*name))
00941                     name++;     /* Skip white space */
00942 
00943                 p = name;
00944                 while((*p) && !isspace((int)*p))
00945                     p++;
00946 
00947                 if (*p) {
00948                     /* Space after name found */
00949                     name_ptr = p;
00950                     name_val = *p;
00951                     *p = '\0';   /* terminate name */
00952 
00953                     value = p+1;
00954                     while((*value) && isspace((int)*value))
00955                         value++;     /* Skip white space */
00956 
00957                     /* Handle quoted string */
00958                     if (*value == '"') {
00959                         value++;
00960                         p = value;
00961                         while((*p) && (*p != '"')) {
00962                             p++;
00963                         }
00964                         if (*p == '"') {
00965                             val_ptr = p;
00966                             val_val = *p;
00967                             *p = '\0';
00968                         }
00969                     } else {
00970                         p = value;
00971                         while((*p) && !isspace((int)*p))
00972                             p++;
00973                         if (*p) {
00974                             /* space after value found */
00975                             val_ptr = p;
00976                             val_val = *p;
00977                             *p = '\0';
00978                         }
00979                     }
00980 
00981                     if (*value == '\0') {
00982                         value = NULL;
00983                     }
00984                 }
00985 
00986                 if (name[0] == '\0') {
00987                     name = NULL;
00988                 }
00989 
00990                 if (name != NULL) {
00991                     nr = parse_option(name, value, location, options, option_found, nr_options);
00992                     if (nr<=0) {
00993                         fclose(f);
00994                         return 1;
00995                     }
00996                     switch (nr) {
00997                     case 1:
00998                         /* One argument processed, value holds start of next */
00999                         name = value;
01000                         break;
01001                     case 2:
01002                         /* Two arguments processed, start immediately after value */
01003                         name = value + strlen(value) + 1;
01004                         break;
01005                     default:
01006                         name = NULL;
01007                         break;
01008                     }
01009                 }
01010                 *name_ptr = name_val;
01011                 *val_ptr = val_val;
01012             } while (name != NULL);
01013         }
01014     }
01015 
01016     fclose(f);
01017     return 0;
01018 } /* end of parse_ini_file */
01019 
01020 /*
01021  * Parse the complete command line.
01022  *
01023  * argc:         Length of the "argv" array.
01024  * argv[]:       List of strings.
01025  * options :     The list of possible options/arguments
01026  * option_found: A list of booleans telling which option was already found
01027  * nr_options:   Length of the "options" array.
01028  *
01029  * Return:       success: zero, failure: non-zero
01030  *
01031  */
01032 static int parse_cmd_line(int argc, char *argv[], option_t options[], int option_found[],
01033                           int nr_options )
01034 {
01035     int nr;
01036 
01037     argv++;
01038     argc--;
01039     while(argc>0) {
01040         nr = parse_option(argv[0], argc>0 ? argv[1] : NULL, "", options, option_found, nr_options);
01041         if (nr<=0) {
01042             return 1;
01043         }
01044 
01045         argv += nr;
01046         argc -= nr;
01047     }
01048 
01049     return 0;
01050 } /* end of parse_cmd_line */
01051 
01052 /*
01053  * Builds the name of the .ini file from the application name.
01054  * Caller must allocate sufficient space in ini_filename.
01055  */
01056 static void build_ini_filename(char *ini_filename, char *appl_name)
01057 {
01058     int i, j=0;
01059 
01060     /* appl name can contain white spaces ' ' --> replace them by underscore */
01061     /* appl name can contain hyphens '-' --> skip them */
01062     for (i=0; appl_name[i]!='\0'; i++) {
01063         if (appl_name[i] == ' ')
01064             ini_filename[j++] = '_';
01065         else
01066         if (appl_name[i] != '-')
01067             ini_filename[j++] = appl_name[i];
01068     }
01069     sprintf(&ini_filename[j], ".ini");
01070 
01071 } /* end of build_ini_filename */
01072 
01073 /*
01074  * Formatted printing to file or string.
01075  * If f != NULL: prints to file, s and len not used.
01076  * Otherwise: prints to string s, len limits output.
01077  */
01078 static int print(FILE *f, char **s, int *len, char *fmt, ...)
01079 {
01080     int  n;
01081     char buf[BUF_LEN];
01082 
01083     va_list ap;
01084     va_start(ap, fmt);
01085     n=vsprintf(buf, fmt, ap);
01086     va_end(ap);
01087 
01088     if (f != NULL) {
01089         fprintf(f, "%s", buf);
01090     }
01091     else {
01092         if (*len>n) {
01093             strcpy(*s, buf);
01094             *s+=n;
01095             *len-=n;
01096         }
01097         else {
01098             n=0;
01099         }
01100     }
01101 
01102     return n;
01103 }  /* end of print */
01104 
01105 /*
01106  * Print a list of all arguments and options.
01107  * If f != NULL: prints to file, s and len not used.
01108  * Otherwise: prints to string s, len limits output.
01109  */
01110 static int gen_print_options(FILE *f, char *s, int len, char *appl_name, option_t options[])
01111 {
01112     option_t     *option;
01113     unsigned int name_width=MIN_NAME_WIDTH;
01114     unsigned int type_width=MIN_TYPE_WIDTH;
01115     unsigned int dflt_width=MIN_DFLT_WIDTH;
01116     int          bools_present;
01117     char         ini_filename[MAX_FILENAME];
01118 
01119     bools_present=0;
01120     for(option=options;option->name!=NULL; option++) {
01121         if (strlen(option->name)<MAX_NAME_WIDTH) {
01122             name_width = MAX(name_width, strlen(option->name));
01123         }
01124         if (strlen(option->type)<MAX_TYPE_WIDTH) {
01125             type_width = MAX(type_width, strlen(option->type));
01126         }
01127         if ((option->default_value!=NULL) && (strlen(option->default_value)<MAX_DFLT_WIDTH)) {
01128             dflt_width = MAX(dflt_width, strlen(option->default_value));
01129         }
01130         if (!strcmp(option->type, "bool")) {
01131             bools_present=1;
01132         }
01133     }
01134 
01135     if (!print(f, &s, &len, "Usage: %s [options]", appl_name)) return 0;
01136     for(option=options;option->name!=NULL; option++) {
01137         if (option_class(option->name) == ARGUMENT) {
01138             if (!print(f, &s, &len, " %s", option->name)) return 0;
01139         }
01140     }
01141     if (!print(f, &s, &len, "\n\n")) return 0;
01142 
01143     if (!print(f, &s, &len, " %-*s  %-*s  %-*s  %s\n",
01144             name_width, "name",
01145             type_width, "type",
01146             dflt_width, "default",
01147             "help text")) return 0;
01148 
01149     if (!print(f, &s, &len, " %-*s  %-*s  %-*s  %s\n",
01150             name_width, "----",
01151             type_width, "----",
01152             dflt_width, "-------",
01153             "---------")) return 0;
01154 
01155     if (!print(f, &s, &len,  " %-*s  %-*s  %-*s  %s\n",
01156             name_width, "-ini",
01157             type_width, "str",
01158             dflt_width, "",
01159             "Parse options from an ini-file")) return 0;
01160 
01161     option=options;
01162     while(option->name != NULL) {
01163         if (!print(f, &s, &len, " ")) return 0;
01164         if (!print(f, &s, &len, "%-*s", name_width, option->name)) return 0;
01165         if (strlen(option->name)>name_width) {
01166             if (!print(f, &s, &len, "\n %-*s", name_width, "")) return 0;
01167         }
01168 
01169         if (!print(f, &s, &len, "  ")) return 0;
01170         if (!print(f, &s, &len, "%-*s", type_width, option->type)) return 0;
01171         if (strlen(option->type)>type_width) {
01172             if (!print(f, &s, &len, "\n %-*s  %-*s", name_width, "", type_width, "")) return 0;
01173         }
01174 
01175         if (!print(f, &s, &len, "  ")) return 0;
01176         if (option->default_value != NULL) {
01177             if (!print(f, &s, &len,  "%-*s", dflt_width, option->default_value)) return 0;
01178         }
01179         else {
01180             if (!print(f, &s, &len,  "%-*s", dflt_width, "")) return 0;
01181         }
01182 
01183         if (!print(f, &s, &len, "  ")) return 0;
01184         if (!print(f, &s, &len, "%s", option->help)) return 0;
01185         if (!print(f, &s, &len, "\n")) return 0;
01186 
01187         option++;
01188     }
01189 
01190     build_ini_filename(ini_filename, appl_name);
01191     if (!print(f, &s, &len, "Processing order: defaults, optional %s file, cmdline args\n", ini_filename)) return 0;
01192 
01193     if (bools_present) {
01194         if (!print(f, &s, &len, "Note: for boolean options you can specify \"-opt\" and \"-no-opt\"\n")) return 0;
01195     }
01196 
01197     return 1;
01198 } /* end of gen_print_options */
01199 
01200 /*
01201  * Option list (help text) printing.
01202  * See header file for parameter descriptions.
01203  */
01204 int sprint_options(char *s, int len, char *appl_name, option_t options[])
01205 {
01206     return gen_print_options(NULL, s, len, appl_name, options);
01207 } /* end of sprint_options */
01208 
01209 int fprint_options(FILE *f, char *appl_name, option_t options[])
01210 {
01211     return gen_print_options(f, NULL, 0, appl_name, options);
01212 } /* end of fprint_options */
01213 
01214 int print_options(char *appl_name, option_t options[])
01215 {
01216     return gen_print_options(stdout, NULL, 0, appl_name, options);
01217 } /* end of print_options */
01218 
01219 /*
01220  * Print a list of the values of all arguments and options.
01221  * If f != NULL: prints to file, s and len not used.
01222  * Otherwise: prints to string s, len limits output.
01223  */
01224 static int gen_print_option_values(FILE *f, char *s, int len, char *prefix, option_t options[])
01225 {
01226     option_t     *option;
01227     unsigned int name_width=MIN_NAME_WIDTH;
01228     unsigned int line_len=MAX_LINE_WIDTH;
01229     unsigned int n;
01230     char         buf[BUF_LEN];
01231 
01232     memset(buf, 0, BUF_LEN);
01233 
01234     for(option=options;option->name!=NULL; option++) {
01235         if (strlen(option->name)<MAX_NAME_WIDTH) {
01236             name_width = MAX(name_width, strlen(option->name));
01237         }
01238     }
01239 
01240     line_len -= print(f, &s, &len, "%s", prefix);
01241 
01242     for (option=options; option->name != NULL; option++) {
01243         if (!strcmp(option->type, "cmd")) {
01244             /* Don't print command options */
01245             continue;
01246         }
01247 
01248         strcpy(buf, " ");
01249         if ((option_class(option->name) == OPTION) && strcmp(option->type, "bool")) {
01250             sprintf(buf+strlen(buf), "%s ", option->name);
01251         }
01252         sprintf(buf+strlen(buf), "%s", get_value(option));
01253         n=strlen(buf);
01254         if ((s==NULL) && (line_len<n)) {
01255             line_len=MAX_LINE_WIDTH-n;
01256             if (!print(f, &s, &len, "\n")) return 0;
01257         } else {
01258             line_len-=n;
01259         }
01260         
01261         if (!print(f, &s, &len, "%s", buf)) return 0;
01262     }
01263 
01264     if (s==NULL) {
01265         if (!print(f, &s, &len, "\n")) return 0;
01266     }
01267 
01268     return 1;
01269 } /* end of gen_print_option_values */
01270 
01271 /*
01272  * Option value printing.
01273  * See header file for parameter descriptions.
01274  */
01275 int sprint_option_values(char *s, int len, char *prefix, option_t options[])
01276 {
01277     return gen_print_option_values(NULL, s, len, prefix, options);
01278 } /* end of sprint_option_values */
01279 
01280 int fprint_option_values(FILE *f, char *prefix, option_t options[])
01281 {
01282     return gen_print_option_values(f, NULL, 0, prefix, options);
01283 } /* end of fprint_option_values */
01284 
01285 int print_option_values(char *prefix, option_t options[])
01286 {
01287     return gen_print_option_values(stdout, NULL, 0, prefix, options);
01288 } /* end of print_option_values */
01289 
01290 /*
01291  * Parse ini-file and command line.
01292  * See header file for parameter descriptions.
01293  */
01294 int parse_options(char *appl_name, int argc, char *argv[], option_t options[])
01295 {
01296     int      i, j, n;
01297     int      err;
01298     int      nr_options;
01299     option_t *option;
01300     int      option_found[MAX_OPTIONS];
01301     char     ini_filename[MAX_FILENAME];
01302     char     **strlist;
01303 
01304     nr_options = 0;
01305     option=options;
01306     while(option->name != NULL) {
01307         nr_options++;
01308         option++;
01309     }
01310 
01311 
01312     /* For all options, store default value (if available) */
01313     for(i=0; i != nr_options; i++) {
01314         char *def = options[i].default_value;
01315 
01316         if (strstr(options[i].type, "strlist")) {
01317             char **list=(char **)malloc(2*sizeof(char *));
01318             list[0] = "";
01319             list[1] = NULL;
01320 
01321             *(char ***)options[i].var = list;
01322             option_found[i] = 0;
01323         }
01324         else if (!strcmp(options[i].type, "bool") || !strcmp(options[i].type, "cmd")) {
01325             /* "true" gives default value 1, otherwise 0. */
01326             *(int *)options[i].var = ((def != NULL) && !strcmp(def, "true"));
01327             option_found[i] = (def != NULL);
01328         }
01329         else if (options[i].default_value != NULL) {
01330             (void)store_value(options[i], def, "INTERNALPROBLEM: ");
01331             option_found[i] = (option_class(options[i].name) != ARGUMENT); /* arguments are never found by default */
01332         }
01333         else {
01334             /* Otherwise */
01335             option_found[i] = 0;
01336         }
01337     }
01338 
01339     build_ini_filename(ini_filename, appl_name);
01340     err = parse_ini_file(ini_filename, options, option_found, nr_options);
01341     if (err>0) {
01342         return 1;
01343     }
01344 
01345     err = parse_cmd_line(argc, argv, options, option_found, nr_options);
01346     if (err) {
01347         return 1;
01348     }
01349 
01350     /*
01351      * If one of the options is a string list, it now has consumed
01352      * not only its own arguments, but also the argument for options
01353      * below in the options[] array.
01354      * In this case, we move the string from the list to the variables
01355      * of the later arguments.
01356      */
01357     for(i=0; i<nr_options; i++) {
01358         if (strstr(options[i].type, "strlist")) {
01359             /* String list option found */
01360             strlist=*((char ***)options[i].var);
01361             for(n=0; strlist[n]!=NULL; n++) {
01362                 /* Find last entry in string list */
01363                 if (strlist[n][0]=='\0') {
01364                     strlist[n]=NULL;
01365                     break;
01366                 }
01367             }
01368 
01369             for(j=nr_options-1; j>i; j--) {
01370                 if (option_class(options[j].name)==ARGUMENT) {
01371                     /* Move last entry from string list to this argument value */
01372                     if (n>0) {
01373                         n--;
01374                         *((char **)options[j].var)=strlist[n];
01375                         strlist[n]=NULL;
01376                         option_found[j]=1;
01377                     }
01378                 }
01379             }
01380 
01381             option_found[i]=(strcmp(options[i].type, "strlist") || (n>0));
01382             break;
01383         }
01384     }
01385 
01386     /* Check if a command was given */
01387     for(i=0; i != nr_options; i++) {
01388         if (option_found[i] && !strcmp(options[i].type, "cmd")) {
01389             err=0;
01390             for(j=i+1; j!=nr_options; j++) {
01391                 if (option_found[j] && !strcmp(options[j].type, "cmd")) {
01392                     /* Two or more commands given */
01393                     fprintf(stderr, "Only one command option is allowed\n");
01394                     err=1;
01395                 }
01396             }
01397             return err;
01398         }
01399     }
01400 
01401     /* Check if mandatory arguments/options are missing */
01402     err=0;
01403     for(i=0; i != nr_options; i++) {
01404         if (!option_found[i] && strcmp(options[i].type, "cmd") && options[i].default_value==NULL) {
01405             err = 1;
01406             switch(option_class(options[i].name)) {
01407             case ARGUMENT:
01408                 fprintf( stderr, "Mandatory argument \"%s\" missing\n", options[i].name );
01409                 break;
01410             case OPTION:
01411                 fprintf( stderr, "Mandatory option \"%s\" missing\n", options[i].name );
01412                 break;
01413             case ENUM_OPTION:
01414                 fprintf( stderr, "Mandatory option from \"%s\" missing\n", options[i].name );
01415                 break;
01416             default:
01417                 fprintf( stderr, "Internal error, invalid option_class(\"%s\") return value\n", options[i].name );
01418                 break;
01419             }
01420         }
01421     }
01422     return err;
01423 } /* end of parse_options */

Generated on Wed Feb 15 14:52:42 2006 for util by doxygen 1.3.2