00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #ifdef HAVE_CONFIG_H
00011 # include "config.h"
00012 #endif
00013 #include "clp.h"
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include <stdio.h>
00017 #include <assert.h>
00018 #include <stdarg.h>
00019 #include <ctype.h>
00020
00021
00022 #if !defined(HAVE_STRTOUL) && !defined(HAVE_CONFIG_H)
00023 # define HAVE_STRTOUL 1
00024 #endif
00025
00026 #ifdef __cplusplus
00027 extern "C" {
00028 #endif
00029
00030
00031 #define Clp_DoubledLong (Clp_LongImplicit * 2)
00032
00033 #define Clp_AnyArgument (Clp_Mandatory | Clp_Optional)
00034
00035 #define MAX_AMBIGUOUS_VALUES 4
00036
00037 typedef struct {
00038
00039 Clp_ArgParseFunc func;
00040 int flags;
00041 void *thunk;
00042
00043 } Clp_ArgType;
00044
00045
00046 typedef struct {
00047
00048 int pos;
00049 int neg;
00050
00051 } Clp_LongMinMatch;
00052
00053
00054 struct Clp_Internal {
00055
00056 Clp_Option *opt;
00057 Clp_LongMinMatch *long_min_match;
00058 int nopt;
00059
00060 Clp_ArgType *argtype;
00061 int nargtype;
00062
00063 char * const *argv;
00064 int argc;
00065
00066 unsigned char option_class[256];
00067 int both_short_and_long;
00068
00069 char option_chars[3];
00070 char *text;
00071
00072 char *program_name;
00073 void (*error_handler)(char *);
00074
00075 int is_short;
00076 int whole_negated;
00077 int could_be_short;
00078
00079 int option_processing;
00080
00081 int ambiguous;
00082 int ambiguous_values[MAX_AMBIGUOUS_VALUES];
00083
00084 Clp_Option *current_option;
00085 int current_short;
00086 int negated_by_no;
00087
00088 };
00089
00090
00091 struct Clp_ParserState {
00092
00093 char * const *argv;
00094 int argc;
00095 char option_chars[3];
00096 char *text;
00097 int is_short;
00098 int whole_negated;
00099
00100 };
00101
00102
00103 typedef struct Clp_StringList {
00104
00105 Clp_Option *items;
00106 Clp_LongMinMatch *long_min_match;
00107 int nitems;
00108
00109 int allow_int;
00110 int nitems_invalid_report;
00111
00112 } Clp_StringList;
00113
00114
00115 #define TEST(o, f) (((o)->flags & (f)) != 0)
00116
00117
00118 static int calculate_long_min_match(int, Clp_Option *, int, int, int);
00119
00120 static int parse_string(Clp_Parser *, const char *, int, void *);
00121 static int parse_int(Clp_Parser *, const char *, int, void *);
00122 static int parse_bool(Clp_Parser *, const char *, int, void *);
00123 static int parse_double(Clp_Parser *, const char *, int, void *);
00124 static int parse_string_list(Clp_Parser *, const char *, int, void *);
00125
00126 static int ambiguity_error(Clp_Parser *, int, int *, Clp_Option *,
00127 const char *, const char *, ...);
00128
00129
00130
00131
00132
00133
00134 static void
00135 check_duplicated_short_options(int nopt, Clp_Option *opt, int negated)
00136 {
00137 int i;
00138 int check[256];
00139
00140 for (i = 0; i < 256; i++)
00141 check[i] = -1;
00142
00143 for (i = 0; i < nopt; i++)
00144 if (opt[i].short_name > 0 && opt[i].short_name < 256
00145 && (negated ? TEST(&opt[i], Clp_Negate)
00146 : !TEST(&opt[i], Clp_OnlyNegated))) {
00147 int sh = opt[i].short_name;
00148 if (check[sh] >= 0 && check[sh] != opt[i].option_id)
00149 fprintf(stderr, "CLP error: more than 1 option has short name `%c'\n",
00150 sh);
00151 check[sh] = opt[i].option_id;
00152 }
00153 }
00154
00155 Clp_Parser *
00156 Clp_NewParser(int argc, char * const argv[], int nopt, Clp_Option *opt)
00157
00158
00159 {
00160 int i;
00161 Clp_Parser *clp = (Clp_Parser *)malloc(sizeof(Clp_Parser));
00162 Clp_Internal *cli = (Clp_Internal *)malloc(sizeof(Clp_Internal));
00163 Clp_LongMinMatch *lmm = (Clp_LongMinMatch *)malloc(sizeof(Clp_LongMinMatch) * nopt);
00164 if (!clp || !cli || !lmm) goto failed;
00165
00166 clp->internal = cli;
00167 cli->long_min_match = lmm;
00168
00169
00170 for (i = 0; i < nopt; i++)
00171 if (opt[i].option_id < 0) {
00172 fprintf(stderr, "CLP error: option %d has negative option_id\n", i);
00173 opt[i] = opt[nopt - 1];
00174 nopt--;
00175 i--;
00176 }
00177
00178
00179 for (i = 0; i < nopt; i++) {
00180
00181 if (opt[i].arg_type <= 0)
00182 opt[i].flags &= ~Clp_AnyArgument;
00183 if (opt[i].arg_type > 0 && !TEST(&opt[i], Clp_Optional))
00184 opt[i].flags |= Clp_Mandatory;
00185
00186
00187
00188 if (opt[i].short_name <= 0 || opt[i].short_name > 255)
00189 opt[i].short_name = 256;
00190
00191
00192 if (opt[i].long_name && strncmp(opt[i].long_name, "no-", 3) == 0) {
00193 opt[i].long_name += 3;
00194 opt[i].flags |= Clp_Negate | Clp_OnlyNegated;
00195 }
00196 }
00197
00198
00199 check_duplicated_short_options(nopt, opt, 0);
00200 check_duplicated_short_options(nopt, opt, 1);
00201
00202
00203 for (i = 0; i < nopt; i++)
00204 if (opt[i].long_name && !TEST(&opt[i], Clp_OnlyNegated))
00205 lmm[i].pos = calculate_long_min_match
00206 (nopt, opt, i, Clp_OnlyNegated, 0);
00207 for (i = 0; i < nopt; i++)
00208 if (opt[i].long_name && TEST(&opt[i], Clp_Negate))
00209 lmm[i].neg = calculate_long_min_match
00210 (nopt, opt, i, Clp_Negate, Clp_Negate);
00211
00212
00213 cli->opt = opt;
00214 cli->nopt = nopt;
00215
00216 cli->argtype = (Clp_ArgType *)malloc(sizeof(Clp_ArgType) * 5);
00217 if (!cli->argtype) goto failed;
00218 cli->nargtype = 5;
00219
00220 cli->argc = argc;
00221 cli->argv = argv;
00222 {
00223 char *slash = strrchr(argv[0], '/');
00224 cli->program_name = slash ? slash + 1 : argv[0];
00225 }
00226 cli->error_handler = 0;
00227
00228 for (i = 0; i < 256; i++)
00229 cli->option_class[i] = 0;
00230 cli->option_class['-'] = Clp_Short;
00231 cli->both_short_and_long = 0;
00232
00233 cli->is_short = 0;
00234 cli->whole_negated = 0;
00235
00236 cli->option_processing = 1;
00237 cli->current_option = 0;
00238
00239
00240 Clp_AddType(clp, Clp_ArgString, 0, parse_string, 0);
00241 Clp_AddType(clp, Clp_ArgStringNotOption, Clp_DisallowOptions, parse_string, 0);
00242 Clp_AddType(clp, Clp_ArgInt, 0, parse_int, 0);
00243 Clp_AddType(clp, Clp_ArgUnsigned, 0, parse_int, (void *)cli);
00244 Clp_AddType(clp, Clp_ArgBool, 0, parse_bool, 0);
00245 Clp_AddType(clp, Clp_ArgDouble, 0, parse_double, 0);
00246 return clp;
00247
00248 failed:
00249 if (cli && cli->argtype) free(cli->argtype);
00250 if (cli) free(cli);
00251 if (clp) free(clp);
00252 if (lmm) free(lmm);
00253 return 0;
00254 }
00255
00256 void
00257 Clp_DeleteParser(Clp_Parser *clp)
00258
00259
00260 {
00261 int i;
00262 Clp_Internal *cli;
00263 if (!clp) return;
00264
00265 cli = clp->internal;
00266
00267
00268 for (i = 0; i < cli->nargtype; i++)
00269 if (cli->argtype[i].func == parse_string_list) {
00270 Clp_StringList *clsl = (Clp_StringList *)cli->argtype[i].thunk;
00271 free(clsl->items);
00272 free(clsl->long_min_match);
00273 free(clsl);
00274 }
00275
00276 free(cli->argtype);
00277 free(cli->long_min_match);
00278 free(cli);
00279 free(clp);
00280 }
00281
00282
00283 int
00284 Clp_SetOptionProcessing(Clp_Parser *clp, int option_processing)
00285
00286
00287
00288 {
00289 Clp_Internal *cli = clp->internal;
00290 int old = cli->option_processing;
00291 cli->option_processing = option_processing;
00292 return old;
00293 }
00294
00295
00296 Clp_ErrorHandler
00297 Clp_SetErrorHandler(Clp_Parser *clp, void (*error_handler)(char *))
00298
00299
00300 {
00301 Clp_Internal *cli = clp->internal;
00302 Clp_ErrorHandler old = cli->error_handler;
00303 cli->error_handler = error_handler;
00304 return old;
00305 }
00306
00307
00308 int
00309 Clp_SetOptionChar(Clp_Parser *clp, int c, int option_type)
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325 {
00326 int i;
00327 Clp_Internal *cli = clp->internal;
00328
00329 if (option_type < 0 || option_type >= 2*Clp_LongImplicit
00330 || ((option_type & Clp_Short) && (option_type & Clp_ShortNegated))
00331 || ((option_type & Clp_Long) && (option_type & Clp_LongNegated))
00332 || ((option_type & Clp_LongImplicit) && (option_type & (Clp_Short | Clp_ShortNegated | Clp_Long | Clp_LongNegated))))
00333 return 0;
00334
00335 if (c == 0)
00336 for (i = 1; i < 256; i++)
00337 cli->option_class[i] = option_type;
00338 else
00339 cli->option_class[c] = option_type;
00340
00341
00342
00343
00344
00345
00346
00347 if (!cli->both_short_and_long) {
00348 int either_short = option_type & (Clp_Short | Clp_ShortNegated);
00349 int either_long = option_type & (Clp_Long | Clp_LongNegated);
00350 if (either_short && either_long) {
00351 unsigned char have_short[257];
00352 for (i = 0; i < 256; i++)
00353 have_short[i] = 0;
00354 for (i = 0; i < cli->nopt; i++)
00355 have_short[cli->opt[i].short_name] = 1;
00356 for (i = 0; i < cli->nopt; i++)
00357 if (cli->opt[i].long_name && cli->long_min_match[i].pos == 1) {
00358
00359 unsigned char first = (unsigned char)cli->opt[i].long_name[0];
00360 if (have_short[first] && first != cli->opt[i].short_name)
00361 cli->long_min_match[i].pos++;
00362 }
00363 cli->both_short_and_long = 1;
00364 }
00365 }
00366
00367 return 1;
00368 }
00369
00370
00371
00372
00373
00374
00375 static int
00376 min_different_chars(char *s, char *t)
00377
00378
00379
00380 {
00381 char *sfirst = s;
00382 while (*s && *t && *s == *t)
00383 s++, t++;
00384 if (!*s)
00385 return s - sfirst;
00386 else
00387 return s - sfirst + 1;
00388 }
00389
00390 static int
00391 calculate_long_min_match(int nopt, Clp_Option *opt, int which,
00392 int flags, int flags_value)
00393 {
00394 int j, lmm = 1;
00395
00396 for (j = 0; j < nopt; j++)
00397 if (opt[j].long_name
00398 && (opt[j].flags & flags) == flags_value
00399 && opt[which].option_id != opt[j].option_id
00400 && strncmp(opt[which].long_name, opt[j].long_name, lmm) == 0)
00401 lmm = min_different_chars(opt[which].long_name, opt[j].long_name);
00402
00403 return lmm;
00404 }
00405
00406
00407
00408 static int
00409 argcmp(const char *ref, const char *arg, int min_match)
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422 {
00423 const char *refstart = ref;
00424 while (*ref && *arg && *arg != '=' && *ref == *arg)
00425 ref++, arg++;
00426 if (*arg && *arg != '=')
00427 return 0;
00428 else if (ref - refstart < min_match)
00429 return -1;
00430 else
00431 return ref - refstart;
00432 }
00433
00434 static int
00435 find_prefix_opt(const char *arg, int nopt, Clp_Option *opt,
00436 Clp_LongMinMatch *lmmvec,
00437 int *ambiguous, int *ambiguous_values, int negated)
00438
00439
00440
00441
00442 {
00443 int i;
00444 for (i = 0; i < nopt; i++) {
00445 int len, lmm;
00446 if (!opt[i].long_name
00447 || (negated && !TEST(&opt[i], Clp_Negate))
00448 || (!negated && TEST(&opt[i], Clp_OnlyNegated)))
00449 continue;
00450
00451 lmm = (negated ? lmmvec[i].neg : lmmvec[i].pos);
00452 len = argcmp(opt[i].long_name, arg, lmm);
00453 if (len > 0)
00454 return i;
00455 else if (len < 0) {
00456 if (*ambiguous < MAX_AMBIGUOUS_VALUES)
00457 ambiguous_values[*ambiguous] = i;
00458 (*ambiguous)++;
00459 }
00460 }
00461 return -1;
00462 }
00463
00464
00465
00466
00467
00468
00469 int
00470 Clp_AddType(Clp_Parser *clp, int type_id, int flags,
00471 Clp_ArgParseFunc func, void *thunk)
00472
00473
00474
00475
00476
00477
00478
00479 {
00480 int i;
00481 Clp_Internal *cli = clp->internal;
00482 Clp_ArgType *new_argtype;
00483 int nargtype = cli->nargtype;
00484 assert(nargtype);
00485
00486 if (type_id <= 0 || !func) return 0;
00487
00488 while (nargtype <= type_id)
00489 nargtype *= 2;
00490 new_argtype = (Clp_ArgType *)
00491 realloc(cli->argtype, sizeof(Clp_ArgType) * nargtype);
00492 if (!new_argtype) return 0;
00493 cli->argtype = new_argtype;
00494
00495 for (i = cli->nargtype; i < nargtype; i++)
00496 cli->argtype[i].func = 0;
00497 cli->nargtype = nargtype;
00498
00499 cli->argtype[type_id].func = func;
00500 cli->argtype[type_id].flags = flags;
00501 cli->argtype[type_id].thunk = thunk;
00502 return 1;
00503 }
00504
00505
00506
00507
00508
00509
00510 static int
00511 parse_string(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00512 {
00513 clp->val.s = (char *)arg;
00514 return 1;
00515 }
00516
00517 static int
00518 parse_int(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00519 {
00520 char *val;
00521 int base = 10;
00522 if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) {
00523 base = 16;
00524 arg += 2;
00525 }
00526
00527 if (thunk != 0) {
00528 #if HAVE_STRTOUL
00529 clp->val.u = strtoul(arg, &val, base);
00530 #else
00531
00532 clp->val.u = strtol(arg, &val, base);
00533 #endif
00534 } else
00535 clp->val.i = strtol(arg, &val, base);
00536 if (*arg != 0 && *val == 0)
00537 return 1;
00538 else if (complain) {
00539 const char *message = thunk != 0
00540 ? "`%O' expects a nonnegative integer, not `%s'"
00541 : "`%O' expects an integer, not `%s'";
00542 if (base == 16) arg -= 2;
00543 return Clp_OptionError(clp, message, arg);
00544 } else
00545 return 0;
00546 }
00547
00548 static int
00549 parse_double(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00550 {
00551 char *val;
00552 clp->val.d = strtod(arg, &val);
00553 if (*arg != 0 && *val == 0)
00554 return 1;
00555 else if (complain)
00556 return Clp_OptionError(clp, "`%O' expects a real number, not `%s'", arg);
00557 else
00558 return 0;
00559 }
00560
00561 static int
00562 parse_bool(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00563 {
00564 int i;
00565 char lcarg[6];
00566 if (strlen(arg) > 5 || strchr(arg, '=') != 0)
00567 goto error;
00568
00569 for (i = 0; arg[i] != 0; i++)
00570 lcarg[i] = tolower(arg[i]);
00571 lcarg[i] = 0;
00572
00573 if (argcmp("yes", lcarg, 1) > 0
00574 || argcmp("true", lcarg, 1) > 0
00575 || argcmp("1", lcarg, 1) > 0) {
00576 clp->val.i = 1;
00577 return 1;
00578 } else if (argcmp("no", lcarg, 1) > 0
00579 || argcmp("false", lcarg, 1) > 0
00580 || argcmp("1", lcarg, 1) > 0) {
00581 clp->val.i = 0;
00582 return 1;
00583 }
00584
00585 error:
00586 if (complain)
00587 Clp_OptionError(clp, "`%O' expects a true-or-false value, not `%s'", arg);
00588 return 0;
00589 }
00590
00591
00592
00593
00594
00595
00596 static int
00597 parse_string_list(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00598 {
00599 Clp_StringList *sl = (Clp_StringList *)thunk;
00600 int index, ambiguous = 0;
00601 int ambiguous_values[MAX_AMBIGUOUS_VALUES + 1];
00602
00603
00604 index = find_prefix_opt
00605 (arg, sl->nitems, sl->items, sl->long_min_match,
00606 &ambiguous, ambiguous_values, 0);
00607 if (index >= 0) {
00608 clp->val.i = sl->items[index].option_id;
00609 return 1;
00610 }
00611
00612 if (sl->allow_int) {
00613 if (parse_int(clp, arg, 0, 0))
00614 return 1;
00615 }
00616
00617 if (complain) {
00618 char *complaint = (ambiguous ? "an ambiguous" : "not a valid");
00619 if (!ambiguous) {
00620 ambiguous = sl->nitems_invalid_report;
00621 for (index = 0; index < ambiguous; index++)
00622 ambiguous_values[index] = index;
00623 }
00624 return ambiguity_error
00625 (clp, ambiguous, ambiguous_values, sl->items, "",
00626 "`%s' is %s argument to `%O'", arg, complaint);
00627 } else
00628 return 0;
00629 }
00630
00631
00632 int
00633 finish_string_list(Clp_Parser *clp, int type_id, int flags,
00634 Clp_Option *items, int nitems, int itemscap)
00635 {
00636 int i;
00637 Clp_StringList *clsl = (Clp_StringList *)malloc(sizeof(Clp_StringList));
00638 Clp_LongMinMatch *lmm = (Clp_LongMinMatch *)malloc(sizeof(Clp_LongMinMatch) * nitems);
00639 if (!clsl || !lmm) goto error;
00640
00641 clsl->items = items;
00642 clsl->long_min_match = lmm;
00643 clsl->nitems = nitems;
00644 clsl->allow_int = (flags & Clp_AllowNumbers) != 0;
00645
00646 if (nitems < MAX_AMBIGUOUS_VALUES && nitems < itemscap && clsl->allow_int) {
00647 items[nitems].long_name = "any integer";
00648 clsl->nitems_invalid_report = nitems + 1;
00649 } else if (nitems > MAX_AMBIGUOUS_VALUES + 1)
00650 clsl->nitems_invalid_report = MAX_AMBIGUOUS_VALUES + 1;
00651 else
00652 clsl->nitems_invalid_report = nitems;
00653
00654 for (i = 0; i < nitems; i++)
00655 lmm[i].pos = calculate_long_min_match(nitems, items, i, 0, 0);
00656
00657 if (Clp_AddType(clp, type_id, 0, parse_string_list, clsl))
00658 return 1;
00659
00660 error:
00661 if (clsl) free(clsl);
00662 if (lmm) free(lmm);
00663 return 0;
00664 }
00665
00666 int
00667 Clp_AddStringListType(Clp_Parser *clp, int type_id, int flags, ...)
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682 {
00683 int nitems = 0;
00684 int itemscap = 5;
00685 Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
00686
00687 va_list val;
00688 va_start(val, flags);
00689
00690 if (!items) goto error;
00691
00692
00693 while (1) {
00694 int value;
00695 char *name = va_arg(val, char *);
00696 if (!name) break;
00697 value = va_arg(val, int);
00698
00699 if (nitems >= itemscap) {
00700 Clp_Option *new_items;
00701 itemscap *= 2;
00702 new_items = (Clp_Option *)realloc(items, sizeof(Clp_Option) * itemscap);
00703 if (!new_items) goto error;
00704 items = new_items;
00705 }
00706
00707 items[nitems].long_name = name;
00708 items[nitems].option_id = value;
00709 items[nitems].flags = 0;
00710 nitems++;
00711 }
00712
00713 va_end(val);
00714 if (finish_string_list(clp, type_id, flags, items, nitems, itemscap))
00715 return 1;
00716
00717 error:
00718 va_end(val);
00719 if (items) free(items);
00720 return 0;
00721 }
00722
00723
00724 int
00725 Clp_AddStringListTypeVec(Clp_Parser *clp, int type_id, int flags,
00726 int nitems, char **strings, int *values)
00727
00728
00729
00730 {
00731 int i;
00732 int itemscap = (nitems < 5 ? 5 : nitems);
00733 Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
00734 if (!items) return 0;
00735
00736
00737 for (i = 0; i < nitems; i++) {
00738 items[i].long_name = strings[i];
00739 items[i].option_id = values[i];
00740 items[i].flags = 0;
00741 }
00742
00743 if (finish_string_list(clp, type_id, flags, items, nitems, itemscap))
00744 return 1;
00745 else {
00746 free(items);
00747 return 0;
00748 }
00749 }
00750
00751
00752
00753
00754
00755
00756 char *
00757 Clp_ProgramName(Clp_Parser *clp)
00758 {
00759 return clp->internal->program_name;
00760 }
00761
00762
00763
00764
00765
00766
00767 Clp_ParserState *
00768 Clp_NewParserState(void)
00769 {
00770 return (Clp_ParserState *)malloc(sizeof(Clp_ParserState));
00771 }
00772
00773 void
00774 Clp_DeleteParserState(Clp_ParserState *save)
00775 {
00776 free(save);
00777 }
00778
00779
00780 void
00781 Clp_SaveParser(Clp_Parser *clp, Clp_ParserState *save)
00782
00783 {
00784 Clp_Internal *cli = clp->internal;
00785 save->argv = cli->argv;
00786 save->argc = cli->argc;
00787 memcpy(save->option_chars, cli->option_chars, 3);
00788 save->text = cli->text;
00789 save->is_short = cli->is_short;
00790 save->whole_negated = cli->whole_negated;
00791 }
00792
00793
00794 void
00795 Clp_RestoreParser(Clp_Parser *clp, Clp_ParserState *save)
00796
00797 {
00798 Clp_Internal *cli = clp->internal;
00799 cli->argv = save->argv;
00800 cli->argc = save->argc;
00801 memcpy(cli->option_chars, save->option_chars, 3);
00802 cli->text = save->text;
00803 cli->is_short = save->is_short;
00804 cli->whole_negated = save->whole_negated;
00805 }
00806
00807
00808
00809
00810
00811
00812 static void
00813 set_option_text(Clp_Internal *cli, char *text, int n_option_chars)
00814 {
00815 char *option_chars = cli->option_chars;
00816 assert(n_option_chars < 3);
00817
00818 while (n_option_chars-- > 0)
00819 *option_chars++ = *text++;
00820 *option_chars = 0;
00821
00822 cli->text = text;
00823 }
00824
00825
00826 static int
00827 next_argument(Clp_Parser *clp, int want_argument)
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845 {
00846 Clp_Internal *cli = clp->internal;
00847 char *text;
00848 int option_class;
00849
00850
00851 clp->have_arg = 0;
00852 clp->arg = 0;
00853 cli->could_be_short = 0;
00854
00855
00856 if (cli->is_short) {
00857 ++cli->text;
00858 if (cli->text[0] == 0)
00859 cli->is_short = 0;
00860 else if (want_argument > 0) {
00861
00862 clp->have_arg = 1;
00863 if (cli->text[0] == '=')
00864 clp->arg = cli->text + 1;
00865 else
00866 clp->arg = cli->text;
00867 cli->is_short = 0;
00868 return 0;
00869 }
00870 }
00871
00872
00873 if (cli->is_short)
00874 return 1;
00875
00876
00877 cli->whole_negated = 0;
00878 cli->text = 0;
00879
00880 if (cli->argc <= 1)
00881 return 0;
00882
00883 cli->argc--;
00884 cli->argv++;
00885 text = cli->argv[0];
00886
00887 if (want_argument > 1)
00888 goto not_option;
00889
00890 option_class = cli->option_class[ (unsigned char)text[0] ];
00891 if (text[0] == '-' && text[1] == '-')
00892 option_class = Clp_DoubledLong;
00893
00894
00895
00896
00897 if ((option_class & (Clp_Short | Clp_ShortNegated))
00898 && (option_class & (Clp_Long | Clp_LongNegated))) {
00899 option_class &= ~(Clp_Short | Clp_ShortNegated);
00900 if (text[1] != 0) cli->could_be_short = 1;
00901 }
00902
00903 switch (option_class) {
00904
00905 case Clp_Short:
00906 cli->is_short = 1;
00907 goto check_singleton;
00908
00909 case Clp_ShortNegated:
00910 cli->is_short = 1;
00911 cli->whole_negated = 1;
00912 goto check_singleton;
00913
00914 case Clp_Long:
00915 goto check_singleton;
00916
00917 case Clp_LongNegated:
00918 cli->whole_negated = 1;
00919 goto check_singleton;
00920
00921 check_singleton:
00922
00923
00924 if (text[1] == 0)
00925 goto not_option;
00926 set_option_text(cli, text, 1);
00927 break;
00928
00929 case Clp_LongImplicit:
00930
00931
00932 if (want_argument > 0)
00933 goto not_option;
00934 set_option_text(cli, text, 0);
00935 break;
00936
00937 case Clp_DoubledLong:
00938 set_option_text(cli, text, 2);
00939 break;
00940
00941 not_option:
00942 case Clp_NotOption:
00943 cli->is_short = 0;
00944 clp->have_arg = 1;
00945 clp->arg = text;
00946 return 0;
00947
00948 default:
00949 assert(0 && "misconfiguration");
00950
00951 }
00952
00953 return 1;
00954 }
00955
00956
00957 static void
00958 switch_to_short_argument(Clp_Parser *clp)
00959 {
00960 Clp_Internal *cli = clp->internal;
00961 const char *text = cli->argv[0];
00962 int option_class = cli->option_class[ (unsigned char)text[0] ];
00963 cli->is_short = 1;
00964 cli->whole_negated = (option_class & Clp_ShortNegated ? 1 : 0);
00965 set_option_text(cli, cli->argv[0], 1);
00966 assert(cli->could_be_short);
00967 }
00968
00969
00970 static Clp_Option *
00971 find_long(Clp_Parser *clp, char *arg)
00972
00973
00974
00975
00976 {
00977 Clp_Internal *cli = clp->internal;
00978 int value, len;
00979 Clp_Option *opt = cli->opt;
00980 int first_negative_ambiguous;
00981
00982
00983 value = find_prefix_opt
00984 (arg, cli->nopt, opt, cli->long_min_match,
00985 &cli->ambiguous, cli->ambiguous_values, clp->negated);
00986 if (value >= 0)
00987 goto worked;
00988
00989
00990
00991
00992 first_negative_ambiguous = cli->ambiguous;
00993 while (arg[0] == 'n' && arg[1] == 'o' && arg[2] == '-') {
00994 arg += 3;
00995 clp->negated = !clp->negated;
00996 value = find_prefix_opt
00997 (arg, cli->nopt, opt, cli->long_min_match,
00998 &cli->ambiguous, cli->ambiguous_values, clp->negated);
00999 if (value >= 0)
01000 goto worked;
01001 }
01002
01003
01004
01005 {
01006 int i, max = cli->ambiguous;
01007 if (max > MAX_AMBIGUOUS_VALUES) max = MAX_AMBIGUOUS_VALUES;
01008 for (i = first_negative_ambiguous; i < max; i++)
01009 cli->ambiguous_values[i] = -cli->ambiguous_values[i] - 1;
01010 return 0;
01011 }
01012
01013 worked:
01014 len = argcmp(opt[value].long_name, arg, cli->long_min_match[value].pos);
01015 if (arg[len] == '=') {
01016 clp->have_arg = 1;
01017 clp->arg = arg + len + 1;
01018 }
01019 return &opt[value];
01020 }
01021
01022
01023 static Clp_Option *
01024 find_short(Clp_Parser *clp, int short_name)
01025
01026 {
01027 Clp_Internal *cli = clp->internal;
01028 Clp_Option *opt = cli->opt;
01029 int i;
01030
01031 for (i = 0; i < cli->nopt; i++)
01032 if (opt[i].short_name == short_name
01033 && (clp->negated ? TEST(&opt[i], Clp_Negate)
01034 : !TEST(&opt[i], Clp_OnlyNegated)))
01035 return &opt[i];
01036
01037 return 0;
01038 }
01039
01040
01041 int
01042 Clp_Next(Clp_Parser *clp)
01043
01044
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062 {
01063 Clp_Internal *cli = clp->internal;
01064 Clp_Option *opt;
01065 Clp_ParserState clpsave;
01066 int complain;
01067
01068
01069 cli->current_option = 0;
01070 cli->ambiguous = 0;
01071
01072
01073 if (!next_argument(clp, cli->option_processing ? 0 : 2))
01074 return clp->have_arg ? Clp_NotOption : Clp_Done;
01075
01076 clp->negated = cli->whole_negated;
01077 if (cli->is_short)
01078 opt = find_short(clp, cli->text[0]);
01079 else
01080 opt = find_long(clp, cli->text);
01081
01082
01083
01084 if (!opt && cli->could_be_short) {
01085 switch_to_short_argument(clp);
01086 opt = find_short(clp, cli->text[0]);
01087 }
01088
01089
01090 if (!opt || (clp->negated && !TEST(opt, Clp_Negate))) {
01091
01092
01093
01094 if (strcmp(cli->argv[0], "--") == 0) {
01095 Clp_SetOptionProcessing(clp, 0);
01096 return Clp_Next(clp);
01097 }
01098
01099
01100 if (cli->ambiguous)
01101 ambiguity_error(clp, cli->ambiguous, cli->ambiguous_values,
01102 cli->opt, cli->option_chars,
01103 "option `%s%s' is ambiguous",
01104 cli->option_chars, cli->text);
01105 else if (cli->is_short && !cli->could_be_short)
01106 Clp_OptionError(clp, "unrecognized option `%s%c'",
01107 cli->option_chars, cli->text[0]);
01108 else
01109 Clp_OptionError(clp, "unrecognized option `%s%s'",
01110 cli->option_chars, cli->text);
01111 return Clp_BadOption;
01112 }
01113
01114
01115 cli->current_option = opt;
01116 cli->current_short = cli->is_short;
01117 cli->negated_by_no = clp->negated && !cli->whole_negated;
01118
01119
01120 if (clp->negated || !TEST(opt, Clp_AnyArgument)) {
01121 if (clp->have_arg) {
01122 Clp_OptionError(clp, "`%O' can't take an argument");
01123 return Clp_BadOption;
01124 } else
01125 return opt->option_id;
01126 }
01127
01128
01129
01130 if (opt->arg_type <= 0 || opt->arg_type >= cli->nargtype
01131 || cli->argtype[ opt->arg_type ].func == 0)
01132 return Clp_Error;
01133
01134
01135
01136 complain = (clp->have_arg != 0) || TEST(opt, Clp_Mandatory);
01137 Clp_SaveParser(clp, &clpsave);
01138
01139 if (TEST(opt, Clp_Mandatory) && !clp->have_arg) {
01140
01141
01142
01143 int disallow = TEST(&cli->argtype[opt->arg_type], Clp_DisallowOptions);
01144 next_argument(clp, disallow ? 1 : 2);
01145 if (!clp->have_arg) {
01146 int got_option = cli->text != 0;
01147 Clp_RestoreParser(clp, &clpsave);
01148 if (got_option)
01149 Clp_OptionError(clp, "`%O' requires a non-option argument");
01150 else
01151 Clp_OptionError(clp, "`%O' requires an argument");
01152 return Clp_BadOption;
01153 }
01154
01155 } else if (cli->is_short && !clp->have_arg && cli->text[1] != 0)
01156
01157
01158 next_argument(clp, 1);
01159
01160
01161 if (clp->have_arg) {
01162 Clp_ArgType *atr = &cli->argtype[ opt->arg_type ];
01163 if (atr->func(clp, clp->arg, complain, atr->thunk) <= 0) {
01164
01165 clp->have_arg = 0;
01166 if (TEST(opt, Clp_Mandatory))
01167 return Clp_BadOption;
01168 else
01169 Clp_RestoreParser(clp, &clpsave);
01170 }
01171 }
01172
01173 return opt->option_id;
01174 }
01175
01176
01177 char *
01178 Clp_Shift(Clp_Parser *clp, int allow_dashes)
01179
01180
01181 {
01182 Clp_ParserState clpsave;
01183 Clp_SaveParser(clp, &clpsave);
01184 next_argument(clp, allow_dashes ? 2 : 1);
01185 if (!clp->have_arg)
01186 Clp_RestoreParser(clp, &clpsave);
01187 return clp->arg;
01188 }
01189
01190
01191
01192
01193
01194
01195 typedef struct Clp_BuildString {
01196 char *text;
01197 char *pos;
01198 int capacity;
01199 int bad;
01200 } Clp_BuildString;
01201
01202 static Clp_BuildString *
01203 new_build_string(void)
01204 {
01205 Clp_BuildString *bs = (Clp_BuildString *)malloc(sizeof(Clp_BuildString));
01206 if (!bs) goto bad;
01207 bs->text = (char *)malloc(256);
01208 if (!bs->text) goto bad;
01209 bs->pos = bs->text;
01210 bs->capacity = 256;
01211 bs->bad = 0;
01212 return bs;
01213
01214 bad:
01215 if (bs) free(bs);
01216 return 0;
01217 }
01218
01219 static void
01220 free_build_string(Clp_BuildString *bs)
01221 {
01222 if (bs) free(bs->text);
01223 free(bs);
01224 }
01225
01226 static int
01227 grow_build_string(Clp_BuildString *bs, int want)
01228 {
01229 char *new_text;
01230 int ipos = bs->pos - bs->text;
01231 int new_capacity = bs->capacity;
01232 while (want >= new_capacity)
01233 new_capacity *= 2;
01234 new_text = (char *)realloc(bs->text, new_capacity);
01235 if (!new_text) {
01236 bs->bad = 1;
01237 return 0;
01238 } else {
01239 bs->text = new_text;
01240 bs->pos = bs->text + ipos;
01241 bs->capacity = new_capacity;
01242 return 1;
01243 }
01244 }
01245
01246 #define ENSURE_BUILD_STRING(bs, space) \
01247 ((((bs)->pos - (bs)->text) + (space) >= (bs)->capacity) \
01248 || grow_build_string((bs), ((bs)->pos - (bs)->text) + (space)))
01249
01250 static void
01251 append_build_string(Clp_BuildString *bs, const char *s, int l)
01252 {
01253 if (l < 0) l = strlen(s);
01254 if (ENSURE_BUILD_STRING(bs, l)) {
01255 memcpy(bs->pos, s, l);
01256 bs->pos += l;
01257 }
01258 }
01259
01260
01261 static Clp_BuildString *
01262 Clp_VaOptionError(Clp_Parser *clp, Clp_BuildString *bs,
01263 const char *fmt, va_list val)
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273 {
01274 Clp_Internal *cli = clp->internal;
01275 const char *percent;
01276
01277 if (!bs) bs = new_build_string();
01278 if (!bs) return 0;
01279 append_build_string(bs, cli->program_name, -1);
01280 append_build_string(bs, ": ", 2);
01281
01282 for (percent = strchr(fmt, '%'); percent; percent = strchr(fmt, '%')) {
01283 append_build_string(bs, fmt, percent - fmt);
01284 switch (*++percent) {
01285
01286 case 's': {
01287 char *s = va_arg(val, char *);
01288 if (s) append_build_string(bs, s, -1);
01289 else append_build_string(bs, "(null)", 6);
01290 break;
01291 }
01292
01293 case 'c': {
01294 int c = va_arg(val, int);
01295 if (ENSURE_BUILD_STRING(bs, 4)) {
01296 if (c >= 32 && c <= 126)
01297 *bs->pos++ = c;
01298 else if (c < 32) {
01299 *bs->pos++ = '^';
01300 *bs->pos++ = c + 64;
01301 } else {
01302 sprintf(bs->pos, "\\%03o", c);
01303 bs->pos += 4;
01304 }
01305 }
01306 break;
01307 }
01308
01309 case 'd': {
01310 int d = va_arg(val, int);
01311 if (ENSURE_BUILD_STRING(bs, 32)) {
01312 sprintf(bs->pos, "%d", d);
01313 bs->pos = strchr(bs->pos, 0);
01314 }
01315 break;
01316 }
01317
01318 case 'O': {
01319 Clp_Option *opt = cli->current_option;
01320 if (!opt)
01321 append_build_string(bs, "(no current option!)", -1);
01322 else if (cli->current_short) {
01323 append_build_string(bs, cli->option_chars, -1);
01324 if (ENSURE_BUILD_STRING(bs, 1))
01325 *bs->pos++ = opt->short_name;
01326 } else if (cli->negated_by_no) {
01327 append_build_string(bs, cli->option_chars, -1);
01328 append_build_string(bs, "no-", 3);
01329 append_build_string(bs, opt->long_name, -1);
01330 } else {
01331 append_build_string(bs, cli->option_chars, -1);
01332 append_build_string(bs, opt->long_name, -1);
01333 }
01334 break;
01335 }
01336
01337 case '%':
01338 if (ENSURE_BUILD_STRING(bs, 1))
01339 *bs->pos++ = '%';
01340 break;
01341
01342 default:
01343 if (ENSURE_BUILD_STRING(bs, 2)) {
01344 *bs->pos++ = '%';
01345 *bs->pos++ = *percent;
01346 }
01347 break;
01348
01349 }
01350 fmt = ++percent;
01351 }
01352
01353 append_build_string(bs, fmt, -1);
01354 append_build_string(bs, "\n", 1);
01355
01356 return bs;
01357 }
01358
01359 static void
01360 do_error(Clp_Parser *clp, Clp_BuildString *bs)
01361 {
01362 char *text;
01363 if (bs && !bs->bad) {
01364 *bs->pos = 0;
01365 text = bs->text;
01366 } else
01367 text = "out of memory\n";
01368
01369 if (clp->internal->error_handler != 0)
01370 (*clp->internal->error_handler)(text);
01371 else
01372 fputs(text, stderr);
01373 }
01374
01375 int
01376 Clp_OptionError(Clp_Parser *clp, const char *fmt, ...)
01377 {
01378 Clp_BuildString *bs;
01379 va_list val;
01380 va_start(val, fmt);
01381 bs = Clp_VaOptionError(clp, 0, fmt, val);
01382 va_end(val);
01383 do_error(clp, bs);
01384 free_build_string(bs);
01385 return 0;
01386 }
01387
01388 static int
01389 ambiguity_error(Clp_Parser *clp, int ambiguous, int *ambiguous_values,
01390 Clp_Option *opt, const char *prefix,
01391 const char *fmt, ...)
01392 {
01393 Clp_BuildString *bs;
01394 int i;
01395 va_list val;
01396 va_start(val, fmt);
01397 bs = Clp_VaOptionError(clp, 0, fmt, val);
01398 if (!bs) goto done;
01399
01400 append_build_string(bs, clp->internal->program_name, -1);
01401 append_build_string(bs, ": (Possibilities are", -1);
01402
01403 for (i = 0; i < ambiguous && i < MAX_AMBIGUOUS_VALUES; i++) {
01404 int val = ambiguous_values[i];
01405 const char *no_dash = "";
01406 if (val < 0) val = -(val + 1), no_dash = "no-";
01407 if (i == 0)
01408 append_build_string(bs, " ", 1);
01409 else if (i == ambiguous - 1)
01410 append_build_string(bs, (i == 1 ? " and " : ", and "), -1);
01411 else
01412 append_build_string(bs, ", ", 2);
01413 append_build_string(bs, prefix, -1);
01414 append_build_string(bs, no_dash, -1);
01415 append_build_string(bs, opt[val].long_name, -1);
01416 }
01417
01418 if (ambiguous > MAX_AMBIGUOUS_VALUES)
01419 append_build_string(bs, ", and others", -1);
01420 append_build_string(bs, ".)\n", -1);
01421 va_end(val);
01422
01423 done:
01424 do_error(clp, bs);
01425 free_build_string(bs);
01426 return 0;
01427 }
01428
01429 #ifdef __cplusplus
01430 }
01431 #endif