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