diff options
-rw-r--r-- | lib/libc/include/plgetopt.h | 75 | ||||
-rw-r--r-- | lib/libc/src/plc.def | 5 | ||||
-rw-r--r-- | lib/libc/src/plgetopt.c | 164 |
3 files changed, 197 insertions, 47 deletions
diff --git a/lib/libc/include/plgetopt.h b/lib/libc/include/plgetopt.h index 423feab3..fcdf23b6 100644 --- a/lib/libc/include/plgetopt.h +++ b/lib/libc/include/plgetopt.h @@ -57,6 +57,14 @@ typedef enum PL_OPT_BAD /* invalid option (and value) */ } PLOptStatus; +typedef struct PLLongOpt +{ + const char * longOptName; /* long option name string */ + PRIntn longOption; /* value put in PLOptState for this option. */ + PRBool valueRequired; /* If option name not followed by '=', */ + /* value is the next argument from argv. */ +} PLLongOpt; + typedef struct PLOptState { char option; /* the name of the option */ @@ -64,13 +72,80 @@ typedef struct PLOptState PLOptionInternal *internal; /* private processing state */ + PRIntn longOption; /* value from PLLongOpt put here */ + PRIntn longOptIndex; /* index into caller's array of PLLongOpts */ } PLOptState; +/* + * PL_CreateOptState + * + * The argument "options" points to a string of single-character option + * names. Option names that may have an option argument value must be + * followed immediately by a ':' character. + */ PR_EXTERN(PLOptState*) PL_CreateOptState( PRIntn argc, char **argv, const char *options); +/* + * PL_CreateLongOptState + * + * Alternative to PL_CreateOptState. + * Allows caller to specify BOTH a string of single-character option names, + * AND an array of structures describing "long" (keyword) option names. + * The array is terminated by a structure in which longOptName is NULL. + * Long option values (arguments) may always be given as "--name=value". + * If PLLongOpt.valueRequired is not PR_FALSE, and the option name was not + * followed by '=' then the next argument from argv is taken as the value. + */ +PR_EXTERN(PLOptState*) PL_CreateLongOptState( + PRIntn argc, char **argv, const char *options, + const PLLongOpt *longOpts); +/* + * PL_DestroyOptState + * + * Call this to destroy the PLOptState returned from PL_CreateOptState or + * PL_CreateLongOptState. + */ PR_EXTERN(void) PL_DestroyOptState(PLOptState *opt); +/* + * PL_GetNextOpt + * + * When this function returns PL_OPT_OK, + * - opt->option will hold the single-character option name that was parsed, + * or zero. + * When opt->option is zero, the token parsed was either a "long" (keyword) + * option or a positional parameter. + * For a positional parameter, + * - opt->longOptIndex will contain -1, and + * - opt->value will point to the positional parameter string. + * For a long option name, + * - opt->longOptIndex will contain the non-negative index of the + * PLLongOpt structure in the caller's array of PLLongOpt structures + 8 corresponding to the long option name, and + * For a single-character or long option, + * - opt->longOption will contain the value of the single-character option + * name, or the value of the longOption from the PLLongOpt structure + * for that long option. See notes below. + * - opt->value will point to the argument option string, or will + * be NULL if no argument option string was given. + * When opt->option is non-zero, + * - opt->longOptIndex will be -1 + * When this function returns PL_OPT_EOL, or PL_OPT_BAD, the contents of + * opt are undefined. + * + * Notes: It is possible to ignore opt->option, and always look at + * opt->longOption instead. opt->longOption will contain the same value + * as opt->option for single-character option names, and will contain the + * value of longOption from the PLLongOpt structure for long option names. + * This means that it is possible to equivalence long option names to + * single character names by giving the longOption in the PLLongOpt struct + * the same value as the single-character option name. + * For long options that are NOT intended to be equivalent to any single- + * character option, the longOption value should be chosen to not match + * any possible single character name. It might be advisable to choose + * longOption values greater than 0xff for such long options. + */ PR_EXTERN(PLOptStatus) PL_GetNextOpt(PLOptState *opt); PR_END_EXTERN_C diff --git a/lib/libc/src/plc.def b/lib/libc/src/plc.def index 18bb88d6..bb996a13 100644 --- a/lib/libc/src/plc.def +++ b/lib/libc/src/plc.def @@ -97,3 +97,8 @@ libVersionPoint; ;+ global: PL_strtok_r; ;+} NSPR_4.0; +;+ +;+NSPR_4.7 { +;+ global: +PL_CreateLongOptState; +;+} NSPR_4.2; diff --git a/lib/libc/src/plgetopt.c b/lib/libc/src/plgetopt.c index e2ccfa1b..0fe4fafe 100644 --- a/lib/libc/src/plgetopt.c +++ b/lib/libc/src/plgetopt.c @@ -57,7 +57,10 @@ struct PLOptionInternal char **argv; /* vector of pointers to arguments */ PRIntn xargc; /* which one we're processing now */ const char *xargv; /* where within *argv[xargc] */ - PRBool minus; /* do we already have the '-'? */ + PRIntn minus; /* do we already have the '-'? */ + const PLLongOpt *longOpts; /* Caller's array */ + PRBool endOfOpts; /* have reached a "--" argument */ + PRIntn optionsLen; /* is strlen(options) */ }; /* @@ -70,39 +73,55 @@ struct PLOptionInternal PR_IMPLEMENT(PLOptState*) PL_CreateOptState( PRIntn argc, char **argv, const char *options) { + return PL_CreateLongOptState( argc, argv, options, NULL); +} /* PL_CreateOptState */ + +PR_IMPLEMENT(PLOptState*) PL_CreateLongOptState( + PRIntn argc, char **argv, const char *options, + const PLLongOpt *longOpts) +{ PLOptState *opt = NULL; - if (NULL == options) + PLOptionInternal *internal; + + if (NULL == options) + { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); - else + return opt; + } + + opt = PR_NEWZAP(PLOptState); + if (NULL == opt) { - opt = PR_NEWZAP(PLOptState); - if (NULL == opt) - PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); - else - { - PLOptionInternal *internal = PR_NEW(PLOptionInternal); - if (NULL == internal) - { - PR_DELETE(opt); - PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); - } - else - { - opt->option = 0; - opt->value = NULL; - opt->internal = internal; - - internal->argc = argc; - internal->argv = argv; - internal->xargc = 0; - internal->xargv = &static_Nul; - internal->minus = PR_FALSE; - internal->options = options; - } - } + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return opt; + } + + internal = PR_NEW(PLOptionInternal); + if (NULL == internal) + { + PR_DELETE(opt); + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; } + + opt->option = 0; + opt->value = NULL; + opt->internal = internal; + opt->longOption = 0; + opt->longOptIndex = -1; + + internal->argc = argc; + internal->argv = argv; + internal->xargc = 0; + internal->xargv = &static_Nul; + internal->minus = 0; + internal->options = options; + internal->longOpts = longOpts; + internal->endOfOpts = PR_FALSE; + internal->optionsLen = PL_strlen(options); + return opt; -} /* PL_CreateOptState */ +} /* PL_CreateLongOptState */ /* ** Destroy object created by CreateOptState() @@ -116,56 +135,107 @@ PR_IMPLEMENT(void) PL_DestroyOptState(PLOptState *opt) PR_IMPLEMENT(PLOptStatus) PL_GetNextOpt(PLOptState *opt) { PLOptionInternal *internal = opt->internal; - PRIntn cop, eoo = PL_strlen(internal->options); + opt->longOption = 0; + opt->longOptIndex = -1; /* ** If the current xarg points to nul, advance to the next ** element of the argv vector. If the vector index is equal ** to argc, we're out of arguments, so return an EOL. - ** Note whether the first character of the new argument is - ** a '-' and skip by it if it is. + ** Note whether the first character of the new argument is + ** a '-' and skip by it if it is. */ while (0 == *internal->xargv) { internal->xargc += 1; if (internal->xargc >= internal->argc) - { - opt->option = 0; - opt->value = NULL; - return PL_OPT_EOL; - } + { + opt->option = 0; + opt->value = NULL; + return PL_OPT_EOL; + } internal->xargv = internal->argv[internal->xargc]; - internal->minus = ('-' == *internal->xargv ? PR_TRUE : PR_FALSE); /* not it */ - if (internal->minus) internal->xargv += 1; /* and consume */ + internal->minus = 0; + if (!internal->endOfOpts && ('-' == *internal->xargv)) + { + internal->minus++; + internal->xargv++; /* and consume */ + if ('-' == *internal->xargv && internal->longOpts) + { + internal->minus++; + internal->xargv++; + if (0 == *internal->xargv) + { + internal->endOfOpts = PR_TRUE; + } + } + } } /* - ** If we already have a '-' in hand, xargv points to the next + ** If we already have a '-' or '--' in hand, xargv points to the next ** option. See if we can find a match in the list of possible ** options supplied. */ + if (internal->minus == 2) + { + char * foundEqual = strchr(internal->xargv,'='); + PRIntn optNameLen = foundEqual ? (foundEqual - internal->xargv) : + strlen(internal->xargv); + const PLLongOpt *longOpt = internal->longOpts; + + opt->option = 0; + opt->value = NULL; + + for (; longOpt->longOptName; ++longOpt) + { + if (strncmp(longOpt->longOptName, internal->xargv, optNameLen)) + continue; /* not a possible match */ + if (strlen(longOpt->longOptName) != optNameLen) + continue; /* not a match */ + /* option name match */ + opt->longOptIndex = longOpt - internal->longOpts; + opt->longOption = longOpt->longOption; + if (foundEqual) + { + opt->value = foundEqual[1] ? foundEqual + 1 : NULL; + } + else if (longOpt->valueRequired) + { + opt->value = internal->argv[++(internal->xargc)]; + } + internal->xargv = &static_Nul; /* consume this */ + return PL_OPT_OK; + } + internal->xargv = &static_Nul; /* consume this */ + return PL_OPT_BAD; + } if (internal->minus) { + PRIntn cop; + PRIntn eoo = internal->optionsLen; for (cop = 0; cop < eoo; ++cop) { if (internal->options[cop] == *internal->xargv) { - opt->option = *internal->xargv; - internal->xargv += 1; + opt->option = *internal->xargv++; + opt->longOption = opt->option & 0xff; /* ** if options indicates that there's an associated - ** value, this argv is finished and the next is the - ** option's value. + ** value, this argv is finished and the next is the + ** option's value. */ if (':' == internal->options[cop + 1]) { - if (0 != *internal->xargv) return PL_OPT_BAD; + if (0 != *internal->xargv) + return PL_OPT_BAD; opt->value = internal->argv[++(internal->xargc)]; internal->xargv = &static_Nul; - internal->minus = PR_FALSE; + internal->minus = 0; } - else opt->value = NULL; + else + opt->value = NULL; return PL_OPT_OK; } } |