Greg Hudson <[EMAIL PROTECTED]> 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; > +}