You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@apr.apache.org by Karl Fogel <kf...@galois.collab.net> on 2000/11/24 22:40:34 UTC

Re: [PATCH] apr_getopt_long interface update and interleaving support

Greg Hudson <gh...@mit.edu> writes:
> Okay, here is my finalized patch for apr_getopt_long, to change over
> to the new simplified interface and to support interleaved arguments.
> Thanks, Greg, for clearing up how to do an anonymous checkout.
> 
> I have asked rbb for commit access, but this being Thanksgiving
> weekend, I'm not sure if he'll be up and about until Monday.  So it
> might speed things up if someone who is around (such as Greg Stein)
> could check this in.

I'm around, and I'd be happy to check it in (or Greg S. can beat me to
it, is fine too).

Could you write a log message to accompany the commit?  I could write
it myself, based on reading the patch, but you can probably do it
better, and with less effort.

I'll put your name and addr when I commit, of course, so people know
who really made the change.

There's also been talk about eliminating the redundant `opts' param to
apr_getopt_long().  Did you not do that because you didn't want to mix
two changes in one commit, or because you don't think it's a good
idea?

-K

> Index: include/apr_getopt.h
> ===================================================================
> RCS file: /home/cvspublic/apr/include/apr_getopt.h,v
> retrieving revision 1.22
> diff -u -r1.22 apr_getopt.h
> --- include/apr_getopt.h	2000/11/17 07:56:52	1.22
> +++ include/apr_getopt.h	2000/11/24 23:39:58
> @@ -77,23 +77,25 @@
>      /** count of arguments */
>      int argc;
>      /** array of pointers to arguments */
> -    char *const *argv;
> +    char **argv;
>      /** argument associated with option */
>      char const* place;
> +    /** set to nonzero to support interleaving */
> +    int interleave;
> +    /** range of non-option arguments skipped for interleaving */
> +    int skip_start;
> +    int skip_end;
>  };
>  
> -typedef struct apr_getopt_long_t apr_getopt_long_t;
> +typedef struct apr_option_t apr_option_t;
>  
> -/* structure representing a single longopt */
> -struct apr_getopt_long_t {
> -    /** the name of the long argument (sans "--") */
> +struct apr_option_t {
> +    /** long option name, or NULL if option has no long name */
>      const char *name;
> -    /** 0 for no arg, 1 for arg */
> +    /** option letter, or a value greater than 255 if option has no letter */
> +    int optch;
> +    /** nonzero if option takes an argument */
>      int has_arg;
> -    /** Either the short option char that this option corresponds to 
> -     * or a unique integer > 255 
> -     */
> -    int val;
>  };
>  
>  /**
> @@ -106,7 +108,7 @@
>   * @deffunc apr_status_t apr_initopt(apr_getopt_t **os, apr_pool_t *cont,int argc, char *const *argv)
>   */
>  APR_DECLARE(apr_status_t) apr_initopt(apr_getopt_t **os, apr_pool_t *cont,
> -                                      int argc, char *const *argv);
> +                                      int argc, char **argv);
>  
>  /**
>   * Parse the options initialized by apr_initopt().
> @@ -131,26 +133,15 @@
>  /**
>   * Parse the options initialized by apr_initopt(), accepting long
>   * options beginning with "--" in addition to single-character
> - * options beginning with "-" (which are passed along to apr_getopt).
> - *
> - * Long options are accepted in both "--foo bar" and well as
> - * "--foo=bar" format
> - *
> - * End of argument processing if we encounter "--" or any option that
> - * doesn't start with "-" or "--".
> - *
> - * @param os       The apr_opt_t structure returned by apr_initopt()
> - * @param opts     A string of acceptable single-character options to the
> - *                 program.  Characters followed by ":" are required to have
> - *                 an argument associated
> - * @param longopts A pointer to an array of apr_long_option_t structures, which
> - *                 can be initialized with { "name", has_args, val }.  has_args
> - *                 is nonzero if the option requires an argument.  A structure
> - *                 with a NULL name terminates the list
> - * @param optval   The next option character parsed, or the value of "optval"
> - *                 from the appropriate apr_long_option_t structure if
> - *                 the next option is a long option.
> - * @param optarg   The argument following the option, if any
> + * options beginning with "-".
> + * @param os     The apr_getopt_t structure created by apr_initopt()
> + * @param opts   A pointer to a list of apr_option_t structures, which can
> + *               be initialized with { "name", optch, has_args }.  has_args
> + *               is nonzero if the option requires an argument.  A structure
> + *               with an optch value of 0 terminates the list.
> + * @param optch  Receives the value of "optch" from the apr_option_t structure
> + *               corresponding to the next option matched.
> + * @param optarg Receives the argument following the option, if any.
>   * @tip There are four potential status values on exit.   They are:
>   * <PRE>
>   *             APR_EOF      --  No more options to parse
> @@ -158,11 +149,14 @@
>   *             APR_BADARG   --  No argument followed @parameter:
>   *             APR_SUCCESS  --  The next option was found.
>   * </PRE>
> - * @deffunc apr_status_t apr_getopt_long(apr_getopt_t *os, const char *opts, const apr_getopt_long_t *longopts, int *optval, const char **optarg) */
> -APR_DECLARE(apr_status_t) apr_getopt_long(apr_getopt_t *os, 
> -                                          const char *opts, 
> -                                          const apr_getopt_long_t *long_opts,
> -                                          int *optval, 
> -                                          const char **optarg);
> -
> + * When APR_SUCCESS is returned, os->ind gives the index of the first
> + * non-option argument.  On error, a message will be printed to stdout unless
> + * os->err is set to 0.  If os->interleave is set to nonzero, options can come
> + * after arguments, and os->argv will be permuted to leave non-option arguments
> + * at the end.
> + * @deffunc apr_status_t apr_getopt_long(apr_getopt_t *os, const apr_option_t *opts, int *optch, const char **optarg)
> + */
> +APR_DECLARE(apr_status_t) apr_getopt_long(apr_getopt_t *os,
> +					  const apr_option_t *opts,
> +					  int *optch, const char **optarg);
>  #endif  /* ! APR_GETOPT_H */
> Index: misc/unix/getopt.c
> ===================================================================
> RCS file: /home/cvspublic/apr/misc/unix/getopt.c,v
> retrieving revision 1.24
> diff -u -r1.24 getopt.c
> --- misc/unix/getopt.c	2000/11/17 07:56:52	1.24
> +++ misc/unix/getopt.c	2000/11/24 23:39:58
> @@ -41,14 +41,15 @@
>  static const char *pretty_path (const char *name) 
>  {
>      const char *p;
> +
>      if (!(p = strrchr(name, '/')))
> -        return p;
> +        return name;
>      else
> -        return ++p;
> +        return p + 1;
>  }
>  
>  APR_DECLARE(apr_status_t) apr_initopt(apr_getopt_t **os, apr_pool_t *cont,
> -                                     int argc, char *const *argv)
> +                                     int argc, char **argv)
>  {
>      *os = apr_palloc(cont, sizeof(apr_getopt_t));
>      (*os)->cont = cont;
> @@ -57,6 +58,8 @@
>      (*os)->place = EMSG;
>      (*os)->argc = argc;
>      (*os)->argv = argv;
> +    (*os)->interleave = 0;
> +    (*os)->skip_start = (*os)->skip_end = (*os)->ind;
>      return APR_SUCCESS;
>  }
>  
> @@ -130,90 +133,158 @@
>      return APR_SUCCESS;
>  }
>  
> -APR_DECLARE(apr_status_t) apr_getopt_long(apr_getopt_t *os, 
> -                                          const char *opts, 
> -                                          const apr_getopt_long_t *long_opts,
> -                                          int *optval, 
> -                                          const char **optarg)
> -     
> -{
> -    const apr_getopt_long_t *ptr;
> -    const char *opt = os->argv[os->ind];
> -    const char *arg = os->argv[os->ind +1];
> -    int arg_index_incr = 1;
> -  
> -    /* Finished processing opts */
> -    if (os->ind >= os->argc)
> -        return APR_EOF;
> -
> -    /* End of options processing if we encounter "--" */
> -    if (strcmp(opt, "--") == 0)
> -        return APR_EOF;
> -
> -    /* 
> -     * End of options processing if we encounter something that
> -     * doesn't start with "-" or "--" (it's not an option if we hit it
> -     * here, it's an argument) 
> -     */
> -    if (*opt != '-')
> -        return APR_EOF;
> +/* Reverse the sequence argv[start..start+len-1]. */
> +static void reverse(char **argv, int start, int len)
> +{
> +    char *temp;
>  
> -    if ((os->ind + 1) >= os->argc) 
> -        arg = NULL;
> +    for (; len >= 2; start++, len -= 2) {
> +	temp = argv[start];
> +	argv[start] = argv[start + len - 1];
> +	argv[start + len - 1] = temp;
> +    }
> +}
>  
> -    /* Handle --foo=bar style opts */
> -    if (strchr(opt, '=')) {
> -        const char *index = strchr(opt, '=') + 1;
> -        opt = apr_pstrndup(os->cont, opt, ((index - opt) - 1));
> -        if (*index != '\0') /* account for "--foo=" */
> -            arg = apr_pstrdup(os->cont, index);
> -        arg_index_incr = 0;
> -    }                       
> -
> -    /* If it's a longopt */
> -    if (opt[1] == '-') {
> -        /* see if it's in our array of long opts */
> -        for (ptr = long_opts; ptr->name; ptr++) {
> -            if (strcmp((opt + 2), ptr->name) == 0) { /* it's in the array */
> -                if (ptr->has_arg) { 
> -                    if (((os->ind + 1) >= os->argc) 
> -                        && (arg == NULL)) {
> -                        fprintf(stderr,
> -                                "%s: option requires an argument: %s\n",
> -                                pretty_path(*os->argv), opt);
> -                        return APR_BADARG;
> -                    }
> -                                
> -                    /* If we make it here, then we should be ok. */
> -                    *optarg = arg;
> -                    os->ind += arg_index_incr;
> -                }
> -                else { /* has no arg */ 
> -                    *optarg = NULL;
> -                }
> -                *optval = ptr->val;
> -                ++os->ind;
> -                return APR_SUCCESS;
> -            } 
> -        }
> +/*
> + * 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 we get here, then we don't have the longopt in our
> -         * longopts array 
> -         */
> -        fprintf(stderr, "%s: illegal option: %s\n", 
> -                pretty_path(*os->argv), opt);
> -        return APR_BADCH;
> +    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 that 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);
>      }
>  
> -    {   /* otherwise, apr_getopt gets it. */
> -        char optch;
> -        apr_status_t status;
> -        status = apr_getopt (os, opts, &optch, optarg);
> -        *optval = optch;
> -        return status;
> -    }
> +    /* Reset skip range to the new location of the non-option sequence. */
> +    os->skip_start += len2;
> +    os->skip_end += len2;
> +}
> +
> +/* Helper function to print out an error involving a long option */
> +static apr_status_t serr(apr_getopt_t *os, const char *err, const char *str,
> +			 apr_status_t status)
> +{
> +    if (os->err)
> +	fprintf(stderr, "%s: %s: %s\n", pretty_path(*os->argv), err, str);
> +    return status;
>  }
>  
> +/* Helper function to print out an error involving a short option */
> +static apr_status_t cerr(apr_getopt_t *os, const char *err, int ch,
> +			 apr_status_t status)
> +{
> +    if (os->err)
> +	fprintf(stderr, "%s: %s: %c\n", pretty_path(*os->argv), err, ch);
> +    return status;
> +}
> +
> +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;
> +}