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