[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

apr_getopt_long implementation with permutation

From: Greg Hudson <ghudson_at_mit.edu>
Date: 2000-11-24 19:43:56 CET

For review (again, can't make a patch until I resolve my apr checkout
issues), here is an apr_getopt_long implementation which supports
argument interleaving like the gnu getopt_long does. Unlike the gnu
getopt_long, you have to set os->interleave = 1 to turn on
interleaving; the gnu version does interleaving by default unless
POSIXLY_CORRECT is set in the environment.

There are a couple of other changes not shown here; "interleave",
"skip_start", and "skip_end" fields need to be added to the
apr_getopt_t structure, and "argv" has to become a "char **" instead
of a "char *const *". When I can submit a real patch, all that will
show up, of course.

/* Reverse the elements argv[start..start+len-1]. */
static void reverse(char **argv, int start, int len)
{
    char *temp;

    for (; len >= 2; start++, len -= 2) {
        temp = argv[start];
        argv[start] = argv[start + len - 1];
        argv[start + len - 1] = temp;
    }
}

/*
 * Permute os->argv with the goal that non-option arguments will all
 * appear at the end. os->skip_start is where we started skipping
 * non-option arguments, os->skip_end is where we stopped, and os->ind
 * is where we are now.
 */
static void permute(apr_getopt_t *os)
{
    int len1 = os->skip_end - os->skip_start;
    int len2 = os->ind - os->skip_end;

    if (os->interleave) {
        /*
         * Exchange the sequences argv[os->skip_start..os->skip_end-1] and
         * argv[os->skip_end..os->ind-1]. The easiest way to do this is
         * to reverse the entire range and then reverse the two
         * sub-ranges.
         */
        reverse(os->argv, os->skip_start, len1 + len2);
        reverse(os->argv, os->skip_start, len2);
        reverse(os->argv, os->skip_start + len2, len1);
    }

    /* Reset skip range to the new location of the non-option sequence. */
    os->skip_start += len2;
    os->skip_end += len2;
}

APR_DECLARE(apr_status_t) apr_getopt_long(apr_getopt_t *os,
                                          const apr_option_t *opts,
                                          int *optch, const char **optarg)
{
    const char *p;
    int i, len;

    /* Let the calling program reset option processing. */
    if (os->reset) {
        os->place = EMSG;
        os->ind = 1;
        os->reset = 0;
    }

    /*
     * We can be in one of two states: in the middle of processing a
     * run of short options, or about to process a new argument.
     * Since the second case can lead to the first one, handle that
     * one first. */
    p = os->place;
    if (*p == '\0') {
        /* If we are interleaving, skip non-option arguments. */
        if (os->interleave) {
            while (os->ind < os->argc && *os->argv[os->ind] != '-')
                os->ind++;
            os->skip_end = os->ind;
        }
        if (os->ind >= os->argc || *os->argv[os->ind] != '-') {
            os->ind = os->skip_start;
            return APR_EOF;
        }

        p = os->argv[os->ind++] + 1;
        if (*p == '-' && p[1] != '\0') { /* Long option */
            /* Search for the long option name in the caller's table. */
            p++;
            for (i = 0; opts[i].optch != 0; i++) {
                len = strlen(opts[i].name);
                if (strncmp(p, opts[i].name, len) == 0
                    && (p[len] == '\0' || p[len] == '='))
                    break;
            }
            if (opts[i].optch == 0) /* No match */
                return serr(os, "invalid option", p - 2, APR_BADCH);
            *optch = opts[i].optch;

            if (opts[i].has_arg) {
                if (p[len] == '=') /* Argument inline */
                    *optarg = p + len + 1;
                else if (os->ind >= os->argc) /* Argument missing */
                    return serr(os, "missing argument", p - 2, APR_BADARG);
                else /* Argument in next arg */
                    *optarg = os->argv[os->ind++];
            } else {
                *optarg = NULL;
                if (p[len] == '=')
                    return serr(os, "erroneous argument", p - 2, APR_BADARG);
            }
            permute(os);
            return APR_SUCCESS;
        } else if (*p == '-') { /* Bare "--"; we're done */
            permute(os);
            os->ind = os->skip_start;
            return APR_EOF;
        }
        else if (*p == '\0') /* Bare "-" is illegal */
            return serr(os, "invalid option", p, APR_BADCH);
    }

    /*
     * Now we're in a run of short options, and *p is the next one.
     * Look for it in the caller's table.
     */
    for (i = 0; opts[i].optch != 0; i++) {
        if (*p == opts[i].optch)
            break;
    }
    if (opts[i].optch == 0) /* No match */
        return cerr(os, "invalid option character", *p, APR_BADCH);
    *optch = *p++;

    if (opts[i].has_arg) {
        if (*p != '\0') /* Argument inline */
            *optarg = p;
        else if (os->ind >= os->argc) /* Argument missing */
            return cerr(os, "option requires an argument", *optch, APR_BADARG);
        else /* Argument in next arg */
            *optarg = os->argv[os->ind++];
        os->place = EMSG;
    } else {
        *optarg = NULL;
        os->place = p;
    }

    permute(os);
    return APR_SUCCESS;
}
Received on Sat Oct 21 14:36:15 2006

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.