00001 #ifdef LINUX
00002
00003 #ifndef _GNU_SOURCE
00004 #define _GNU_SOURCE
00005 #endif
00006
00007
00008 #ifndef __USE_BSD
00009 #define __USE_BSD
00010 #endif
00011
00012 #endif
00013
00014 #include <errno.h>
00015 #include <limits.h>
00016 #include <stdlib.h>
00017 #include <stdio.h>
00018 #include <sys/param.h>
00019 #include <ctype.h>
00020 #include <sys/stat.h>
00021 #include <unistd.h>
00022 #include <libgen.h>
00023 #include <string.h>
00024
00025 #include "options.h"
00026
00027
00028 void display_table(FILE*f, char**table, int rows, int columns, int padding);
00029
00030
00031 const char * options_banner_string = "";
00032
00033 void options_banner(const char*message) {
00034 options_banner_string = message;
00035 }
00036
00038 char * strdup_(const char *s) {
00039 size_t len = strlen(s) + 1;
00040 char * t = (char*) malloc(len);
00041 memcpy(t,s,len);
00042 return t;
00043 }
00044
00045
00047 int get_int(int*p, const char*s) {
00048 int value;
00049 errno = 0;
00050 value = strtol(s, (char **)NULL, 10);
00051 if(0 == errno) {
00052 *p = value;
00053 return 1;
00054 } else return 0;
00055 }
00056
00058 int get_double(double*p, const char*s) {
00059 char *endptr;
00060 *p = strtod(s, &endptr);
00061 return endptr != s;
00062 }
00063
00064 int options_tolerant = 0;
00065
00066 int options_parse_args(struct option*ops, int argc, const char* argv[]) {
00067 int i;
00068 for (i=1; i<argc; i++) {
00069 const char * name = argv[i];
00070 while(*name == '-') name++;
00071
00072 if(!strcmp("config_dump", name)) {
00073 options_dump(ops, stdout, 0);
00074 exit(0);
00075 }
00076
00077 if(!strcmp("help",name) || !strcmp("h",name) ) {
00078 options_print_help(ops, stdout);
00079 exit(0);
00080 }
00081
00082 if(!strcmp("config", name)) {
00083 if(i>=argc-1) {
00084 fprintf(stderr, "Please specify config file.\n");
00085 if(!options_tolerant) return 0;
00086 }
00087 if(!options_parse_file(ops, ".", argv[i+1])) {
00088 if(!options_tolerant) return 0;
00089 }
00090 i++;
00091 continue;
00092 }
00093
00094 struct option * o;
00095 if(0 == (o =options_find(ops, name)) ) {
00096 fprintf(stderr, "Option '%s' not found (use -help to get list of options).\n", name);
00097 if(!options_tolerant) return 0;
00098 }
00099
00100 options_set_passed(o);
00101 if(options_requires_argument(o)) {
00102 if(i>=argc-1) {
00103 fprintf(stderr, "Argument %s needs value.\n", o->name);
00104 if(!options_tolerant) return 0;
00105 }
00106 if(!options_set(o, argv[i+1])) {
00107 if(!options_tolerant) return 0;
00108 }
00109 i++;
00110 }
00111
00112 }
00113
00114 return 1;
00115 }
00116
00117
00118
00120 void options_set_passed(struct option*o) {
00121 if(o->set_pointer)
00122 *(o->set_pointer) = 1;
00123 }
00124
00126 int options_requires_argument(struct option*o) {
00127 return o->value_pointer != 0;
00128 }
00129
00130
00131 int options_parse_stream(struct option*ops, const char*pwd, FILE*file) {
00132 #define MAX_LINE_LENGTH 10000
00133 char linesto[MAX_LINE_LENGTH];
00134 while(fgets(linesto, MAX_LINE_LENGTH-1, file)) {
00135 char *line=linesto; while(*line) { if(*line=='\n') *line=0; line++; }
00136 line = linesto;
00137 while(isspace(*line)) line++;
00138 if(*line == '#') continue;
00139 if(*line == '<') { line++;
00140 while(isspace(*line)) line++;
00141 if(!options_parse_file(ops, pwd, line)) {
00142 if(!options_tolerant) return 0;
00143 }
00144 continue;
00145 }
00146 if(!*line) continue;
00147
00148 const char * name = line;
00149
00150 while(!isspace(*line)) line++;
00151
00152 char empty[5] = "";
00153 char * value;
00154 if(*line == 0) value = empty; else {
00155 *line = 0;
00156 line++;
00157
00158 while(isspace(*line)) line++;
00159
00160 if(*line == '=') line++;
00161
00162 while(isspace(*line)) line++;
00163
00164
00165 value = line;
00166
00167 int len = strlen(value);
00168 while(isspace(value[len-1]) && len > 0) {
00169 value[len-1] = 0; len--;
00170 }
00171 }
00172
00173 if(!options_try_pair(ops, name, value) && !options_tolerant) {
00174 return 0;
00175 }
00176 }
00177 return 1;
00178 }
00179
00180 int options_parse_file(struct option*ops, const char*pwd, const char*filename) {
00181 char concat[PATH_MAX*2+1];
00182
00183 if(filename[0] != '/') {
00184 strcpy(concat, pwd);
00185 strcat(concat, "/");
00186 strcat(concat, filename);
00187 } else {
00188 strcpy(concat, filename);
00189 }
00190
00191 char resolved_path[PATH_MAX];
00192 char *resolved;
00193 if(! (resolved = realpath(concat, resolved_path))) {
00194 fprintf(stderr, "Could not resolve '%s' ('%s').\n", concat, resolved);
00195 return 0;
00196 }
00197
00198 const char * newdir = dirname(resolved);
00199 if(!newdir) {
00200 fprintf(stderr, "Could not get dirname for '%s'.\n", resolved);
00201 free(resolved);
00202 return 0;
00203 }
00204
00205 FILE * file;
00206 file = fopen(resolved,"r");
00207 if(file==NULL) {
00208 fprintf(stderr, "Could not open '%s': %s.\n", resolved, strerror(errno));
00209
00210 return 0;
00211 }
00212
00213
00214 return options_parse_stream(ops, newdir, file);
00215 }
00216
00217
00219 struct option * options_find(struct option*ops, const char * name) {
00220 int j;
00221 for(j=0;options_valid(ops+j);j++)
00222 if(!strcmp(name,ops[j].name))
00223 return ops+j;
00224
00225 return 0;
00226 }
00227
00228 int options_try_pair(struct option*ops, const char*name, const char*value) {
00229 struct option* o;
00230 if(0 == (o = options_find(ops, name))) {
00231
00232 fprintf(stderr, "Option '%s' does not exist.\n", name);
00233 return 0;
00234 }
00235 return options_set(o, value);
00236 }
00237
00238 int options_valid(struct option*o) {
00239 return o->name != 0;
00240 }
00241
00242 int options_set(struct option*o, const char*value) {
00243 switch(o->type) {
00244 case(OPTION_INT): {
00245 int * value_pointer = (int*) o->value_pointer;
00246 int ok = get_int(value_pointer, value);
00247 if(!ok) {
00248 fprintf(stderr, "Could not parse int: '%s' = '%s'.\n", o->name, value);
00249 return 0;
00250 }
00251 return 1;
00252 }
00253
00254 case(OPTION_STRING): {
00255 char** value_pointer = (char**) o->value_pointer;
00256 *value_pointer = (char*) strdup_(value);
00257
00258
00259
00260
00261 return 1;
00262 }
00263
00264 case(OPTION_DOUBLE): {
00265 double * value_pointer = (double*) o->value_pointer;
00266 int ok = get_double(value_pointer, value);
00267 if(!ok) {
00268 fprintf(stderr, "Could not parse double: '%s' = '%s'.\n", o->name, value);
00269 return 0;
00270 }
00271 return 1;
00272 }
00273
00274 case(OPTION_ALTERNATIVE): {
00275 int * value_pointer = (int*) o->value_pointer;
00276 struct option_alternative * a = o->alternative;
00277 for(; a->label; a++) {
00278 if( !strcasecmp(a->label, value) ) {
00279 *value_pointer = a->value;
00280 return 1;
00281 }
00282 }
00283 fprintf(stderr, "Could not recognize '%s' as one of the alternative for %s: ",
00284 value, o->name);
00285
00286 for(a = o->alternative; a->label; a++) {
00287 fprintf(stderr, "\"%s\"", a->label);
00288 if( (a+1)->label ) fprintf(stderr, ", ");
00289 }
00290 fprintf(stderr, ".\n");
00291 return 0;
00292 }
00293
00294
00295
00296 default: {
00297
00298 fprintf(stderr, "Could not parse type %d: '%s' = '%s'.\n", (int) o->type, o->name, value);
00299 return 0;
00300 }
00301 }
00302 }
00303
00304 const char*options_value_as_string(struct option*o);
00305
00306 void display_table(FILE*f, char**table, int rows, int columns, int padding) {
00307 int col_size[columns];
00308
00309 int i,j;
00310 for(j=0;j<columns;j++) {
00311 col_size[j]=0;
00312 for(i=0;i<rows;i++) {
00313 const char * s = table[j+i*columns];
00314 col_size[j] = MAX(col_size[j], (int) strlen(s));
00315 }
00316 col_size[j] += padding;
00317 }
00318
00319 for(i=0;i<rows;i++) {
00320 for(j=0;j<columns;j++) {
00321 const char * s = table[j+i*columns];
00322
00323 if(j != columns - 1)
00324 fprintf(f, "%s%*s", s, (int)(col_size[j]-strlen(s)), "");
00325 else
00326 fputs(s, f);
00327 }
00328 fprintf(f, "\n");
00329 }
00330 }
00331
00332 void options_dump(struct option * options, FILE*f, int write_desc) {
00333 int n; for (n=0;options_valid(options+n);n++);
00334
00335 int nrows = n + 2;
00336 char**table = malloc(sizeof(char*)*nrows*3);
00337
00338 int row = 0;
00339 if(write_desc) {
00340 table[row*3 +0] = strdup_("Option name");
00341 table[row*3 +1] = strdup_("Default");
00342 table[row*3 +2] = strdup_("Description");
00343 row++;
00344 table[row*3 +0] = strdup_("-----------");
00345 table[row*3 +1] = strdup_("-------");
00346 table[row*3 +2] = strdup_("-----------");
00347 row++;
00348 } else {
00349 table[row*3 +0] = strdup_("");
00350 table[row*3 +1] = strdup_("");
00351 table[row*3 +2] = strdup_("");
00352 row++;
00353 table[row*3 +0] = strdup_("");
00354 table[row*3 +1] = strdup_("");
00355 table[row*3 +2] = strdup_("");
00356 row++;
00357 }
00358
00359 int i;
00360 for (i=0;i<n;i++) {
00361 table[row*3 +0] = strdup_(options[i].name);
00362 table[row*3 +1] = strdup_(options_value_as_string(options+i));
00363 table[row*3 +2] = write_desc ? strdup_(options[i].desc) : strdup_("");
00364
00365 if( write_desc)
00366 if(options[i].type == OPTION_ALTERNATIVE) {
00367 char extended[1000];
00368 strcat(extended, options[i].desc);
00369 strcat(extended, " Possible options are: ");
00370
00371 struct option_alternative * a = options[i].alternative;
00372 for(; a->label; a++) {
00373 strcat(extended, "\"");
00374 strcat(extended, a->label);
00375 strcat(extended, "\"");
00376 if(a->desc) {
00377 strcat(extended, ": ");
00378 strcat(extended, a->desc);
00379 } else {
00380 }
00381 if((a+1)->label)
00382 strcat(extended, ", ");
00383 }
00384 strcat(extended, ".");
00385
00386 table[row*3 +2] = strdup_(extended);
00387 }
00388
00389 row ++;
00390 }
00391
00392 display_table(f, table, nrows, 3, 2);
00393
00394 int a; for(a=0;a<nrows*3;a++) free((void*)table[a]);
00395 free((void*)table);
00396 }
00397
00398 void options_print_help(struct option * options, FILE*f) {
00399 fprintf(f, "%s", options_banner_string);
00400 fprintf(f,
00401 "Generic options: \n"
00402 " -help Displays this help.\n"
00403 " -config_dump Dumps the configuration on the standard output. \n"
00404 " -config FILE Loads a config file in the format used by config_dump.\n"
00405 "\n");
00406
00407 options_dump(options, f, 1);
00408 }
00409
00410 static char options_value_as_string_buf[1000];
00411 const char*options_value_as_string(struct option*o) {
00412 if(!o->value_pointer) {
00413 return "NULL";
00414 }
00415
00416 switch(o->type) {
00417 case(OPTION_INT): {
00418 int * value_pointer = (int*) o->value_pointer;
00419 sprintf(options_value_as_string_buf, "%d", *value_pointer);
00420 break;
00421 }
00422
00423 case(OPTION_STRING): {
00424 char** value_pointer = (char**) o->value_pointer;
00425 sprintf(options_value_as_string_buf, "%s", *value_pointer);
00426 break;
00427 }
00428
00429 case(OPTION_DOUBLE): {
00430 double * value_pointer = (double*) o->value_pointer;
00431 sprintf(options_value_as_string_buf, "%g", *value_pointer);
00432 break;
00433 }
00434
00435 case(OPTION_ALTERNATIVE): {
00436 int * value_pointer = (int*) o->value_pointer;
00437 struct option_alternative * a = o->alternative;
00438 for(; a->label; a++) {
00439 if( a->value == *value_pointer )
00440 sprintf(options_value_as_string_buf, "%s", a->label);
00441 }
00442 break;
00443 }
00444
00445 default:
00446 strcpy(options_value_as_string_buf, "?");
00447 }
00448
00449 return options_value_as_string_buf;
00450 }
00451