Preparing for release of 2.6.6pre1
[rsync.git] / popt / popt.c
1 /** \ingroup popt
2  * \file popt/popt.c
3  */
4
5 /* (C) 19982000 Red Hat, Inc. -- Licensing details are in the COPYING
6    file accompanying popt source distributions, available from
7    ftp://ftp.rpm.org/pub/rpm/dist */
8
9 #undef  MYDEBUG
10
11 #include "system.h"
12
13 #if HAVE_FLOAT_H
14 #include <float.h>
15 #endif
16 #include <math.h>
17
18 #include "findme.h"
19 #include "poptint.h"
20
21 #ifndef HAVE_STRERROR
22 static char * strerror(int errno) {
23     extern int sys_nerr;
24     extern char * sys_errlist[];
25
26     if ((0 <= errno) && (errno < sys_nerr))
27         return sys_errlist[errno];
28     else
29         return POPT_("unknown errno");
30 }
31 #endif
32
33 #ifdef MYDEBUG
34 /*@unused@*/ static void prtcon(const char *msg, poptContext con)
35 {
36     if (msg) fprintf(stderr, "%s", msg);
37     fprintf(stderr, "\tcon %p os %p nextCharArg \"%s\" nextArg \"%s\" argv[%d] \"%s\"\n",
38         con, con->os,
39         (con->os->nextCharArg ? con->os->nextCharArg : ""),
40         (con->os->nextArg ? con->os->nextArg : ""),
41         con->os->next,
42         (con->os->argv && con->os->argv[con->os->next]
43                 ? con->os->argv[con->os->next] : ""));
44 }
45 #endif
46
47 void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
48 {
49     con->execPath = _free(con->execPath);
50     con->execPath = xstrdup(path);
51     con->execAbsolute = allowAbsolute;
52     /*@-nullstate@*/ /* LCL: con->execPath can be NULL? */
53     return;
54     /*@=nullstate@*/
55 }
56
57 static void invokeCallbacksPRE(poptContext con, const struct poptOption * opt)
58         /*@globals internalState@*/
59         /*@modifies internalState@*/
60 {
61     if (opt != NULL)
62     for (; opt->longName || opt->shortName || opt->arg; opt++) {
63         if (opt->arg == NULL) continue;         /* XXX program error. */
64         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
65             /* Recurse on included sub-tables. */
66             invokeCallbacksPRE(con, opt->arg);
67         } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
68                    (opt->argInfo & POPT_CBFLAG_PRE))
69         {   /*@-castfcnptr@*/
70             poptCallbackType cb = (poptCallbackType)opt->arg;
71             /*@=castfcnptr@*/
72             /* Perform callback. */
73             /*@-moduncon -noeffectuncon @*/
74             cb(con, POPT_CALLBACK_REASON_PRE, NULL, NULL, opt->descrip);
75             /*@=moduncon =noeffectuncon @*/
76         }
77     }
78 }
79
80 static void invokeCallbacksPOST(poptContext con, const struct poptOption * opt)
81         /*@globals internalState@*/
82         /*@modifies internalState@*/
83 {
84     if (opt != NULL)
85     for (; opt->longName || opt->shortName || opt->arg; opt++) {
86         if (opt->arg == NULL) continue;         /* XXX program error. */
87         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
88             /* Recurse on included sub-tables. */
89             invokeCallbacksPOST(con, opt->arg);
90         } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
91                    (opt->argInfo & POPT_CBFLAG_POST))
92         {   /*@-castfcnptr@*/
93             poptCallbackType cb = (poptCallbackType)opt->arg;
94             /*@=castfcnptr@*/
95             /* Perform callback. */
96             /*@-moduncon -noeffectuncon @*/
97             cb(con, POPT_CALLBACK_REASON_POST, NULL, NULL, opt->descrip);
98             /*@=moduncon =noeffectuncon @*/
99         }
100     }
101 }
102
103 static void invokeCallbacksOPTION(poptContext con,
104                                   const struct poptOption * opt,
105                                   const struct poptOption * myOpt,
106                                   /*@null@*/ const void * myData, int shorty)
107         /*@globals internalState@*/
108         /*@modifies internalState@*/
109 {
110     const struct poptOption * cbopt = NULL;
111
112     if (opt != NULL)
113     for (; opt->longName || opt->shortName || opt->arg; opt++) {
114         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
115             /* Recurse on included sub-tables. */
116             if (opt->arg != NULL)       /* XXX program error */
117                 invokeCallbacksOPTION(con, opt->arg, myOpt, myData, shorty);
118         } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
119                   !(opt->argInfo & POPT_CBFLAG_SKIPOPTION)) {
120             /* Save callback info. */
121             cbopt = opt;
122         } else if (cbopt != NULL &&
123                    ((myOpt->shortName && opt->shortName && shorty &&
124                         myOpt->shortName == opt->shortName) ||
125                     (myOpt->longName && opt->longName &&
126                 /*@-nullpass@*/         /* LCL: opt->longName != NULL */
127                         !strcmp(myOpt->longName, opt->longName)))
128                 /*@=nullpass@*/
129                    )
130         {   /*@-castfcnptr@*/
131             poptCallbackType cb = (poptCallbackType)cbopt->arg;
132             /*@=castfcnptr@*/
133             const void * cbData = (cbopt->descrip ? cbopt->descrip : myData);
134             /* Perform callback. */
135             if (cb != NULL) {   /* XXX program error */
136                 /*@-moduncon -noeffectuncon @*/
137                 cb(con, POPT_CALLBACK_REASON_OPTION, myOpt,
138                         con->os->nextArg, cbData);
139                 /*@=moduncon =noeffectuncon @*/
140             }
141             /* Terminate (unless explcitly continuing). */
142             if (!(cbopt->argInfo & POPT_CBFLAG_CONTINUE))
143                 return;
144         }
145     }
146 }
147
148 poptContext poptGetContext(const char * name, int argc, const char ** argv,
149                            const struct poptOption * options, int flags)
150 {
151     poptContext con = malloc(sizeof(*con));
152
153     if (con == NULL) return NULL;       /* XXX can't happen */
154     memset(con, 0, sizeof(*con));
155
156     con->os = con->optionStack;
157     con->os->argc = argc;
158     /*@-dependenttrans -assignexpose@*/ /* FIX: W2DO? */
159     con->os->argv = argv;
160     /*@=dependenttrans =assignexpose@*/
161     con->os->argb = NULL;
162
163     if (!(flags & POPT_CONTEXT_KEEP_FIRST))
164         con->os->next = 1;                      /* skip argv[0] */
165
166     con->leftovers = calloc( (argc + 1), sizeof(*con->leftovers) );
167     /*@-dependenttrans -assignexpose@*/ /* FIX: W2DO? */
168     con->options = options;
169     /*@=dependenttrans =assignexpose@*/
170     con->aliases = NULL;
171     con->numAliases = 0;
172     con->flags = flags;
173     con->execs = NULL;
174     con->numExecs = 0;
175     con->finalArgvAlloced = argc * 2;
176     con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
177     con->execAbsolute = 1;
178     con->arg_strip = NULL;
179
180     if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
181         con->flags |= POPT_CONTEXT_POSIXMEHARDER;
182
183     if (name) {
184         char * t = malloc(strlen(name) + 1);
185         if (t) con->appName = strcpy(t, name);
186     }
187
188     /*@-internalglobs@*/
189     invokeCallbacksPRE(con, con->options);
190     /*@=internalglobs@*/
191
192     return con;
193 }
194
195 static void cleanOSE(/*@special@*/ struct optionStackEntry *os)
196         /*@uses os @*/
197         /*@releases os->nextArg, os->argv, os->argb @*/
198         /*@modifies os @*/
199 {
200     os->nextArg = _free(os->nextArg);
201     os->argv = _free(os->argv);
202     os->argb = PBM_FREE(os->argb);
203 }
204
205 void poptResetContext(poptContext con)
206 {
207     int i;
208
209     if (con == NULL) return;
210     while (con->os > con->optionStack) {
211         cleanOSE(con->os--);
212     }
213     con->os->argb = PBM_FREE(con->os->argb);
214     con->os->currAlias = NULL;
215     con->os->nextCharArg = NULL;
216     con->os->nextArg = NULL;
217     con->os->next = 1;                  /* skip argv[0] */
218
219     con->numLeftovers = 0;
220     con->nextLeftover = 0;
221     con->restLeftover = 0;
222     con->doExec = NULL;
223
224     if (con->finalArgv != NULL)
225     for (i = 0; i < con->finalArgvCount; i++)
226         /*@-unqualifiedtrans@*/         /* FIX: typedef double indirection. */
227         con->finalArgv[i] = _free(con->finalArgv[i]);
228         /*@=unqualifiedtrans@*/
229
230     con->finalArgvCount = 0;
231     con->arg_strip = PBM_FREE(con->arg_strip);
232     /*@-nullstate@*/    /* FIX: con->finalArgv != NULL */
233     return;
234     /*@=nullstate@*/
235 }
236
237 /* Only one of longName, shortName should be set, not both. */
238 static int handleExec(/*@special@*/ poptContext con,
239                 /*@null@*/ const char * longName, char shortName)
240         /*@uses con->execs, con->numExecs, con->flags, con->doExec,
241                 con->finalArgv, con->finalArgvAlloced, con->finalArgvCount @*/
242         /*@modifies con @*/
243 {
244     poptItem item;
245     int i;
246
247     if (con->execs == NULL || con->numExecs <= 0) /* XXX can't happen */
248         return 0;
249
250     for (i = con->numExecs - 1; i >= 0; i--) {
251         item = con->execs + i;
252         if (longName && !(item->option.longName &&
253                         !strcmp(longName, item->option.longName)))
254             continue;
255         else if (shortName != item->option.shortName)
256             continue;
257         break;
258     }
259     if (i < 0) return 0;
260
261
262     if (con->flags & POPT_CONTEXT_NO_EXEC)
263         return 1;
264
265     if (con->doExec == NULL) {
266         con->doExec = con->execs + i;
267         return 1;
268     }
269
270     /* We already have an exec to do; remember this option for next
271        time 'round */
272     if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
273         con->finalArgvAlloced += 10;
274         con->finalArgv = realloc(con->finalArgv,
275                         sizeof(*con->finalArgv) * con->finalArgvAlloced);
276     }
277
278     i = con->finalArgvCount++;
279     if (con->finalArgv != NULL) /* XXX can't happen */
280     {   char *s  = malloc((longName ? strlen(longName) : 0) + 3);
281         if (s != NULL) {        /* XXX can't happen */
282             if (longName)
283                 sprintf(s, "--%s", longName);
284             else
285                 sprintf(s, "-%c", shortName);
286             con->finalArgv[i] = s;
287         } else
288             con->finalArgv[i] = NULL;
289     }
290
291     /*@-nullstate@*/    /* FIX: con->finalArgv[] == NULL */
292     return 1;
293     /*@=nullstate@*/
294 }
295
296 /* Only one of longName, shortName may be set at a time */
297 static int handleAlias(/*@special@*/ poptContext con,
298                 /*@null@*/ const char * longName, char shortName,
299                 /*@keep@*/ /*@null@*/ const char * nextCharArg)
300         /*@uses con->aliases, con->numAliases, con->optionStack, con->os,
301                 con->os->currAlias, con->os->currAlias->option.longName @*/
302         /*@modifies con @*/
303 {
304     poptItem item = con->os->currAlias;
305     int rc;
306     int i;
307
308     if (item) {
309         if (longName && (item->option.longName &&
310                 !strcmp(longName, item->option.longName)))
311             return 0;
312         if (shortName && shortName == item->option.shortName)
313             return 0;
314     }
315
316     if (con->aliases == NULL || con->numAliases <= 0) /* XXX can't happen */
317         return 0;
318
319     for (i = con->numAliases - 1; i >= 0; i--) {
320         item = con->aliases + i;
321         if (longName && !(item->option.longName &&
322                         !strcmp(longName, item->option.longName)))
323             continue;
324         else if (shortName != item->option.shortName)
325             continue;
326         break;
327     }
328     if (i < 0) return 0;
329
330     if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
331         return POPT_ERROR_OPTSTOODEEP;
332
333     if (nextCharArg && *nextCharArg)
334         con->os->nextCharArg = nextCharArg;
335
336     con->os++;
337     con->os->next = 0;
338     con->os->stuffed = 0;
339     con->os->nextArg = NULL;
340     con->os->nextCharArg = NULL;
341     con->os->currAlias = con->aliases + i;
342     rc = poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
343                 &con->os->argc, &con->os->argv);
344     con->os->argb = NULL;
345
346     return (rc ? rc : 1);
347 }
348
349 static int execCommand(poptContext con)
350     /*@*/
351 {
352     poptItem item = con->doExec;
353     const char ** argv;
354     int argc = 0;
355     int rc;
356
357     if (item == NULL) /*XXX can't happen*/
358         return POPT_ERROR_NOARG;
359
360     if (item->argv == NULL || item->argc < 1 ||
361         (!con->execAbsolute && strchr(item->argv[0], '/')))
362             return POPT_ERROR_NOARG;
363
364     argv = malloc(sizeof(*argv) *
365                         (6 + item->argc + con->numLeftovers + con->finalArgvCount));
366     if (argv == NULL) return POPT_ERROR_MALLOC; /* XXX can't happen */
367
368     if (!strchr(item->argv[0], '/') && con->execPath) {
369         char *s = alloca(strlen(con->execPath) + strlen(item->argv[0]) + sizeof("/"));
370         sprintf(s, "%s/%s", con->execPath, item->argv[0]);
371         argv[argc] = s;
372     } else {
373         argv[argc] = findProgramPath(item->argv[0]);
374     }
375     if (argv[argc++] == NULL) return POPT_ERROR_NOARG;
376
377     if (item->argc > 1) {
378         memcpy(argv + argc, item->argv + 1, sizeof(*argv) * (item->argc - 1));
379         argc += (item->argc - 1);
380     }
381
382     if (con->finalArgv != NULL && con->finalArgvCount > 0) {
383         memcpy(argv + argc, con->finalArgv,
384                 sizeof(*argv) * con->finalArgvCount);
385         argc += con->finalArgvCount;
386     }
387
388     if (con->leftovers != NULL && con->numLeftovers > 0) {
389 #if 0
390         argv[argc++] = "--";
391 #endif
392         memcpy(argv + argc, con->leftovers, sizeof(*argv) * con->numLeftovers);
393         argc += con->numLeftovers;
394     }
395
396     argv[argc] = NULL;
397
398 #ifdef __hpux
399     rc = setresuid(getuid(), getuid(),-1);
400     if (rc) return POPT_ERROR_ERRNO;
401 #else
402 /*
403  * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
404  * XXX  sez' Timur Bakeyev <mc@bat.ru>
405  * XXX  from Norbert Warmuth <nwarmuth@privat.circular.de>
406  */
407 #if defined(HAVE_SETUID)
408     rc = setuid(getuid());
409     if (rc) return POPT_ERROR_ERRNO;
410 #elif defined (HAVE_SETREUID)
411     rc = setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
412     if (rc) return POPT_ERROR_ERRNO;
413 #else
414     ; /* Can't drop privileges */
415 #endif
416 #endif
417
418     if (argv[0] == NULL)
419         return POPT_ERROR_NOARG;
420 #ifdef MYDEBUG
421     {   const char ** avp;
422         fprintf(stderr, "==> execvp(%s) argv[%d]:", argv[0], argc);
423         for (avp = argv; *avp; avp++)
424             fprintf(stderr, " '%s'", *avp);
425         fprintf(stderr, "\n");
426     }
427 #endif
428
429     rc = execvp(argv[0], (char *const *)argv);
430     return POPT_ERROR_ERRNO;
431 }
432
433 /*@observer@*/ /*@null@*/ static const struct poptOption *
434 findOption(const struct poptOption * opt, /*@null@*/ const char * longName,
435                 char shortName,
436                 /*@null@*/ /*@out@*/ poptCallbackType * callback,
437                 /*@null@*/ /*@out@*/ const void ** callbackData,
438                 int singleDash)
439         /*@modifies *callback, *callbackData */
440 {
441     const struct poptOption * cb = NULL;
442
443     /* This happens when a single - is given */
444     if (singleDash && !shortName && (longName && *longName == '\0'))
445         shortName = '-';
446
447     for (; opt->longName || opt->shortName || opt->arg; opt++) {
448
449         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
450             const struct poptOption * opt2;
451
452             /* Recurse on included sub-tables. */
453             if (opt->arg == NULL) continue;     /* XXX program error */
454             opt2 = findOption(opt->arg, longName, shortName, callback,
455                               callbackData, singleDash);
456             if (opt2 == NULL) continue;
457             /* Sub-table data will be inheirited if no data yet. */
458             if (!(callback && *callback)) return opt2;
459             if (!(callbackData && *callbackData == NULL)) return opt2;
460             /*@-observertrans -dependenttrans @*/
461             *callbackData = opt->descrip;
462             /*@=observertrans =dependenttrans @*/
463             return opt2;
464         } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
465             cb = opt;
466         } else if (longName && opt->longName &&
467                    (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
468                 /*@-nullpass@*/         /* LCL: opt->longName != NULL */
469                    !strcmp(longName, opt->longName))
470                 /*@=nullpass@*/
471         {
472             break;
473         } else if (shortName && shortName == opt->shortName) {
474             break;
475         }
476     }
477
478     if (!opt->longName && !opt->shortName)
479         return NULL;
480     /*@-modobserver -mods @*/
481     if (callback) *callback = NULL;
482     if (callbackData) *callbackData = NULL;
483     if (cb) {
484         if (callback)
485         /*@-castfcnptr@*/
486             *callback = (poptCallbackType)cb->arg;
487         /*@=castfcnptr@*/
488         if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) {
489             if (callbackData)
490                 /*@-observertrans@*/    /* FIX: typedef double indirection. */
491                 *callbackData = cb->descrip;
492                 /*@=observertrans@*/
493         }
494     }
495     /*@=modobserver =mods @*/
496
497     return opt;
498 }
499
500 static const char * findNextArg(/*@special@*/ poptContext con,
501                 unsigned argx, int delete_arg)
502         /*@uses con->optionStack, con->os,
503                 con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
504         /*@modifies con @*/
505 {
506     struct optionStackEntry * os = con->os;
507     const char * arg;
508
509     do {
510         int i;
511         arg = NULL;
512         while (os->next == os->argc && os > con->optionStack) os--;
513         if (os->next == os->argc && os == con->optionStack) break;
514         if (os->argv != NULL)
515         for (i = os->next; i < os->argc; i++) {
516             /*@-sizeoftype@*/
517             if (os->argb && PBM_ISSET(i, os->argb))
518                 /*@innercontinue@*/ continue;
519             if (*os->argv[i] == '-')
520                 /*@innercontinue@*/ continue;
521             if (--argx > 0)
522                 /*@innercontinue@*/ continue;
523             arg = os->argv[i];
524             if (delete_arg) {
525                 if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
526                 if (os->argb != NULL)   /* XXX can't happen */
527                 PBM_SET(i, os->argb);
528             }
529             /*@innerbreak@*/ break;
530             /*@=sizeoftype@*/
531         }
532         if (os > con->optionStack) os--;
533     } while (arg == NULL);
534     return arg;
535 }
536
537 static /*@only@*/ /*@null@*/ const char *
538 expandNextArg(/*@special@*/ poptContext con, const char * s)
539         /*@uses con->optionStack, con->os,
540                 con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
541         /*@modifies con @*/
542 {
543     const char * a = NULL;
544     size_t alen;
545     char *t, *te;
546     size_t tn = strlen(s) + 1;
547     char c;
548
549     te = t = malloc(tn);;
550     if (t == NULL) return NULL;         /* XXX can't happen */
551     while ((c = *s++) != '\0') {
552         switch (c) {
553 #if 0   /* XXX can't do this */
554         case '\\':      /* escape */
555             c = *s++;
556             /*@switchbreak@*/ break;
557 #endif
558         case '!':
559             if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
560                 /*@switchbreak@*/ break;
561             /* XXX Make sure that findNextArg deletes only next arg. */
562             if (a == NULL) {
563                 if ((a = findNextArg(con, 1, 1)) == NULL)
564                     /*@switchbreak@*/ break;
565             }
566             s += 3;
567
568             alen = strlen(a);
569             tn += alen;
570             *te = '\0';
571             t = realloc(t, tn);
572             te = t + strlen(t);
573             strncpy(te, a, alen); te += alen;
574             continue;
575             /*@notreached@*/ /*@switchbreak@*/ break;
576         default:
577             /*@switchbreak@*/ break;
578         }
579         *te++ = c;
580     }
581     *te = '\0';
582     t = realloc(t, strlen(t) + 1);      /* XXX memory leak, hard to plug */
583     return t;
584 }
585
586 static void poptStripArg(/*@special@*/ poptContext con, int which)
587         /*@uses con->arg_strip, con->optionStack @*/
588         /*@defines con->arg_strip @*/
589         /*@modifies con @*/
590 {
591     /*@-sizeoftype@*/
592     if (con->arg_strip == NULL)
593         con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
594     if (con->arg_strip != NULL)         /* XXX can't happen */
595     PBM_SET(which, con->arg_strip);
596     /*@=sizeoftype@*/
597     /*@-compdef@*/ /* LCL: con->arg_strip udefined? */
598     return;
599     /*@=compdef@*/
600 }
601
602 static int poptSaveLong(const struct poptOption * opt, long aLong)
603         /*@modifies opt->arg @*/
604 {
605     if (opt->arg == NULL)
606         return POPT_ERROR_NULLARG;
607
608     if (opt->argInfo & POPT_ARGFLAG_NOT)
609         aLong = ~aLong;
610     switch (opt->argInfo & POPT_ARGFLAG_LOGICALOPS) {
611     case 0:
612         *((long *) opt->arg) = aLong;
613         break;
614     case POPT_ARGFLAG_OR:
615         *((long *) opt->arg) |= aLong;
616         break;
617     case POPT_ARGFLAG_AND:
618         *((long *) opt->arg) &= aLong;
619         break;
620     case POPT_ARGFLAG_XOR:
621         *((long *) opt->arg) ^= aLong;
622         break;
623     default:
624         return POPT_ERROR_BADOPERATION;
625         /*@notreached@*/ break;
626     }
627     return 0;
628 }
629
630 static int poptSaveInt(const struct poptOption * opt, long aLong)
631         /*@modifies opt->arg @*/
632 {
633     if (opt->arg == NULL)
634         return POPT_ERROR_NULLARG;
635
636     if (opt->argInfo & POPT_ARGFLAG_NOT)
637         aLong = ~aLong;
638     switch (opt->argInfo & POPT_ARGFLAG_LOGICALOPS) {
639     case 0:
640         *((int *) opt->arg) = aLong;
641         break;
642     case POPT_ARGFLAG_OR:
643         *((int *) opt->arg) |= aLong;
644         break;
645     case POPT_ARGFLAG_AND:
646         *((int *) opt->arg) &= aLong;
647         break;
648     case POPT_ARGFLAG_XOR:
649         *((int *) opt->arg) ^= aLong;
650         break;
651     default:
652         return POPT_ERROR_BADOPERATION;
653         /*@notreached@*/ break;
654     }
655     return 0;
656 }
657
658 /* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
659 int poptGetNextOpt(poptContext con)
660 {
661     const struct poptOption * opt = NULL;
662     int done = 0;
663
664     if (con == NULL)
665         return -1;
666     while (!done) {
667         const char * origOptString = NULL;
668         poptCallbackType cb = NULL;
669         const void * cbData = NULL;
670         const char * longArg = NULL;
671         int canstrip = 0;
672         int shorty = 0;
673
674         while (!con->os->nextCharArg && con->os->next == con->os->argc
675                 && con->os > con->optionStack) {
676             cleanOSE(con->os--);
677         }
678         if (!con->os->nextCharArg && con->os->next == con->os->argc) {
679             /*@-internalglobs@*/
680             invokeCallbacksPOST(con, con->options);
681             /*@=internalglobs@*/
682             if (con->doExec) return execCommand(con);
683             return -1;
684         }
685
686         /* Process next long option */
687         if (!con->os->nextCharArg) {
688             char * localOptString, * optString;
689             int thisopt;
690
691             /*@-sizeoftype@*/
692             if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
693                 con->os->next++;
694                 continue;
695             }
696             /*@=sizeoftype@*/
697             thisopt = con->os->next;
698             if (con->os->argv != NULL)  /* XXX can't happen */
699             origOptString = con->os->argv[con->os->next++];
700
701             if (origOptString == NULL)  /* XXX can't happen */
702                 return POPT_ERROR_BADOPT;
703
704             if (con->restLeftover || *origOptString != '-') {
705                 if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
706                     con->restLeftover = 1;
707                 if (con->flags & POPT_CONTEXT_ARG_OPTS) {
708                     con->os->nextArg = xstrdup(origOptString);
709                     return 0;
710                 }
711                 if (con->leftovers != NULL)     /* XXX can't happen */
712                     con->leftovers[con->numLeftovers++] = origOptString;
713                 continue;
714             }
715
716             /* Make a copy we can hack at */
717             localOptString = optString =
718                 strcpy(alloca(strlen(origOptString) + 1), origOptString);
719
720             if (optString[0] == '\0')
721                 return POPT_ERROR_BADOPT;
722
723             if (optString[1] == '-' && !optString[2]) {
724                 con->restLeftover = 1;
725                 continue;
726             } else {
727                 char *oe;
728                 int singleDash;
729
730                 optString++;
731                 if (*optString == '-')
732                     singleDash = 0, optString++;
733                 else
734                     singleDash = 1;
735
736                 /* XXX aliases with arg substitution need "--alias=arg" */
737                 if (handleAlias(con, optString, '\0', NULL))
738                     continue;
739
740                 if (handleExec(con, optString, '\0'))
741                     continue;
742
743                 /* Check for "--long=arg" option. */
744                 for (oe = optString; *oe && *oe != '='; oe++)
745                     {};
746                 if (*oe == '=') {
747                     *oe++ = '\0';
748                     /* XXX longArg is mapped back to persistent storage. */
749                     longArg = origOptString + (oe - localOptString);
750                 }
751
752                 opt = findOption(con->options, optString, '\0', &cb, &cbData,
753                                  singleDash);
754                 if (!opt && !singleDash)
755                     return POPT_ERROR_BADOPT;
756             }
757
758             if (!opt) {
759                 con->os->nextCharArg = origOptString + 1;
760             } else {
761                 if (con->os == con->optionStack &&
762                    opt->argInfo & POPT_ARGFLAG_STRIP)
763                 {
764                     canstrip = 1;
765                     poptStripArg(con, thisopt);
766                 }
767                 shorty = 0;
768             }
769         }
770
771         /* Process next short option */
772         /*@-branchstate@*/              /* FIX: W2DO? */
773         if (con->os->nextCharArg) {
774             origOptString = con->os->nextCharArg;
775
776             con->os->nextCharArg = NULL;
777
778             if (handleAlias(con, NULL, *origOptString, origOptString + 1))
779                 continue;
780
781             if (handleExec(con, NULL, *origOptString)) {
782                 /* Restore rest of short options for further processing */
783                 origOptString++;
784                 if (*origOptString != '\0')
785                     con->os->nextCharArg = origOptString;
786                 continue;
787             }
788
789             opt = findOption(con->options, NULL, *origOptString, &cb,
790                              &cbData, 0);
791             if (!opt)
792                 return POPT_ERROR_BADOPT;
793             shorty = 1;
794
795             origOptString++;
796             if (*origOptString != '\0')
797                 con->os->nextCharArg = origOptString;
798         }
799         /*@=branchstate@*/
800
801         if (opt == NULL) return POPT_ERROR_BADOPT;      /* XXX can't happen */
802         if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
803             if (poptSaveInt(opt, 1L))
804                 return POPT_ERROR_BADOPERATION;
805         } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
806             if (opt->arg) {
807                 if (poptSaveInt(opt, (long)opt->val))
808                     return POPT_ERROR_BADOPERATION;
809             }
810         } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
811             con->os->nextArg = _free(con->os->nextArg);
812             /*@-usedef@*/       /* FIX: W2DO? */
813             if (longArg) {
814             /*@=usedef@*/
815                 longArg = expandNextArg(con, longArg);
816                 con->os->nextArg = longArg;
817             } else if (con->os->nextCharArg) {
818                 longArg = expandNextArg(con, con->os->nextCharArg);
819                 con->os->nextArg = longArg;
820                 con->os->nextCharArg = NULL;
821             } else {
822                 while (con->os->next == con->os->argc &&
823                        con->os > con->optionStack) {
824                     cleanOSE(con->os--);
825                 }
826                 if (con->os->next == con->os->argc) {
827                     if (!(opt->argInfo & POPT_ARGFLAG_OPTIONAL))
828                         /*@-compdef@*/  /* FIX: con->os->argv not defined */
829                         return POPT_ERROR_NOARG;
830                         /*@=compdef@*/
831                     con->os->nextArg = NULL;
832                 } else {
833
834                     /*
835                      * Make sure this isn't part of a short arg or the
836                      * result of an alias expansion.
837                      */
838                     if (con->os == con->optionStack &&
839                         (opt->argInfo & POPT_ARGFLAG_STRIP) &&
840                         canstrip) {
841                         poptStripArg(con, con->os->next);
842                     }
843                 
844                     if (con->os->argv != NULL) {        /* XXX can't happen */
845                         /* XXX watchout: subtle side-effects live here. */
846                         longArg = con->os->argv[con->os->next++];
847                         longArg = expandNextArg(con, longArg);
848                         con->os->nextArg = longArg;
849                     }
850                 }
851             }
852             longArg = NULL;
853
854             if (opt->arg) {
855                 switch (opt->argInfo & POPT_ARG_MASK) {
856                 case POPT_ARG_STRING:
857                     /* XXX memory leak, hard to plug */
858                     *((const char **) opt->arg) = (con->os->nextArg)
859                         ? xstrdup(con->os->nextArg) : NULL;
860                     /*@switchbreak@*/ break;
861
862                 case POPT_ARG_INT:
863                 case POPT_ARG_LONG:
864                 {   long aLong = 0;
865                     char *end;
866
867                     if (con->os->nextArg) {
868                         aLong = strtol(con->os->nextArg, &end, 0);
869                         if (!(end && *end == '\0'))
870                             return POPT_ERROR_BADNUMBER;
871                     }
872
873                     if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
874                         if (aLong == LONG_MIN || aLong == LONG_MAX)
875                             return POPT_ERROR_OVERFLOW;
876                         if (poptSaveLong(opt, aLong))
877                             return POPT_ERROR_BADOPERATION;
878                     } else {
879                         if (aLong > INT_MAX || aLong < INT_MIN)
880                             return POPT_ERROR_OVERFLOW;
881                         if (poptSaveInt(opt, aLong))
882                             return POPT_ERROR_BADOPERATION;
883                     }
884                 }   /*@switchbreak@*/ break;
885
886                 case POPT_ARG_FLOAT:
887                 case POPT_ARG_DOUBLE:
888                 {   double aDouble = 0.0;
889                     char *end;
890
891                     if (con->os->nextArg) {
892                         /*@-mods@*/
893                         int saveerrno = errno;
894                         errno = 0;
895                         aDouble = strtod(con->os->nextArg, &end);
896                         if (errno == ERANGE)
897                             return POPT_ERROR_OVERFLOW;
898                         errno = saveerrno;
899                         /*@=mods@*/
900                         if (*end != '\0')
901                             return POPT_ERROR_BADNUMBER;
902                     }
903
904                     if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_DOUBLE) {
905                         *((double *) opt->arg) = aDouble;
906                     } else {
907 #ifndef DBL_EPSILON
908 #define DBL_EPSILON 2.2204460492503131e-16
909 #endif
910 #define MY_ABS(a) ((((a) - 0.0) < DBL_EPSILON) ? -(a) : (a))
911                         if ((MY_ABS(aDouble) - FLT_MAX) > DBL_EPSILON)
912                             return POPT_ERROR_OVERFLOW;
913                         if ((FLT_MIN - MY_ABS(aDouble)) > DBL_EPSILON)
914                             return POPT_ERROR_OVERFLOW;
915                         *((float *) opt->arg) = aDouble;
916                     }
917                 }   /*@switchbreak@*/ break;
918                 default:
919                     fprintf(stdout,
920                         POPT_("option type (%d) not implemented in popt\n"),
921                         (opt->argInfo & POPT_ARG_MASK));
922                     exit(EXIT_FAILURE);
923                     /*@notreached@*/ /*@switchbreak@*/ break;
924                 }
925             }
926         }
927
928         if (cb) {
929             /*@-internalglobs@*/
930             invokeCallbacksOPTION(con, con->options, opt, cbData, shorty);
931             /*@=internalglobs@*/
932         } else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
933             done = 1;
934
935         if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
936             con->finalArgvAlloced += 10;
937             con->finalArgv = realloc(con->finalArgv,
938                             sizeof(*con->finalArgv) * con->finalArgvAlloced);
939         }
940
941         if (con->finalArgv != NULL)
942         {   char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
943             if (s != NULL) {    /* XXX can't happen */
944                 if (opt->longName)
945                     sprintf(s, "%s%s",
946                         ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
947                         opt->longName);
948                 else
949                     sprintf(s, "-%c", opt->shortName);
950                 con->finalArgv[con->finalArgvCount++] = s;
951             } else
952                 con->finalArgv[con->finalArgvCount++] = NULL;
953         }
954
955         if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE)
956             /*@-ifempty@*/ ; /*@=ifempty@*/
957         else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL)
958             /*@-ifempty@*/ ; /*@=ifempty@*/
959         else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
960             if (con->finalArgv != NULL && con->os->nextArg)
961                 con->finalArgv[con->finalArgvCount++] =
962                         /*@-nullpass@*/ /* LCL: con->os->nextArg != NULL */
963                         xstrdup(con->os->nextArg);
964                         /*@=nullpass@*/
965         }
966     }
967
968     return (opt ? opt->val : -1);       /* XXX can't happen */
969 }
970
971 const char * poptGetOptArg(poptContext con)
972 {
973     const char * ret = NULL;
974     /*@-branchstate@*/
975     if (con) {
976         ret = con->os->nextArg;
977         con->os->nextArg = NULL;
978     }
979     /*@=branchstate@*/
980     return ret;
981 }
982
983 const char * poptGetArg(poptContext con)
984 {
985     const char * ret = NULL;
986     if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
987         ret = con->leftovers[con->nextLeftover++];
988     return ret;
989 }
990
991 const char * poptPeekArg(poptContext con)
992 {
993     const char * ret = NULL;
994     if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
995         ret = con->leftovers[con->nextLeftover];
996     return ret;
997 }
998
999 const char ** poptGetArgs(poptContext con)
1000 {
1001     if (con == NULL ||
1002         con->leftovers == NULL || con->numLeftovers == con->nextLeftover)
1003         return NULL;
1004
1005     /* some apps like [like RPM ;-) ] need this NULL terminated */
1006     con->leftovers[con->numLeftovers] = NULL;
1007
1008     /*@-nullret -nullstate @*/  /* FIX: typedef double indirection. */
1009     return (con->leftovers + con->nextLeftover);
1010     /*@=nullret =nullstate @*/
1011 }
1012
1013 poptContext poptFreeContext(poptContext con)
1014 {
1015     poptItem item;
1016     int i;
1017
1018     if (con == NULL) return con;
1019     poptResetContext(con);
1020     con->os->argb = _free(con->os->argb);
1021
1022     if (con->aliases != NULL)
1023     for (i = 0; i < con->numAliases; i++) {
1024         item = con->aliases + i;
1025         /*@-modobserver -observertrans -dependenttrans@*/
1026         item->option.longName = _free(item->option.longName);
1027         item->option.descrip = _free(item->option.descrip);
1028         item->option.argDescrip = _free(item->option.argDescrip);
1029         /*@=modobserver =observertrans =dependenttrans@*/
1030         item->argv = _free(item->argv);
1031     }
1032     con->aliases = _free(con->aliases);
1033
1034     if (con->execs != NULL)
1035     for (i = 0; i < con->numExecs; i++) {
1036         item = con->execs + i;
1037         /*@-modobserver -observertrans -dependenttrans@*/
1038         item->option.longName = _free(item->option.longName);
1039         item->option.descrip = _free(item->option.descrip);
1040         item->option.argDescrip = _free(item->option.argDescrip);
1041         /*@=modobserver =observertrans =dependenttrans@*/
1042         item->argv = _free(item->argv);
1043     }
1044     con->execs = _free(con->execs);
1045
1046     con->leftovers = _free(con->leftovers);
1047     con->finalArgv = _free(con->finalArgv);
1048     con->appName = _free(con->appName);
1049     con->otherHelp = _free(con->otherHelp);
1050     con->execPath = _free(con->execPath);
1051     con->arg_strip = PBM_FREE(con->arg_strip);
1052     
1053     con = _free(con);
1054     return con;
1055 }
1056
1057 int poptAddAlias(poptContext con, struct poptAlias alias,
1058                 /*@unused@*/ UNUSED(int flags))
1059 {
1060     poptItem item = (poptItem) alloca(sizeof(*item));
1061     memset(item, 0, sizeof(*item));
1062     item->option.longName = alias.longName;
1063     item->option.shortName = alias.shortName;
1064     item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
1065     item->option.arg = 0;
1066     item->option.val = 0;
1067     item->option.descrip = NULL;
1068     item->option.argDescrip = NULL;
1069     item->argc = alias.argc;
1070     item->argv = alias.argv;
1071     return poptAddItem(con, item, 0);
1072 }
1073
1074 /*@-mustmod@*/ /* LCL: con not modified? */
1075 int poptAddItem(poptContext con, poptItem newItem, int flags)
1076 {
1077     poptItem * items, item;
1078     int * nitems;
1079
1080     switch (flags) {
1081     case 1:
1082         items = &con->execs;
1083         nitems = &con->numExecs;
1084         break;
1085     case 0:
1086         items = &con->aliases;
1087         nitems = &con->numAliases;
1088         break;
1089     default:
1090         return 1;
1091         /*@notreached@*/ break;
1092     }
1093
1094     *items = realloc((*items), ((*nitems) + 1) * sizeof(**items));
1095     if ((*items) == NULL)
1096         return 1;
1097
1098     item = (*items) + (*nitems);
1099
1100     item->option.longName =
1101         (newItem->option.longName ? xstrdup(newItem->option.longName) : NULL);
1102     item->option.shortName = newItem->option.shortName;
1103     item->option.argInfo = newItem->option.argInfo;
1104     item->option.arg = newItem->option.arg;
1105     item->option.val = newItem->option.val;
1106     item->option.descrip =
1107         (newItem->option.descrip ? xstrdup(newItem->option.descrip) : NULL);
1108     item->option.argDescrip =
1109        (newItem->option.argDescrip ? xstrdup(newItem->option.argDescrip) : NULL);
1110     item->argc = newItem->argc;
1111     item->argv = newItem->argv;
1112
1113     (*nitems)++;
1114
1115     return 0;
1116 }
1117 /*@=mustmod@*/
1118
1119 const char * poptBadOption(poptContext con, int flags)
1120 {
1121     struct optionStackEntry * os = NULL;
1122
1123     if (con != NULL)
1124         os = (flags & POPT_BADOPTION_NOALIAS) ? con->optionStack : con->os;
1125
1126     /*@-nullderef@*/    /* LCL: os->argv != NULL */
1127     return (os && os->argv ? os->argv[os->next - 1] : NULL);
1128     /*@=nullderef@*/
1129 }
1130
1131 const char * poptStrerror(const int error)
1132 {
1133     switch (error) {
1134       case POPT_ERROR_NOARG:
1135         return POPT_("missing argument");
1136       case POPT_ERROR_BADOPT:
1137         return POPT_("unknown option");
1138       case POPT_ERROR_BADOPERATION:
1139         return POPT_("mutually exclusive logical operations requested");
1140       case POPT_ERROR_NULLARG:
1141         return POPT_("opt->arg should not be NULL");
1142       case POPT_ERROR_OPTSTOODEEP:
1143         return POPT_("aliases nested too deeply");
1144       case POPT_ERROR_BADQUOTE:
1145         return POPT_("error in parameter quoting");
1146       case POPT_ERROR_BADNUMBER:
1147         return POPT_("invalid numeric value");
1148       case POPT_ERROR_OVERFLOW:
1149         return POPT_("number too large or too small");
1150       case POPT_ERROR_MALLOC:
1151         return POPT_("memory allocation failed");
1152       case POPT_ERROR_ERRNO:
1153         return strerror(errno);
1154       default:
1155         return POPT_("unknown error");
1156     }
1157 }
1158
1159 int poptStuffArgs(poptContext con, const char ** argv)
1160 {
1161     int argc;
1162     int rc;
1163
1164     if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
1165         return POPT_ERROR_OPTSTOODEEP;
1166
1167     for (argc = 0; argv[argc]; argc++)
1168         {};
1169
1170     con->os++;
1171     con->os->next = 0;
1172     con->os->nextArg = NULL;
1173     con->os->nextCharArg = NULL;
1174     con->os->currAlias = NULL;
1175     rc = poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
1176     con->os->argb = NULL;
1177     con->os->stuffed = 1;
1178
1179     return rc;
1180 }
1181
1182 const char * poptGetInvocationName(poptContext con)
1183 {
1184     return (con->os->argv ? con->os->argv[0] : "");
1185 }
1186
1187 int poptStrippedArgv(poptContext con, int argc, char ** argv)
1188 {
1189     int numargs = argc;
1190     int j = 1;
1191     int i;
1192     
1193     /*@-sizeoftype@*/
1194     if (con->arg_strip)
1195     for (i = 1; i < argc; i++) {
1196         if (PBM_ISSET(i, con->arg_strip))
1197             numargs--;
1198     }
1199     
1200     for (i = 1; i < argc; i++) {
1201         if (con->arg_strip && PBM_ISSET(i, con->arg_strip))
1202             continue;
1203         argv[j] = (j < numargs) ? argv[i] : NULL;
1204         j++;
1205     }
1206     /*@=sizeoftype@*/
1207     
1208     return numargs;
1209 }