예제 #1
0
파일: cliopts.c 프로젝트: Sweebr/couchnode
/**
 * 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;
}
예제 #2
0
/**
 * 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;
}