/** * This function tries to extract a single value for an option key. * If it successfully has extracted a value, it returns MODE_VALUE. * If the entry takes no arguments, then the current string is a key, * and it will return MODE_OPTION. On error, MODE_ERROR is set, and errp * will point to a string. * * @param entry The current entry * @param value the string which might be a value * @errp a pointer which will be populated with the address of the error, if any * * @return a MODE_* type */ static int parse_value(struct cliopts_priv *ctx, const char *value) { cliopts_entry *entry = ctx->current; size_t vlen = strlen(value); cliopts_extractor_func exfn = NULL; int exret; int is_option = 0; cliopt_debug("Called with %s, want=%d", value, ctx->wanted); if (ctx->argsplit) { if (vlen > 2 && strncmp(value, "--", 2) == 0) { is_option = 1; } else if (*value == '-') { is_option = 1; } } if (is_option) { ctx->errstr = "Expected option. Got '-' or '--' prefixed value " "(use = if this is really a value)"; ctx->errnum = CLIOPTS_ERR_NEED_ARG; return MODE_ERROR; } if (entry->ktype == CLIOPTS_ARGT_STRING) { char *vp = malloc(vlen+1); vp[vlen] = 0; strcpy(vp, value); *(char**)entry->dest = vp; return WANT_OPTION; } if (entry->ktype == CLIOPTS_ARGT_FLOAT) { exfn = extract_float; } else if (entry->ktype == CLIOPTS_ARGT_HEX) { exfn = extract_hex; } else if (entry->ktype == CLIOPTS_ARGT_INT) { exfn = extract_int; } else if (entry->ktype == CLIOPTS_ARGT_UINT) { exfn = extract_uint; } else { fprintf(stderr, "Unrecognized type %d. Abort.\n", entry->ktype); } exret = exfn(value, entry->dest, &ctx->errstr); if (exret == 0) { return WANT_OPTION; } else { ctx->errnum = CLIOPTS_ERR_BAD_VALUE; } return MODE_ERROR; }
/** * Like parse_value, except for keys. * * @param entries all option entries * @param key the current string from argv * @param errp a pointer which will be populated with the address of an error * string * * @param found_entry a pointer to be populated with the relevant entry * structure * @param kp a pointer which will be poplated with the address of the 'sanitized' * key string * * @param valp if the string is actually a key-value pair (i.e. --foo=bar) then * this will be populated with the address of that string * * @return MODE_OPTION if an option was found, MODE_VALUE if the current option * is a value, or MODE_ERROR on error */ static int parse_option(struct cliopts_priv *ctx, const char *key) { cliopts_entry *cur = NULL; int prefix_len = 0; unsigned ii = 0; const char *valp = NULL; size_t klen; klen = strlen(key); ctx->errstr = NULL; ctx->prev = ctx->current; ctx->current = NULL; cliopt_debug("Called with %s, want=%d", key, ctx->wanted); if (klen == 0) { ctx->errstr = "Got an empty string"; ctx->errnum = CLIOPTS_ERR_BADOPT; return MODE_ERROR; } /** * figure out what type of option it is.. * it can either be a -c, --long, or --long=value */ while (*key == '-') { key++; prefix_len++; klen--; } for (ii = 0; ii < klen; ii++) { if (key[ii] == '"' || key[ii] == '\'') { ii = klen; break; } else if (key[ii] == '=' && prefix_len == 2) { /* only split on '=' if we're called as '--' */ valp = key + (ii + 1); klen = ii; break; } } GT_PARSEOPT: memset(ctx->current_value, 0, sizeof(ctx->current_value)); memcpy(ctx->current_key, key, klen); ctx->current_key[ii] = '\0'; if (valp) { strcpy(ctx->current_value, valp); } if (prefix_len == 0 || prefix_len > 2) { if (ctx->settings->restargs) { key -= prefix_len; ctx->settings->restargs[ctx->settings->nrestargs++] = key; return WANT_OPTION; } else if (ctx->prev && ctx->prev->ktype == CLIOPTS_ARGT_NONE) { ctx->errstr = "Option does not accept a value"; ctx->errnum = CLIOPTS_ERR_ISSWITCH; } else { ctx->errstr = "Options must begin with either '-' or '--'"; ctx->errnum = CLIOPTS_ERR_BADOPT; } return MODE_ERROR; } /** * --help or -? */ if ( (prefix_len == 1 && *key == '?') || (prefix_len == 2 && strcmp(key, "help") == 0)) { return MODE_HELP; } /** * Bare -- */ if (prefix_len == 2 && *key == '\0') { if (ctx->settings->restargs) { } if (ctx->wanted == WANT_VALUE) { ctx->errnum = CLIOPTS_ERR_NEED_ARG; ctx->errstr = "Found bare '--', but value wanted"; return MODE_ERROR; } return MODE_RESTARGS; } for (cur = ctx->entries; cur->dest; cur++) { int optlen; if (prefix_len == 1) { if (cur->kshort == ctx->current_key[0]) { ctx->current = cur; break; } continue; } /** else, prefix_len is 2 */ if (cur->klong == NULL || (optlen = strlen(cur->klong) != klen) || strncmp(cur->klong, ctx->current_key, klen) != 0) { continue; } ctx->current = cur; break; } if (!ctx->current) { ctx->errstr = "Unknown option"; ctx->errnum = CLIOPTS_ERR_UNRECOGNIZED; return MODE_ERROR; } ctx->current->found++; if (ctx->current->ktype != CLIOPTS_ARGT_NONE) { ctx->wanted = WANT_VALUE; } if (ctx->current_value[0]) { /* --foo=bar */ if (ctx->current->ktype == CLIOPTS_ARGT_NONE) { ctx->errnum = CLIOPTS_ERR_ISSWITCH; ctx->errstr = "Option takes no arguments"; return MODE_ERROR; } else { return parse_value(ctx, ctx->current_value); } } if (ctx->current->ktype == CLIOPTS_ARGT_NONE) { *(char*)ctx->current->dest = 1; if (prefix_len == 1 && klen > 1) { /** * e.g. ls -lsh */ klen--; key++; /** * While we can also possibly recurse, this may be a security risk * as it wouldn't take much to cause a deep recursion on the stack * which will cause all sorts of nasties. */ goto GT_PARSEOPT; } return WANT_OPTION; } else if (prefix_len == 1 && klen > 1) { /* e.g. patch -p0 */ ctx->wanted = WANT_VALUE; return parse_value(ctx, key + 1); } return WANT_VALUE; }