help-gengetopt
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [help-gengetopt] make cmd_line_list and cmd_line_list_tmp static?


From: Lorenzo Bettini
Subject: Re: [help-gengetopt] make cmd_line_list and cmd_line_list_tmp static?
Date: Thu, 11 May 2006 11:37:11 +0200
User-agent: Thunderbird 1.5.0.2 (X11/20060501)

Andre Noll wrote:
On 12:41, Lorenzo Bettini wrote:
on what happend during the first call. How about coding up an own
version of getopt() with sane semantics?
that would be the other possibility I was thinking of... but I'm quite scared of writing another version of getopt with the same semantics... would you be interested in that?

Something like the attached?

This is glibc's getopt() and getopt_long() in a single file, with
its monster getopt_internal_r() function split up into three smaller
functions, and some glibc cruft removed.

It's still a bit crufty, so it sure needs some more work. But it is
hopefully more readable than the original glibc source and definitely
much shorter.

The getopt() and getopt_long() functions of this version take the same
arguments as the original ones, so they may used as a drop-in
replacement for the corresponding functions of the system.

At the bottom of the file there's a test included. Activate it with

        gcc -Wall -DTEST getopt.c

Regards
Andre


Hi Andre

thanks for the file, I guess I have the problem clear now.

I think you're right about the fact that even with non-dynamic memory the problem still raises (although I haven't tried it I guess it's like you said).

I think the problem is a sort of design problem of the getopt implementation itself, or, at least, the implementation is correct but under the (quite strong) condition that if you call one of the functions twice you have to use the same arguments (I mean the same argv).

I'd like to post a bug report at the glibc bugzilla about this, but first I'd like to have a confirmation from you about the problem (by the way could you please send me, in private, all the original sources of glibc of getopt stuff you extracted this file from?).


struct getopt_data {
        /*
         * These have exactly the same meaning as the corresponding global 
variables,
         * except that they are used for the reentrant versions of getopt.
         */
        int optind;
        int opterr;
        int optopt;
        char *optarg;

        /* True if the internal members have been initialized.  */
        int initialized;

        /*
         * The next char to be scanned in the option-element in which the last 
option
         * character we returned was found.  This allows us to pick up the scan 
where
         * we left off.  If this is zero, or a null string, it means resume the 
scan by
         * advancing to the next ARGV-element.
         */
        char *nextchar;

        /*
         * Describe the part of ARGV that contains non-options that have been 
skipped.
         * `first_nonopt' is the index in ARGV of the first of them; 
`last_nonopt' is
         * the index after the last of them.
         */
        int first_nonopt;
        int last_nonopt;
};

so internally this struct is used, instead of the global variables, and the problem, I think, is in the last three fields...

/*
 * Index in ARGV of the next element to be scanned.  This is used for
 * communication to and from the caller and for communication between
 * successive calls to `getopt'.
 *
 * On entry to `getopt', zero means this is the first call; initialize.
 *
 * When `getopt' returns -1, this is the index of the first of the non-option
 * elements that the caller should itself scan.
 *
 * Otherwise, `optind' communicates from one call to the next how much of ARGV
 * has been scanned so far.
 *
 * 1003.2 says this must be 1 before any call.
 */
int optind = 1;

this is the confirmation that the standard says that it must be 1, but they use the 0 value for initializations...

/*
 * Exchange two adjacent subsequences of ARGV.  One subsequence is elements
 * [first_nonopt,last_nonopt) which contains all the non-options that have been
 * skipped so far.  The other is elements [last_nonopt,optind), which contains
 * all the options processed since those non-options were skipped.
 * `first_nonopt' and `last_nonopt' are relocated so that they describe the new
 * indices of the non-options in ARGV after they are moved.
 */
static void exchange(char **argv, struct getopt_data *d)
{
        int bottom = d->first_nonopt;
        int middle = d->last_nonopt;
        int top = d->optind;
        char *tem;


<snip>
this function exchanges elements of argv according to the values d->first_nonopt and d->last_nonopt... so their values must be correct otherwise we spoil memory...


/* Initialize the internal data when the first call is made.  */
static void getopt_initialize(struct getopt_data *d)
{
        /*
         * Start processing options with ARGV-element 1 (since ARGV-element 0
         * is the program name); the sequence of previously skipped non-option
         * ARGV-elements is empty.
         */
        d->first_nonopt = d->last_nonopt = d->optind;
        d->nextchar = NULL;
        d->initialized = 1;
}

this initialization function resets d->first_nonopt and d->last_nonopt

static int getopt_internal_r(int argc, char *const *argv, const char *optstring,
                const struct option *longopts, int *longind,
                struct getopt_data *d)
{
        int ret, print_errors = d->opterr;

        if (optstring[0] == ':')
                print_errors = 0;
        if (argc < 1)
                return -1;
        d->optarg = NULL;
        if (d->optind == 0 || !d->initialized) {
                if (d->optind == 0)
                        d->optind = 1;       /* Don't scan ARGV[0], the program 
name.  */
                getopt_initialize(d);
        }
        if (d->nextchar == NULL || *d->nextchar == '\0') {
                ret = shuffle_argv(argc, argv, longopts, d);
                if (ret)
                        return ret;
        }

this is the point where the struct is initialized, i.e., only when d->optind == 0 (or !d->initialized)


static int getopt_internal(int argc, char *const *argv, const char *optstring,
        const struct option *longopts, int *longind)
{
        int result;
        /* Keep a global copy of all internal members of d */
        static struct getopt_data d;

        d.optind = optind;
        d.opterr = opterr;
        result = getopt_internal_r(argc, argv, optstring, longopts,
                longind, &d);
        optind = d.optind;
        optarg = d.optarg;
        optopt = d.optopt;
        return result;
}

the struct that is passed to getopt_internal_r is static, so its fields are initialized to 0 only once: the first time getopt_internal_r is called the struct is correctly initialized, the other times it is not (unless you set optind to 0, which is not standard)

what do you think?

I'd really like to hear from glibc people to know whether this is the intended behavior (which is not documented anyway)

I hope to hear from you soon

cheers
        Lorenzo

--
+-----------------------------------------------------+
|  Lorenzo Bettini          ICQ# lbetto, 16080134     |
|  PhD in Computer Science                            |
|  Dip. Sistemi e Informatica, Univ. di Firenze       |
|  Florence - Italy        (GNU/Linux User # 158233)  |
|  Home Page        : http://www.lorenzobettini.it    |
|  http://music.dsi.unifi.it         XKlaim language  |
|  http://www.purplesucker.com Deep Purple Cover Band |
|  http://www.gnu.org/software/src-highlite           |
|  http://www.gnu.org/software/gengetopt              |
|  http://www.lorenzobettini.it/software/gengen       |
|  http://www.lorenzobettini.it/software/doublecpp    |
+-----------------------------------------------------+




reply via email to

[Prev in Thread] Current Thread [Next in Thread]