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 #include <float.h>
14 #include <math.h>
15 #include <unistd.h>
16 #include <limits.h>
17 #include <errno.h>
18 
19 #include "poptint.h"
20 
21 #ifdef HAVE_STDALIGN_H
22 #include <stdalign.h>
23 #define ALIGNOF(x) alignof(x)
24 #elif defined __GNUC__
25 #define ALIGNOF(x) __alignof__(x)
26 #else
27 #define ALIGNOF(x) sizeof(x)
28 #endif
29 
30 #ifdef	MYDEBUG
31 int _popt_debug = 0;
32 #endif
33 
34 unsigned int _poptArgMask = POPT_ARG_MASK;
35 unsigned int _poptGroupMask = POPT_GROUP_MASK;
36 
37 #if !defined(HAVE_STRERROR)
strerror(int errno)38 static char * strerror(int errno)
39 {
40     extern int sys_nerr;
41     extern char * sys_errlist[];
42 
43     if ((0 <= errno) && (errno < sys_nerr))
44 	return sys_errlist[errno];
45     else
46 	return POPT_("unknown errno");
47 }
48 #endif
49 
50 #ifdef MYDEBUG
prtcon(const char * msg,poptContext con)51 static void prtcon(const char *msg, poptContext con)
52 {
53     if (msg) fprintf(stderr, "%s", msg);
54     fprintf(stderr, "\tcon %p os %p nextCharArg \"%s\" nextArg \"%s\" argv[%d] \"%s\"\n",
55 	con, con->os,
56 	(con->os->nextCharArg ? con->os->nextCharArg : ""),
57 	(con->os->nextArg ? con->os->nextArg : ""),
58 	con->os->next,
59 	(con->os->argv && con->os->argv[con->os->next]
60 		? con->os->argv[con->os->next] : ""));
61 }
62 #endif
63 
poptSetExecPath(poptContext con,const char * path,int allowAbsolute)64 void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
65 {
66     con->execPath = _free(con->execPath);
67     con->execPath = xstrdup(path);
68     con->execAbsolute = allowAbsolute;
69     return;
70 }
71 
invokeCallbacksPRE(poptContext con,const struct poptOption * opt)72 static void invokeCallbacksPRE(poptContext con, const struct poptOption * opt)
73 {
74     if (opt != NULL)
75     for (; opt->longName || opt->shortName || opt->arg; opt++) {
76 	poptArg arg = { .ptr = opt->arg };
77 	if (arg.ptr)
78 	switch (poptArgType(opt)) {
79 	case POPT_ARG_INCLUDE_TABLE:	/* Recurse on included sub-tables. */
80 	    poptSubstituteHelpI18N(arg.opt);	/* XXX side effects */
81 	    invokeCallbacksPRE(con, arg.opt);
82 	    break;
83 	case POPT_ARG_CALLBACK:		/* Perform callback. */
84 	    if (!CBF_ISSET(opt, PRE))
85 		break;
86 	    arg.cb(con, POPT_CALLBACK_REASON_PRE, NULL, NULL, opt->descrip);
87 	    break;
88 	}
89     }
90 }
91 
invokeCallbacksPOST(poptContext con,const struct poptOption * opt)92 static void invokeCallbacksPOST(poptContext con, const struct poptOption * opt)
93 {
94     if (opt != NULL)
95     for (; opt->longName || opt->shortName || opt->arg; opt++) {
96 	poptArg arg = { .ptr = opt->arg };
97 	if (arg.ptr)
98 	switch (poptArgType(opt)) {
99 	case POPT_ARG_INCLUDE_TABLE:	/* Recurse on included sub-tables. */
100 	    poptSubstituteHelpI18N(arg.opt);	/* XXX side effects */
101 	    invokeCallbacksPOST(con, arg.opt);
102 	    break;
103 	case POPT_ARG_CALLBACK:		/* Perform callback. */
104 	    if (!CBF_ISSET(opt, POST))
105 		break;
106 	    arg.cb(con, POPT_CALLBACK_REASON_POST, NULL, NULL, opt->descrip);
107 	    break;
108 	}
109     }
110 }
111 
invokeCallbacksOPTION(poptContext con,const struct poptOption * opt,const struct poptOption * myOpt,const void * myData,int shorty)112 static void invokeCallbacksOPTION(poptContext con,
113 				const struct poptOption * opt,
114 				const struct poptOption * myOpt,
115 				const void * myData, int shorty)
116 {
117     const struct poptOption * cbopt = NULL;
118     poptArg cbarg = { .ptr = NULL };
119 
120     if (opt != NULL)
121     for (; opt->longName || opt->shortName || opt->arg; opt++) {
122 	poptArg arg = { .ptr = opt->arg };
123 	switch (poptArgType(opt)) {
124 	case POPT_ARG_INCLUDE_TABLE:	/* Recurse on included sub-tables. */
125 	    poptSubstituteHelpI18N(arg.opt);	/* XXX side effects */
126 	    if (opt->arg != NULL)
127 		invokeCallbacksOPTION(con, opt->arg, myOpt, myData, shorty);
128 	    break;
129 	case POPT_ARG_CALLBACK:		/* Save callback info. */
130 	    if (CBF_ISSET(opt, SKIPOPTION))
131 		break;
132 	    cbopt = opt;
133 	    cbarg.ptr = opt->arg;
134 	    break;
135 	default:		/* Perform callback on matching option. */
136 	    if (cbopt == NULL || cbarg.cb == NULL)
137 		break;
138 	    if ((myOpt->shortName && opt->shortName && shorty &&
139 			myOpt->shortName == opt->shortName)
140 	     || (myOpt->longName != NULL && opt->longName != NULL &&
141 			!strcmp(myOpt->longName, opt->longName)))
142 	    {	const void *cbData = (cbopt->descrip ? cbopt->descrip : myData);
143 		cbarg.cb(con, POPT_CALLBACK_REASON_OPTION,
144 			myOpt, con->os->nextArg, cbData);
145 		/* Terminate (unless explcitly continuing). */
146 		if (!CBF_ISSET(cbopt, CONTINUE))
147 		    return;
148 	    }
149 	    break;
150 	}
151     }
152 }
153 
poptGetContext(const char * name,int argc,const char ** argv,const struct poptOption * options,unsigned int flags)154 poptContext poptGetContext(const char * name, int argc, const char ** argv,
155 			const struct poptOption * options, unsigned int flags)
156 {
157     poptContext con = malloc(sizeof(*con));
158 
159     if (con == NULL) return NULL;	/* XXX can't happen */
160     memset(con, 0, sizeof(*con));
161 
162     con->os = con->optionStack;
163     con->os->argc = argc;
164     con->os->argv = argv;
165     con->os->argb = NULL;
166 
167     if (!(flags & POPT_CONTEXT_KEEP_FIRST))
168 	con->os->next = 1;		/* skip argv[0] */
169 
170     con->leftovers = calloc( (size_t)(argc + 1), sizeof(*con->leftovers) );
171     con->allocLeftovers = argc + 1;
172     con->options = options;
173     con->aliases = NULL;
174     con->numAliases = 0;
175     con->flags = flags;
176     con->execs = NULL;
177     con->numExecs = 0;
178     con->execFail = NULL;
179     con->finalArgvAlloced = argc * 2;
180     con->finalArgv = calloc( (size_t)con->finalArgvAlloced, sizeof(*con->finalArgv) );
181     con->execAbsolute = 1;
182     con->arg_strip = NULL;
183 
184     if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
185 	con->flags |= POPT_CONTEXT_POSIXMEHARDER;
186 
187     if (name)
188 	con->appName = xstrdup(name);
189 
190     invokeCallbacksPRE(con, con->options);
191 
192     return con;
193 }
194 
cleanOSE(struct optionStackEntry * os)195 static void cleanOSE(struct optionStackEntry *os)
196 {
197     os->nextArg = _free(os->nextArg);
198     os->argv = _free(os->argv);
199     os->argb = PBM_FREE(os->argb);
200 }
201 
poptResetContext(poptContext con)202 void poptResetContext(poptContext con)
203 {
204     int i;
205 
206     if (con == NULL) return;
207     while (con->os > con->optionStack) {
208 	cleanOSE(con->os--);
209     }
210     con->os->argb = PBM_FREE(con->os->argb);
211     con->os->currAlias = NULL;
212     con->os->nextCharArg = NULL;
213     con->os->nextArg = _free(con->os->nextArg);
214     con->os->next = 1;			/* skip argv[0] */
215 
216     con->numLeftovers = 0;
217     con->nextLeftover = 0;
218     con->restLeftover = 0;
219     con->doExec = NULL;
220     con->execFail = _free(con->execFail);
221 
222     if (con->finalArgv != NULL)
223     for (i = 0; i < con->finalArgvCount; i++) {
224 	con->finalArgv[i] = _free(con->finalArgv[i]);
225     }
226 
227     con->finalArgvCount = 0;
228     con->arg_strip = PBM_FREE(con->arg_strip);
229     return;
230 }
231 
232 /* Only one of longName, shortName should be set, not both. */
handleExec(poptContext con,const char * longName,char shortName)233 static int handleExec(poptContext con,
234 		const char * longName, char shortName)
235 {
236     poptItem item;
237     int i;
238 
239     if (con->execs == NULL || con->numExecs <= 0) /* XXX can't happen */
240 	return 0;
241 
242     for (i = con->numExecs - 1; i >= 0; i--) {
243 	item = con->execs + i;
244 	if (longName && !(item->option.longName &&
245 			!strcmp(longName, item->option.longName)))
246 	    continue;
247 	else if (shortName != item->option.shortName)
248 	    continue;
249 	break;
250     }
251     if (i < 0) return 0;
252 
253 
254     if (con->flags & POPT_CONTEXT_NO_EXEC)
255 	return 1;
256 
257     if (con->doExec == NULL) {
258 	con->doExec = con->execs + i;
259 	return 1;
260     }
261 
262     /* We already have an exec to do; remember this option for next
263        time 'round */
264     if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
265 	con->finalArgvAlloced += 10;
266 	con->finalArgv = realloc(con->finalArgv,
267 			sizeof(*con->finalArgv) * con->finalArgvAlloced);
268     }
269 
270     i = con->finalArgvCount++;
271     if (con->finalArgv != NULL)	/* XXX can't happen */
272     {	char *s  = malloc((longName ? strlen(longName) : 0) + sizeof("--"));
273 	if (s != NULL) {	/* XXX can't happen */
274 	    con->finalArgv[i] = s;
275 	    *s++ = '-';
276 	    if (longName)
277 		s = stpcpy( stpcpy(s, "-"), longName);
278 	    else
279 		*s++ = shortName;
280 	    *s = '\0';
281 	} else
282 	    con->finalArgv[i] = NULL;
283     }
284 
285     return 1;
286 }
287 
288 /**
289  * Compare long option for equality, adjusting for POPT_ARGFLAG_TOGGLE.
290  * @param opt           option
291  * @param longName	arg option
292  * @param longNameLen	arg option length
293  * @return		does long option match?
294  */
295 static int
longOptionStrcmp(const struct poptOption * opt,const char * longName,size_t longNameLen)296 longOptionStrcmp(const struct poptOption * opt,
297 		const char * longName, size_t longNameLen)
298 {
299     const char * optLongName = opt->longName;
300     int rc;
301 
302     if (optLongName == NULL || longName == NULL)	/* XXX can't heppen */
303 	return 0;
304 
305     if (F_ISSET(opt, TOGGLE)) {
306 	if (optLongName[0] == 'n' && optLongName[1] == 'o') {
307 	    optLongName += sizeof("no") - 1;
308 	    if (optLongName[0] == '-')
309 		optLongName++;
310 	}
311 	if (longName[0] == 'n' && longName[1] == 'o') {
312 	    longName += sizeof("no") - 1;
313 	    longNameLen -= sizeof("no") - 1;
314 	    if (longName[0] == '-') {
315 		longName++;
316 		longNameLen--;
317 	    }
318 	}
319     }
320     rc = (int)(strlen(optLongName) == longNameLen);
321     if (rc)
322 	rc = (int)(strncmp(optLongName, longName, longNameLen) == 0);
323     return rc;
324 }
325 
326 /* Only one of longName, shortName may be set at a time */
handleAlias(poptContext con,const char * longName,size_t longNameLen,char shortName,const char * nextArg)327 static int handleAlias(poptContext con,
328 		const char * longName, size_t longNameLen,
329 		char shortName,
330 		const char * nextArg)
331 {
332     poptItem item = con->os->currAlias;
333     int rc;
334     int i;
335 
336     if (item) {
337 	if (longName && item->option.longName != NULL
338 	 && longOptionStrcmp(&item->option, longName, longNameLen))
339 	    return 0;
340 	else
341 	if (shortName && shortName == item->option.shortName)
342 	    return 0;
343     }
344 
345     if (con->aliases == NULL || con->numAliases <= 0) /* XXX can't happen */
346 	return 0;
347 
348     for (i = con->numAliases - 1; i >= 0; i--) {
349 	item = con->aliases + i;
350 	if (longName) {
351 	    if (item->option.longName == NULL)
352 		continue;
353 	    if (!longOptionStrcmp(&item->option, longName, longNameLen))
354 		continue;
355 	} else if (shortName != item->option.shortName)
356 	    continue;
357 	break;
358     }
359     if (i < 0) return 0;
360 
361     if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
362 	return POPT_ERROR_OPTSTOODEEP;
363 
364     if (longName == NULL && nextArg != NULL && *nextArg != '\0')
365 	con->os->nextCharArg = nextArg;
366 
367     con->os++;
368     con->os->next = 0;
369     con->os->stuffed = 0;
370     con->os->nextArg = NULL;
371     con->os->nextCharArg = NULL;
372     con->os->currAlias = con->aliases + i;
373     {	const char ** av;
374 	int ac = con->os->currAlias->argc;
375 	/* Append --foo=bar arg to alias argv array (if present). */
376 	if (longName && nextArg != NULL && *nextArg != '\0') {
377 	    av = malloc((ac + 1 + 1) * sizeof(*av));
378 	    if (av != NULL) {	/* XXX won't happen. */
379 		for (i = 0; i < ac; i++) {
380 		    av[i] = con->os->currAlias->argv[i];
381 		}
382 		av[ac++] = nextArg;
383 		av[ac] = NULL;
384 	    } else	/* XXX revert to old popt behavior if malloc fails. */
385 		av = con->os->currAlias->argv;
386 	} else
387 	    av = con->os->currAlias->argv;
388 	rc = poptDupArgv(ac, av, &con->os->argc, &con->os->argv);
389 	if (av != NULL && av != con->os->currAlias->argv)
390 	    free(av);
391     }
392     con->os->argb = NULL;
393 
394     return (rc ? rc : 1);
395 }
396 
397 /**
398  * Return absolute path to executable by searching PATH.
399  * @param argv0		name of executable
400  * @return		(malloc'd) absolute path to executable (or NULL)
401  */
402 static
findProgramPath(const char * argv0)403 const char * findProgramPath(const char * argv0)
404 {
405     char *path = NULL, *s = NULL, *se;
406     char *t = NULL;
407 
408     if (argv0 == NULL) return NULL;	/* XXX can't happen */
409 
410     /* If there is a / in argv[0], it has to be an absolute path. */
411     /* XXX Hmmm, why not if (argv0[0] == '/') ... instead? */
412     if (strchr(argv0, '/'))
413 	return xstrdup(argv0);
414 
415     if ((path = getenv("PATH")) == NULL || (path = xstrdup(path)) == NULL)
416 	return NULL;
417 
418     /* The return buffer in t is big enough for any path. */
419     if ((t = malloc(strlen(path) + strlen(argv0) + sizeof("/"))) != NULL)
420     for (s = path; s && *s; s = se) {
421 
422 	/* Snip PATH element into [s,se). */
423 	if ((se = strchr(s, ':')))
424 	    *se++ = '\0';
425 
426 	/* Append argv0 to PATH element. */
427 	(void) stpcpy(stpcpy(stpcpy(t, s), "/"), argv0);
428 
429 	/* If file is executable, bingo! */
430 	if (!access(t, X_OK))
431 	    break;
432     }
433 
434     /* If no executable was found in PATH, return NULL. */
435     if (!(s && *s) && t != NULL)
436 	t = _free(t);
437     path = _free(path);
438 
439     return t;
440 }
441 
execCommand(poptContext con)442 static int execCommand(poptContext con)
443 {
444     poptItem item = con->doExec;
445     poptArgv argv = NULL;
446     int argc = 0;
447     int rc;
448     int ec = POPT_ERROR_ERRNO;
449 
450     if (item == NULL) /*XXX can't happen*/
451 	return POPT_ERROR_NOARG;
452 
453     if (item->argv == NULL || item->argc < 1 ||
454 	(!con->execAbsolute && strchr(item->argv[0], '/')))
455 	    return POPT_ERROR_NOARG;
456 
457     argv = malloc(sizeof(*argv) *
458 			(6 + item->argc + con->numLeftovers + con->finalArgvCount));
459     if (argv == NULL) return POPT_ERROR_MALLOC;
460 
461     if (!strchr(item->argv[0], '/') && con->execPath != NULL) {
462 	char *s = malloc(strlen(con->execPath) + strlen(item->argv[0]) + sizeof("/"));
463 	if (s)
464 	    (void)stpcpy(stpcpy(stpcpy(s, con->execPath), "/"), item->argv[0]);
465 
466 	argv[argc] = s;
467     } else
468 	argv[argc] = findProgramPath(item->argv[0]);
469     if (argv[argc++] == NULL) {
470 	ec = POPT_ERROR_NOARG;
471 	goto exit;
472     }
473 
474     if (item->argc > 1) {
475 	memcpy(argv + argc, item->argv + 1, sizeof(*argv) * (item->argc - 1));
476 	argc += (item->argc - 1);
477     }
478 
479     if (con->finalArgv != NULL && con->finalArgvCount > 0) {
480 	memcpy(argv + argc, con->finalArgv,
481 		sizeof(*argv) * con->finalArgvCount);
482 	argc += con->finalArgvCount;
483     }
484 
485     if (con->leftovers != NULL && con->numLeftovers > 0) {
486 	memcpy(argv + argc, con->leftovers, sizeof(*argv) * con->numLeftovers);
487 	argc += con->numLeftovers;
488     }
489 
490     argv[argc] = NULL;
491 
492 #if defined(hpux) || defined(__hpux)
493     rc = setresgid(getgid(), getgid(),-1);
494     if (rc) goto exit;
495     rc = setresuid(getuid(), getuid(),-1);
496     if (rc) goto exit;
497 #else
498 /*
499  * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
500  * XXX 	sez' Timur Bakeyev <mc@bat.ru>
501  * XXX	from Norbert Warmuth <nwarmuth@privat.circular.de>
502  */
503 #if defined(HAVE_SETUID)
504     rc = setgid(getgid());
505     if (rc) goto exit;
506     rc = setuid(getuid());
507     if (rc) goto exit;
508 #elif defined (HAVE_SETREUID)
509     rc = setregid(getgid(), getgid());
510     if (rc) goto exit;
511     rc = setreuid(getuid(), getuid());
512     if (rc) goto exit;
513 #else
514     /* refuse to exec if we cannot drop suid/sgid privileges */
515     if (getuid() != geteuid() || getgid() != getegid()) {
516 	errno = ENOTSUP;
517 	goto exit;
518     }
519 #endif
520 #endif
521 
522 #ifdef	MYDEBUG
523 if (_popt_debug)
524     {	poptArgv avp;
525 	fprintf(stderr, "==> execvp(%s) argv[%d]:", argv[0], argc);
526 	for (avp = argv; *avp; avp++)
527 	    fprintf(stderr, " '%s'", *avp);
528 	fprintf(stderr, "\n");
529     }
530 #endif
531 
532     rc = execvp(argv[0], (char *const *)argv);
533 
534     /* only reached on execvp() failure */
535     con->execFail = xstrdup(argv[0]);
536 
537 exit:
538     if (argv) {
539         if (argv[0])
540             free((void *)argv[0]);
541         free(argv);
542     }
543     return ec;
544 }
545 
546 static const struct poptOption *
findOption(const struct poptOption * opt,const char * longName,size_t longNameLen,char shortName,poptCallbackType * callback,const void ** callbackData,unsigned int argInfo)547 findOption(const struct poptOption * opt,
548 		const char * longName, size_t longNameLen,
549 		char shortName,
550 		poptCallbackType * callback,
551 		const void ** callbackData,
552 		unsigned int argInfo)
553 {
554     const struct poptOption * cb = NULL;
555     poptArg cbarg = { .ptr = NULL };
556 
557     /* This happens when a single - is given */
558     if (LF_ISSET(ONEDASH) && !shortName && (longName && *longName == '\0'))
559 	shortName = '-';
560 
561     for (; opt->longName || opt->shortName || opt->arg; opt++) {
562 	poptArg arg = { .ptr = opt->arg };
563 
564 	switch (poptArgType(opt)) {
565 	case POPT_ARG_INCLUDE_TABLE:	/* Recurse on included sub-tables. */
566 	{   const struct poptOption * opt2;
567 
568 	    poptSubstituteHelpI18N(arg.opt);	/* XXX side effects */
569 	    if (arg.ptr == NULL) continue;	/* XXX program error */
570 	    opt2 = findOption(arg.opt, longName, longNameLen, shortName, callback,
571 			      callbackData, argInfo);
572 	    if (opt2 == NULL) continue;
573 	    /* Sub-table data will be inheirited if no data yet. */
574 	    if (callback && *callback
575 	     && callbackData && *callbackData == NULL)
576 		*callbackData = opt->descrip;
577 	    return opt2;
578 	}   break;
579 	case POPT_ARG_CALLBACK:
580 	    cb = opt;
581 	    cbarg.ptr = opt->arg;
582 	    continue;
583 	    break;
584 	default:
585 	    break;
586 	}
587 
588 	if (longName != NULL && opt->longName != NULL &&
589 		   (!LF_ISSET(ONEDASH) || F_ISSET(opt, ONEDASH)) &&
590 		   longOptionStrcmp(opt, longName, longNameLen))
591 	{
592 	    break;
593 	} else if (shortName && shortName == opt->shortName) {
594 	    break;
595 	}
596     }
597 
598     if (opt->longName == NULL && !opt->shortName)
599 	return NULL;
600 
601     if (callback)
602 	*callback = (cb ? cbarg.cb : NULL);
603     if (callbackData)
604 	*callbackData = (cb && !CBF_ISSET(cb, INC_DATA) ? cb->descrip : NULL);
605 
606     return opt;
607 }
608 
findNextArg(poptContext con,unsigned argx,int delete_arg)609 static const char * findNextArg(poptContext con,
610 		unsigned argx, int delete_arg)
611 {
612     struct optionStackEntry * os = con->os;
613     const char * arg;
614 
615     do {
616 	int i;
617 	arg = NULL;
618 	while (os->next == os->argc && os > con->optionStack) os--;
619 	if (os->next == os->argc && os == con->optionStack) break;
620 	if (os->argv != NULL)
621 	for (i = os->next; i < os->argc; i++) {
622 	    if (os->argb && PBM_ISSET(i, os->argb))
623 		continue;
624 	    if (*os->argv[i] == '-')
625 		continue;
626 	    if (--argx > 0)
627 		continue;
628 	    arg = os->argv[i];
629 	    if (delete_arg) {
630 		if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
631 		if (os->argb != NULL)	/* XXX can't happen */
632 		    PBM_SET(i, os->argb);
633 	    }
634 	    break;
635 	}
636 	if (os > con->optionStack) os--;
637     } while (arg == NULL);
638     return arg;
639 }
640 
641 static const char *
expandNextArg(poptContext con,const char * s)642 expandNextArg(poptContext con, const char * s)
643 {
644     const char * a = NULL;
645     char *t, *te;
646     size_t tn = strlen(s) + 1;
647     char c;
648 
649     te = t = malloc(tn);
650     if (t == NULL) return NULL;		/* XXX can't happen */
651     *t = '\0';
652     while ((c = *s++) != '\0') {
653 	switch (c) {
654 #if 0	/* XXX can't do this */
655 	case '\\':	/* escape */
656 	    c = *s++;
657 	    break;
658 #endif
659 	case '!':
660 	    if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
661 		break;
662 	    /* XXX Make sure that findNextArg deletes only next arg. */
663 	    if (a == NULL) {
664 		if ((a = findNextArg(con, 1U, 1)) == NULL)
665 		    break;
666 	    }
667 	    s += sizeof("#:+") - 1;
668 
669 	    tn += strlen(a);
670 	    {   size_t pos = (size_t) (te - t);
671 		if ((t = realloc(t, tn)) == NULL)	/* XXX can't happen */
672 		    return NULL;
673 		te = stpcpy(t + pos, a);
674 	    }
675 	    continue;
676 	    break;
677 	default:
678 	    break;
679 	}
680 	*te++ = c;
681     }
682     *te++ = '\0';
683     /* If the new string is longer than needed, shorten. */
684     if ((t + tn) > te) {
685 	if ((te = realloc(t, (size_t)(te - t))) == NULL)
686 	    free(t);
687 	t = te;
688     }
689     return t;
690 }
691 
poptStripArg(poptContext con,int which)692 static void poptStripArg(poptContext con, int which)
693 {
694     if (con->arg_strip == NULL)
695 	con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
696     if (con->arg_strip != NULL)		/* XXX can't happen */
697     PBM_SET(which, con->arg_strip);
698     return;
699 }
700 
701 unsigned int _poptBitsN = _POPT_BITS_N;
702 unsigned int _poptBitsM = _POPT_BITS_M;
703 unsigned int _poptBitsK = _POPT_BITS_K;
704 
_poptBitsNew(poptBits * bitsp)705 static int _poptBitsNew(poptBits *bitsp)
706 {
707     if (bitsp == NULL)
708 	return POPT_ERROR_NULLARG;
709 
710     /* XXX handle negated initialization. */
711     if (*bitsp == NULL) {
712 	if (_poptBitsN == 0) {
713 	    _poptBitsN = _POPT_BITS_N;
714 	    _poptBitsM = _POPT_BITS_M;
715 	}
716 	if (_poptBitsM == 0U) _poptBitsM = (3 * _poptBitsN) / 2;
717 	if (_poptBitsK == 0U || _poptBitsK > 32U) _poptBitsK = _POPT_BITS_K;
718 	*bitsp = PBM_ALLOC(_poptBitsM-1);
719     }
720     return 0;
721 }
722 
poptBitsAdd(poptBits bits,const char * s)723 int poptBitsAdd(poptBits bits, const char * s)
724 {
725     size_t ns = (s ? strlen(s) : 0);
726     uint32_t h0 = 0;
727     uint32_t h1 = 0;
728 
729     if (bits == NULL || ns == 0)
730 	return POPT_ERROR_NULLARG;
731 
732     poptJlu32lpair(s, ns, &h0, &h1);
733 
734     for (ns = 0; ns < (size_t)_poptBitsK; ns++) {
735         uint32_t h = h0 + ns * h1;
736         uint32_t ix = (h % _poptBitsM);
737         PBM_SET(ix, bits);
738     }
739     return 0;
740 }
741 
poptBitsChk(poptBits bits,const char * s)742 int poptBitsChk(poptBits bits, const char * s)
743 {
744     size_t ns = (s ? strlen(s) : 0);
745     uint32_t h0 = 0;
746     uint32_t h1 = 0;
747     int rc = 1;
748 
749     if (bits == NULL || ns == 0)
750 	return POPT_ERROR_NULLARG;
751 
752     poptJlu32lpair(s, ns, &h0, &h1);
753 
754     for (ns = 0; ns < (size_t)_poptBitsK; ns++) {
755         uint32_t h = h0 + ns * h1;
756         uint32_t ix = (h % _poptBitsM);
757         if (PBM_ISSET(ix, bits))
758             continue;
759         rc = 0;
760         break;
761     }
762     return rc;
763 }
764 
poptBitsClr(poptBits bits)765 int poptBitsClr(poptBits bits)
766 {
767     static size_t nbw = (__PBM_NBITS/8);
768     size_t nw = (__PBM_IX(_poptBitsM-1) + 1);
769 
770     if (bits == NULL)
771 	return POPT_ERROR_NULLARG;
772     memset(bits, 0, nw * nbw);
773     return 0;
774 }
775 
poptBitsDel(poptBits bits,const char * s)776 int poptBitsDel(poptBits bits, const char * s)
777 {
778     size_t ns = (s ? strlen(s) : 0);
779     uint32_t h0 = 0;
780     uint32_t h1 = 0;
781 
782     if (bits == NULL || ns == 0)
783 	return POPT_ERROR_NULLARG;
784 
785     poptJlu32lpair(s, ns, &h0, &h1);
786 
787     for (ns = 0; ns < (size_t)_poptBitsK; ns++) {
788         uint32_t h = h0 + ns * h1;
789         uint32_t ix = (h % _poptBitsM);
790         PBM_CLR(ix, bits);
791     }
792     return 0;
793 }
794 
poptBitsIntersect(poptBits * ap,const poptBits b)795 int poptBitsIntersect(poptBits *ap, const poptBits b)
796 {
797     __pbm_bits *abits;
798     __pbm_bits *bbits;
799     __pbm_bits rc = 0;
800     size_t nw = (__PBM_IX(_poptBitsM-1) + 1);
801     size_t i;
802 
803     if (ap == NULL || b == NULL || _poptBitsNew(ap))
804 	return POPT_ERROR_NULLARG;
805     abits = __PBM_BITS(*ap);
806     bbits = __PBM_BITS(b);
807 
808     for (i = 0; i < nw; i++) {
809         abits[i] &= bbits[i];
810 	rc |= abits[i];
811     }
812     return (rc ? 1 : 0);
813 }
814 
poptBitsUnion(poptBits * ap,const poptBits b)815 int poptBitsUnion(poptBits *ap, const poptBits b)
816 {
817     __pbm_bits *abits;
818     __pbm_bits *bbits;
819     __pbm_bits rc = 0;
820     size_t nw = (__PBM_IX(_poptBitsM-1) + 1);
821     size_t i;
822 
823     if (ap == NULL || b == NULL || _poptBitsNew(ap))
824 	return POPT_ERROR_NULLARG;
825     abits = __PBM_BITS(*ap);
826     bbits = __PBM_BITS(b);
827 
828     for (i = 0; i < nw; i++) {
829         abits[i] |= bbits[i];
830 	rc |= abits[i];
831     }
832     return (rc ? 1 : 0);
833 }
834 
poptBitsArgs(poptContext con,poptBits * ap)835 int poptBitsArgs(poptContext con, poptBits *ap)
836 {
837     const char ** av;
838     int rc = 0;
839 
840     if (con == NULL || ap == NULL || _poptBitsNew(ap) ||
841 	con->leftovers == NULL || con->numLeftovers == con->nextLeftover)
842 	return POPT_ERROR_NULLARG;
843 
844     /* some apps like [like RPM ;-) ] need this NULL terminated */
845     con->leftovers[con->numLeftovers] = NULL;
846 
847     for (av = con->leftovers + con->nextLeftover; *av != NULL; av++) {
848 	if ((rc = poptBitsAdd(*ap, *av)) != 0)
849 	    break;
850     }
851     return rc;
852 }
853 
poptSaveBits(poptBits * bitsp,UNUSED (unsigned int argInfo),const char * s)854 int poptSaveBits(poptBits * bitsp,
855 		UNUSED(unsigned int argInfo), const char * s)
856 {
857     char *tbuf = NULL;
858     char *t, *te;
859     int rc = 0;
860 
861     if (bitsp == NULL || s == NULL || *s == '\0' || _poptBitsNew(bitsp))
862 	return POPT_ERROR_NULLARG;
863 
864     /* Parse comma separated attributes. */
865     te = tbuf = xstrdup(s);
866     while ((t = te) != NULL && *t) {
867 	while (*te != '\0' && *te != ',')
868 	    te++;
869 	if (*te != '\0')
870 	    *te++ = '\0';
871 	/* XXX Ignore empty strings. */
872 	if (*t == '\0')
873 	    continue;
874 	/* XXX Permit negated attributes. caveat emptor: false negatives. */
875 	if (*t == '!') {
876 	    t++;
877 	    if ((rc = poptBitsChk(*bitsp, t)) > 0)
878 		rc = poptBitsDel(*bitsp, t);
879 	} else
880 	    rc = poptBitsAdd(*bitsp, t);
881 	if (rc)
882 	    break;
883     }
884     tbuf = _free(tbuf);
885     return rc;
886 }
887 
poptSaveString(const char *** argvp,UNUSED (unsigned int argInfo),const char * val)888 int poptSaveString(const char *** argvp,
889 		UNUSED(unsigned int argInfo), const char * val)
890 {
891     int argc = 0;
892 
893     if (argvp == NULL || val == NULL)
894 	return POPT_ERROR_NULLARG;
895 
896     /* XXX likely needs an upper bound on argc. */
897     if (*argvp != NULL)
898     while ((*argvp)[argc] != NULL)
899 	argc++;
900 
901     if ((*argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp))) != NULL) {
902 	(*argvp)[argc++] = xstrdup(val);
903 	(*argvp)[argc  ] = NULL;
904     }
905     return 0;
906 }
907 
908 static unsigned int seed = 0;
909 
poptSaveLongLong(long long * arg,unsigned int argInfo,long long aLongLong)910 int poptSaveLongLong(long long * arg, unsigned int argInfo, long long aLongLong)
911 {
912     /* XXX Check alignment, may fail on funky platforms. */
913     if (arg == NULL || (((unsigned long)arg) & (ALIGNOF(*arg)-1)))
914 	return POPT_ERROR_NULLARG;
915 
916     if (aLongLong != 0 && LF_ISSET(RANDOM)) {
917 #if defined(HAVE_SRANDOM)
918 	if (!seed) {
919 	    srandom((unsigned)getpid());
920 	    srandom((unsigned)random());
921 	}
922 	aLongLong = (long long)(random() % (aLongLong > 0 ? aLongLong : -aLongLong));
923 	aLongLong++;
924 #else
925 	/* XXX avoid adding POPT_ERROR_UNIMPLEMENTED to minimize i18n churn. */
926 	return POPT_ERROR_BADOPERATION;
927 #endif
928     }
929     if (LF_ISSET(NOT))
930 	aLongLong = ~aLongLong;
931     switch (LF_ISSET(LOGICALOPS)) {
932     case 0:
933 	*arg = aLongLong;
934 	break;
935     case POPT_ARGFLAG_OR:
936 	*(unsigned long long *)arg |= (unsigned long long)aLongLong;
937 	break;
938     case POPT_ARGFLAG_AND:
939 	*(unsigned long long *)arg &= (unsigned long long)aLongLong;
940 	break;
941     case POPT_ARGFLAG_XOR:
942 	*(unsigned long long *)arg ^= (unsigned long long)aLongLong;
943 	break;
944     default:
945 	return POPT_ERROR_BADOPERATION;
946 	break;
947     }
948     return 0;
949 }
950 
poptSaveLong(long * arg,unsigned int argInfo,long aLong)951 int poptSaveLong(long * arg, unsigned int argInfo, long aLong)
952 {
953     /* XXX Check alignment, may fail on funky platforms. */
954     if (arg == NULL || (((unsigned long)arg) & (ALIGNOF(*arg)-1)))
955 	return POPT_ERROR_NULLARG;
956 
957     if (aLong != 0 && LF_ISSET(RANDOM)) {
958 #if defined(HAVE_SRANDOM)
959 	if (!seed) {
960 	    srandom((unsigned)getpid());
961 	    srandom((unsigned)random());
962 	}
963 	aLong = random() % (aLong > 0 ? aLong : -aLong);
964 	aLong++;
965 #else
966 	/* XXX avoid adding POPT_ERROR_UNIMPLEMENTED to minimize i18n churn. */
967 	return POPT_ERROR_BADOPERATION;
968 #endif
969     }
970     if (LF_ISSET(NOT))
971 	aLong = ~aLong;
972     switch (LF_ISSET(LOGICALOPS)) {
973     case 0:		   *arg = aLong; break;
974     case POPT_ARGFLAG_OR:  *(unsigned long *)arg |= (unsigned long)aLong; break;
975     case POPT_ARGFLAG_AND: *(unsigned long *)arg &= (unsigned long)aLong; break;
976     case POPT_ARGFLAG_XOR: *(unsigned long *)arg ^= (unsigned long)aLong; break;
977     default:
978 	return POPT_ERROR_BADOPERATION;
979 	break;
980     }
981     return 0;
982 }
983 
poptSaveInt(int * arg,unsigned int argInfo,long aLong)984 int poptSaveInt(int * arg, unsigned int argInfo, long aLong)
985 {
986     /* XXX Check alignment, may fail on funky platforms. */
987     if (arg == NULL || (((unsigned long)arg) & (ALIGNOF(*arg)-1)))
988 	return POPT_ERROR_NULLARG;
989 
990     if (aLong != 0 && LF_ISSET(RANDOM)) {
991 #if defined(HAVE_SRANDOM)
992 	if (!seed) {
993 	    srandom((unsigned)getpid());
994 	    srandom((unsigned)random());
995 	}
996 	aLong = random() % (aLong > 0 ? aLong : -aLong);
997 	aLong++;
998 #else
999 	/* XXX avoid adding POPT_ERROR_UNIMPLEMENTED to minimize i18n churn. */
1000 	return POPT_ERROR_BADOPERATION;
1001 #endif
1002     }
1003     if (LF_ISSET(NOT))
1004 	aLong = ~aLong;
1005     switch (LF_ISSET(LOGICALOPS)) {
1006     case 0:		   *arg = (int) aLong;				break;
1007     case POPT_ARGFLAG_OR:  *(unsigned int *)arg |= (unsigned int) aLong; break;
1008     case POPT_ARGFLAG_AND: *(unsigned int *)arg &= (unsigned int) aLong; break;
1009     case POPT_ARGFLAG_XOR: *(unsigned int *)arg ^= (unsigned int) aLong; break;
1010     default:
1011 	return POPT_ERROR_BADOPERATION;
1012 	break;
1013     }
1014     return 0;
1015 }
1016 
poptSaveShort(short * arg,unsigned int argInfo,long aLong)1017 int poptSaveShort(short * arg, unsigned int argInfo, long aLong)
1018 {
1019     /* XXX Check alignment, may fail on funky platforms. */
1020     if (arg == NULL || (((unsigned long)arg) & (ALIGNOF(*arg)-1)))
1021 	return POPT_ERROR_NULLARG;
1022 
1023     if (aLong != 0 && LF_ISSET(RANDOM)) {
1024 #if defined(HAVE_SRANDOM)
1025 	if (!seed) {
1026 	    srandom((unsigned)getpid());
1027 	    srandom((unsigned)random());
1028 	}
1029 	aLong = random() % (aLong > 0 ? aLong : -aLong);
1030 	aLong++;
1031 #else
1032 	/* XXX avoid adding POPT_ERROR_UNIMPLEMENTED to minimize i18n churn. */
1033 	return POPT_ERROR_BADOPERATION;
1034 #endif
1035     }
1036     if (LF_ISSET(NOT))
1037 	aLong = ~aLong;
1038     switch (LF_ISSET(LOGICALOPS)) {
1039     case 0:		   *arg = (short) aLong;
1040 	break;
1041     case POPT_ARGFLAG_OR:  *(unsigned short *)arg |= (unsigned short) aLong;
1042 	break;
1043     case POPT_ARGFLAG_AND: *(unsigned short *)arg &= (unsigned short) aLong;
1044 	break;
1045     case POPT_ARGFLAG_XOR: *(unsigned short *)arg ^= (unsigned short) aLong;
1046 	break;
1047     default: return POPT_ERROR_BADOPERATION;
1048 	break;
1049     }
1050     return 0;
1051 }
1052 
1053 /**
1054  * Return argInfo field, handling POPT_ARGFLAG_TOGGLE overrides.
1055  * @param con		context
1056  * @param opt           option
1057  * @return		argInfo
1058  */
poptArgInfo(poptContext con,const struct poptOption * opt)1059 static unsigned int poptArgInfo(poptContext con, const struct poptOption * opt)
1060 {
1061     unsigned int argInfo = opt->argInfo;
1062 
1063     if (con->os->argv != NULL && con->os->next > 0 && opt->longName != NULL)
1064     if (LF_ISSET(TOGGLE)) {
1065 	const char * longName = con->os->argv[con->os->next-1];
1066 	while (*longName == '-') longName++;
1067 	/* XXX almost good enough but consider --[no]nofoo corner cases. */
1068 	if (longName[0] != opt->longName[0] || longName[1] != opt->longName[1])
1069 	{
1070 	    if (!LF_ISSET(XOR)) {	/* XXX dont toggle with XOR */
1071 		/* Toggle POPT_BIT_SET <=> POPT_BIT_CLR. */
1072 		if (LF_ISSET(LOGICALOPS))
1073 		    argInfo ^= (POPT_ARGFLAG_OR|POPT_ARGFLAG_AND);
1074 		argInfo ^= POPT_ARGFLAG_NOT;
1075 	    }
1076 	}
1077     }
1078     return argInfo;
1079 }
1080 
1081 /**
1082  * Parse an integer expression.
1083  * @retval *llp		integer expression value
1084  * @param argInfo	integer expression type
1085  * @param val		integer expression string
1086  * @return		0 on success, otherwise POPT_* error.
1087  */
poptParseInteger(long long * llp,UNUSED (unsigned int argInfo),const char * val)1088 static int poptParseInteger(long long * llp,
1089 		UNUSED(unsigned int argInfo),
1090 		const char * val)
1091 {
1092     if (val) {
1093 	char *end = NULL;
1094 	*llp = strtoll(val, &end, 0);
1095 
1096 	/* XXX parse scaling suffixes here. */
1097 
1098 	if (!(end && *end == '\0'))
1099 	    return POPT_ERROR_BADNUMBER;
1100     } else
1101 	*llp = 0;
1102     return 0;
1103 }
1104 
1105 /**
1106  * Save the option argument through the (*opt->arg) pointer.
1107  * @param con		context
1108  * @param opt           option
1109  * @return		0 on success, otherwise POPT_* error.
1110  */
poptSaveArg(poptContext con,const struct poptOption * opt)1111 static int poptSaveArg(poptContext con, const struct poptOption * opt)
1112 {
1113     poptArg arg = { .ptr = opt->arg };
1114     int rc = 0;		/* assume success */
1115 
1116     switch (poptArgType(opt)) {
1117     case POPT_ARG_BITSET:
1118 	/* XXX memory leak, application is responsible for free. */
1119 	rc = poptSaveBits(arg.ptr, opt->argInfo, con->os->nextArg);
1120 	break;
1121     case POPT_ARG_ARGV:
1122 	/* XXX memory leak, application is responsible for free. */
1123 	rc = poptSaveString(arg.ptr, opt->argInfo, con->os->nextArg);
1124 	break;
1125     case POPT_ARG_STRING:
1126 	/* XXX memory leak, application is responsible for free. */
1127 	arg.argv[0] = (con->os->nextArg) ? xstrdup(con->os->nextArg) : NULL;
1128 	break;
1129 
1130     case POPT_ARG_INT:
1131     case POPT_ARG_SHORT:
1132     case POPT_ARG_LONG:
1133     case POPT_ARG_LONGLONG:
1134     {	unsigned int argInfo = poptArgInfo(con, opt);
1135 	long long aNUM = 0;
1136 
1137 	if ((rc = poptParseInteger(&aNUM, argInfo, con->os->nextArg)) != 0)
1138 	    break;
1139 
1140 	switch (poptArgType(opt)) {
1141 	case POPT_ARG_LONGLONG:
1142 /* XXX let's not demand C99 compiler flags for <limits.h> quite yet. */
1143 #if !defined(LLONG_MAX)
1144 #   define LLONG_MAX    9223372036854775807LL
1145 #   define LLONG_MIN    (-LLONG_MAX - 1LL)
1146 #endif
1147 	    rc = !(aNUM == LLONG_MIN || aNUM == LLONG_MAX)
1148 		? poptSaveLongLong(arg.longlongp, argInfo, aNUM)
1149 		: POPT_ERROR_OVERFLOW;
1150 	    break;
1151 	case POPT_ARG_LONG:
1152 	    rc = !(aNUM < (long long)LONG_MIN || aNUM > (long long)LONG_MAX)
1153 		? poptSaveLong(arg.longp, argInfo, (long)aNUM)
1154 		: POPT_ERROR_OVERFLOW;
1155 	    break;
1156 	case POPT_ARG_INT:
1157 	    rc = !(aNUM < (long long)INT_MIN || aNUM > (long long)INT_MAX)
1158 		? poptSaveInt(arg.intp, argInfo, (long)aNUM)
1159 		: POPT_ERROR_OVERFLOW;
1160 	    break;
1161 	case POPT_ARG_SHORT:
1162 	    rc = !(aNUM < (long long)SHRT_MIN || aNUM > (long long)SHRT_MAX)
1163 		? poptSaveShort(arg.shortp, argInfo, (long)aNUM)
1164 		: POPT_ERROR_OVERFLOW;
1165 	    break;
1166 	}
1167     }   break;
1168 
1169     case POPT_ARG_FLOAT:
1170     case POPT_ARG_DOUBLE:
1171     {	char *end = NULL;
1172 	double aDouble = 0.0;
1173 
1174 	if (con->os->nextArg) {
1175 	    int saveerrno = errno;
1176 	    errno = 0;
1177 	    aDouble = strtod(con->os->nextArg, &end);
1178 	    if (errno == ERANGE) {
1179 		rc = POPT_ERROR_OVERFLOW;
1180 		break;
1181 	    }
1182 	    errno = saveerrno;
1183 	    if (*end != '\0') {
1184 		rc = POPT_ERROR_BADNUMBER;
1185 		break;
1186 	    }
1187 	}
1188 
1189 	switch (poptArgType(opt)) {
1190 	case POPT_ARG_DOUBLE:
1191 	    arg.doublep[0] = aDouble;
1192 	    break;
1193 	case POPT_ARG_FLOAT:
1194 #define POPT_ABS(a)	((((a) - 0.0) < DBL_EPSILON) ? -(a) : (a))
1195 	    if ((FLT_MIN - POPT_ABS(aDouble)) > DBL_EPSILON
1196 	     || (POPT_ABS(aDouble) - FLT_MAX) > DBL_EPSILON)
1197 		rc = POPT_ERROR_OVERFLOW;
1198 	    else
1199 		arg.floatp[0] = (float) aDouble;
1200 	    break;
1201 	}
1202     }   break;
1203     case POPT_ARG_MAINCALL:
1204 	con->maincall = opt->arg;
1205 	break;
1206     default:
1207 	fprintf(stdout, POPT_("option type (%u) not implemented in popt\n"),
1208 		poptArgType(opt));
1209 	exit(EXIT_FAILURE);
1210 	break;
1211     }
1212     return rc;
1213 }
1214 
1215 /* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
poptGetNextOpt(poptContext con)1216 int poptGetNextOpt(poptContext con)
1217 {
1218     const struct poptOption * opt = NULL;
1219     int done = 0;
1220 
1221     if (con == NULL)
1222 	return -1;
1223     while (!done) {
1224 	const char * origOptString = NULL;
1225 	poptCallbackType cb = NULL;
1226 	const void * cbData = NULL;
1227 	const char * longArg = NULL;
1228 	int canstrip = 0;
1229 	int shorty = 0;
1230 
1231 	while (!con->os->nextCharArg && con->os->next == con->os->argc
1232 		&& con->os > con->optionStack) {
1233 	    cleanOSE(con->os--);
1234 	}
1235 	if (!con->os->nextCharArg && con->os->next == con->os->argc) {
1236 	    invokeCallbacksPOST(con, con->options);
1237 
1238 	    if (con->maincall) {
1239 		(void) (*con->maincall) (con->finalArgvCount, con->finalArgv);
1240 		return -1;
1241 	    }
1242 
1243 	    if (con->doExec) return execCommand(con);
1244 	    return -1;
1245 	}
1246 
1247 	/* Process next long option */
1248 	if (!con->os->nextCharArg) {
1249 	    const char * optString;
1250             size_t optStringLen;
1251 	    int thisopt;
1252 
1253 	    if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
1254 		con->os->next++;
1255 		continue;
1256 	    }
1257 	    thisopt = con->os->next;
1258 	    if (con->os->argv != NULL)	/* XXX can't happen */
1259 	    origOptString = con->os->argv[con->os->next++];
1260 
1261 	    if (origOptString == NULL)	/* XXX can't happen */
1262 		return POPT_ERROR_BADOPT;
1263 
1264 	    if (con->restLeftover || *origOptString != '-' ||
1265 		(*origOptString == '-' && origOptString[1] == '\0'))
1266 	    {
1267 		if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
1268 		    con->restLeftover = 1;
1269 		if (con->flags & POPT_CONTEXT_ARG_OPTS) {
1270 		    con->os->nextArg = xstrdup(origOptString);
1271 		    return 0;
1272 		}
1273 		if (con->leftovers != NULL) {	/* XXX can't happen */
1274 		    /* One might think we can never overflow the leftovers
1275 		       array.  Actually, that's true, as long as you don't
1276 		       use poptStuffArgs()... */
1277 		    if ((con->numLeftovers + 1) >= (con->allocLeftovers)) {
1278 			con->allocLeftovers += 10;
1279 			con->leftovers =
1280 			    realloc(con->leftovers,
1281 				    sizeof(*con->leftovers) * con->allocLeftovers);
1282 		    }
1283 		    con->leftovers[con->numLeftovers++]
1284 			= xstrdup(origOptString); /* so a free of a stuffed
1285 						     argv doesn't give us a
1286 						     dangling pointer */
1287 		}
1288 		continue;
1289 	    }
1290 
1291 	    /* Make a copy we can hack at */
1292 	    optString = origOptString;
1293 
1294 	    if (optString[0] == '\0')
1295 		return POPT_ERROR_BADOPT;
1296 
1297 	    if (optString[1] == '-' && !optString[2]) {
1298 		con->restLeftover = 1;
1299 		continue;
1300 	    } else {
1301 		const char *oe;
1302 		unsigned int argInfo = 0;
1303 
1304 		optString++;
1305 		if (*optString == '-')
1306 		    optString++;
1307 		else
1308 		    argInfo |= POPT_ARGFLAG_ONEDASH;
1309 
1310 		/* Check for "--long=arg" option. */
1311 		for (oe = optString; *oe && *oe != '='; oe++)
1312 		    {};
1313 		optStringLen = (size_t)(oe - optString);
1314 		if (*oe == '=')
1315 		    longArg = oe + 1;
1316 
1317 		/* XXX aliases with arg substitution need "--alias=arg" */
1318 		if (handleAlias(con, optString, optStringLen, '\0', longArg)) {
1319 		    longArg = NULL;
1320 		    continue;
1321 		}
1322 
1323 		if (handleExec(con, optString, '\0'))
1324 		    continue;
1325 
1326 		opt = findOption(con->options, optString, optStringLen, '\0', &cb, &cbData,
1327 				 argInfo);
1328 		if (!opt && !LF_ISSET(ONEDASH))
1329 		    return POPT_ERROR_BADOPT;
1330 	    }
1331 
1332 	    if (!opt) {
1333 		con->os->nextCharArg = origOptString + 1;
1334 		longArg = NULL;
1335 	    } else {
1336 		if (con->os == con->optionStack && F_ISSET(opt, STRIP))
1337 		{
1338 		    canstrip = 1;
1339 		    poptStripArg(con, thisopt);
1340 		}
1341 		shorty = 0;
1342 	    }
1343 	}
1344 
1345 	/* Process next short option */
1346 	if (con->os->nextCharArg) {
1347 	    const char * nextCharArg = con->os->nextCharArg;
1348 
1349 	    con->os->nextCharArg = NULL;
1350 
1351 	    if (handleAlias(con, NULL, 0, *nextCharArg, nextCharArg + 1))
1352 		continue;
1353 
1354 	    if (handleExec(con, NULL, *nextCharArg)) {
1355 		/* Restore rest of short options for further processing */
1356 		nextCharArg++;
1357 		if (*nextCharArg != '\0')
1358 		    con->os->nextCharArg = nextCharArg;
1359 		continue;
1360 	    }
1361 
1362 	    opt = findOption(con->options, NULL, 0, *nextCharArg, &cb,
1363 			     &cbData, 0);
1364 	    if (!opt)
1365 		return POPT_ERROR_BADOPT;
1366 	    shorty = 1;
1367 
1368 	    nextCharArg++;
1369 	    if (*nextCharArg != '\0')
1370 		con->os->nextCharArg = nextCharArg + (int)(*nextCharArg == '=');
1371 	}
1372 
1373 	if (opt == NULL) return POPT_ERROR_BADOPT;	/* XXX can't happen */
1374 	if (opt->arg && poptArgType(opt) == POPT_ARG_NONE) {
1375 	    unsigned int argInfo = poptArgInfo(con, opt);
1376 	    if (poptSaveInt((int *)opt->arg, argInfo, 1L))
1377 		return POPT_ERROR_BADOPERATION;
1378 	} else if (poptArgType(opt) == POPT_ARG_VAL) {
1379 	    if (opt->arg) {
1380 		unsigned int argInfo = poptArgInfo(con, opt);
1381 		if (poptSaveInt((int *)opt->arg, argInfo, (long)opt->val))
1382 		    return POPT_ERROR_BADOPERATION;
1383 	    }
1384 	} else if (poptArgType(opt) != POPT_ARG_NONE) {
1385 	    int rc;
1386 
1387 	    con->os->nextArg = _free(con->os->nextArg);
1388 	    if (longArg) {
1389 		longArg = expandNextArg(con, longArg);
1390 		con->os->nextArg = (char *) longArg;
1391 	    } else if (con->os->nextCharArg) {
1392 		longArg = expandNextArg(con, con->os->nextCharArg);
1393 		con->os->nextArg = (char *) longArg;
1394 		con->os->nextCharArg = NULL;
1395 	    } else {
1396 		while (con->os->next == con->os->argc &&
1397 			con->os > con->optionStack)
1398 		{
1399 		    cleanOSE(con->os--);
1400 		}
1401 		if (con->os->next == con->os->argc) {
1402 		    if (!F_ISSET(opt, OPTIONAL))
1403 			return POPT_ERROR_NOARG;
1404 		    con->os->nextArg = NULL;
1405 		} else {
1406 
1407 		    /*
1408 		     * Make sure this isn't part of a short arg or the
1409 		     * result of an alias expansion.
1410 		     */
1411 		    if (con->os == con->optionStack
1412 		     && F_ISSET(opt, STRIP) && canstrip)
1413 		    {
1414 			poptStripArg(con, con->os->next);
1415 		    }
1416 
1417 		    if (con->os->argv != NULL) {	/* XXX can't happen */
1418 			if (F_ISSET(opt, OPTIONAL) &&
1419 			    con->os->argv[con->os->next][0] == '-') {
1420 			    con->os->nextArg = NULL;
1421 			} else {
1422 			    /* XXX watchout: subtle side-effects live here. */
1423 			    longArg = con->os->argv[con->os->next++];
1424 			    longArg = expandNextArg(con, longArg);
1425 			    con->os->nextArg = (char *) longArg;
1426 			}
1427 		    }
1428 		}
1429 	    }
1430 	    longArg = NULL;
1431 
1432 	   /* Save the option argument through a (*opt->arg) pointer. */
1433 	    if (opt->arg != NULL && (rc = poptSaveArg(con, opt)) != 0)
1434 		return rc;
1435 	}
1436 
1437 	if (cb)
1438 	    invokeCallbacksOPTION(con, con->options, opt, cbData, shorty);
1439 	else if (opt->val && (poptArgType(opt) != POPT_ARG_VAL))
1440 	    done = 1;
1441 
1442 	if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
1443 	    con->finalArgvAlloced += 10;
1444 	    con->finalArgv = realloc(con->finalArgv,
1445 			    sizeof(*con->finalArgv) * con->finalArgvAlloced);
1446 	}
1447 
1448 	if (con->finalArgv != NULL)
1449 	{   char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + sizeof("--"));
1450 	    if (s != NULL) {	/* XXX can't happen */
1451 		con->finalArgv[con->finalArgvCount++] = s;
1452 		*s++ = '-';
1453 		if (opt->longName) {
1454 		    if (!F_ISSET(opt, ONEDASH))
1455 			*s++ = '-';
1456 		    s = stpcpy(s, opt->longName);
1457 		} else {
1458 		    *s++ = opt->shortName;
1459 		    *s = '\0';
1460 		}
1461 	    } else
1462 		con->finalArgv[con->finalArgvCount++] = NULL;
1463 	}
1464 
1465 	if (opt->arg && poptArgType(opt) == POPT_ARG_NONE)
1466 	    ;
1467 	else if (poptArgType(opt) == POPT_ARG_VAL)
1468 	    ;
1469 	else if (poptArgType(opt) != POPT_ARG_NONE) {
1470 	    if (con->finalArgv != NULL && con->os->nextArg != NULL)
1471 	        con->finalArgv[con->finalArgvCount++] =
1472 			xstrdup(con->os->nextArg);
1473 	}
1474     }
1475 
1476     return (opt ? opt->val : -1);	/* XXX can't happen */
1477 }
1478 
poptGetOptArg(poptContext con)1479 char * poptGetOptArg(poptContext con)
1480 {
1481     char * ret = NULL;
1482     if (con) {
1483 	ret = con->os->nextArg;
1484 	con->os->nextArg = NULL;
1485     }
1486     return ret;
1487 }
1488 
poptGetArg(poptContext con)1489 const char * poptGetArg(poptContext con)
1490 {
1491     const char * ret = NULL;
1492     if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
1493 	ret = con->leftovers[con->nextLeftover++];
1494     return ret;
1495 }
1496 
poptPeekArg(poptContext con)1497 const char * poptPeekArg(poptContext con)
1498 {
1499     const char * ret = NULL;
1500     if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
1501 	ret = con->leftovers[con->nextLeftover];
1502     return ret;
1503 }
1504 
poptGetArgs(poptContext con)1505 const char ** poptGetArgs(poptContext con)
1506 {
1507     if (con == NULL ||
1508 	con->leftovers == NULL || con->numLeftovers == con->nextLeftover)
1509 	return NULL;
1510 
1511     /* some apps like [like RPM ;-) ] need this NULL terminated */
1512     con->leftovers[con->numLeftovers] = NULL;
1513 
1514     return (con->leftovers + con->nextLeftover);
1515 }
1516 
1517 static
poptFreeItems(poptItem items,int nitems)1518 poptItem poptFreeItems(poptItem items, int nitems)
1519 {
1520     if (items != NULL) {
1521 	poptItem item = items;
1522 	while (--nitems >= 0) {
1523 	    item->option.longName = _free(item->option.longName);
1524 	    item->option.descrip = _free(item->option.descrip);
1525 	    item->option.argDescrip = _free(item->option.argDescrip);
1526 	    item->argv = _free(item->argv);
1527 	    item++;
1528 	}
1529 	items = _free(items);
1530     }
1531     return NULL;
1532 }
1533 
poptFreeContext(poptContext con)1534 poptContext poptFreeContext(poptContext con)
1535 {
1536     int i;
1537 
1538     if (con == NULL) return con;
1539     poptResetContext(con);
1540 
1541     con->aliases = poptFreeItems(con->aliases, con->numAliases);
1542     con->numAliases = 0;
1543 
1544     con->execs = poptFreeItems(con->execs, con->numExecs);
1545     con->numExecs = 0;
1546 
1547     for (i = 0; i < con->numLeftovers; i++) {
1548         con->leftovers[i] = _free(&con->leftovers[i]);
1549     }
1550     con->leftovers = _free(con->leftovers);
1551 
1552     con->finalArgv = _free(con->finalArgv);
1553     con->appName = _free(con->appName);
1554     con->otherHelp = _free(con->otherHelp);
1555     con->execPath = _free(con->execPath);
1556     con->arg_strip = PBM_FREE(con->arg_strip);
1557 
1558     con = _free(con);
1559     return con;
1560 }
1561 
poptAddAlias(poptContext con,struct poptAlias alias,UNUSED (int flags))1562 int poptAddAlias(poptContext con, struct poptAlias alias,
1563 		UNUSED(int flags))
1564 {
1565     struct poptItem_s item_buf;
1566     poptItem item = &item_buf;
1567     memset(item, 0, sizeof(*item));
1568     item->option.longName = alias.longName;
1569     item->option.shortName = alias.shortName;
1570     item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
1571     item->option.arg = 0;
1572     item->option.val = 0;
1573     item->option.descrip = NULL;
1574     item->option.argDescrip = NULL;
1575     item->argc = alias.argc;
1576     item->argv = alias.argv;
1577     return poptAddItem(con, item, 0);
1578 }
1579 
poptAddItem(poptContext con,poptItem newItem,int flags)1580 int poptAddItem(poptContext con, poptItem newItem, int flags)
1581 {
1582     poptItem * items, item;
1583     int * nitems;
1584 
1585     switch (flags) {
1586     case 1:
1587 	items = &con->execs;
1588 	nitems = &con->numExecs;
1589 	break;
1590     case 0:
1591 	items = &con->aliases;
1592 	nitems = &con->numAliases;
1593 	break;
1594     default:
1595 	return 1;
1596 	break;
1597     }
1598 
1599     *items = realloc((*items), ((*nitems) + 1) * sizeof(**items));
1600     if ((*items) == NULL)
1601 	return 1;
1602 
1603     item = (*items) + (*nitems);
1604 
1605     item->option.longName =
1606 	(newItem->option.longName ? xstrdup(newItem->option.longName) : NULL);
1607     item->option.shortName = newItem->option.shortName;
1608     item->option.argInfo = newItem->option.argInfo;
1609     item->option.arg = newItem->option.arg;
1610     item->option.val = newItem->option.val;
1611     item->option.descrip =
1612 	(newItem->option.descrip ? xstrdup(newItem->option.descrip) : NULL);
1613     item->option.argDescrip =
1614        (newItem->option.argDescrip ? xstrdup(newItem->option.argDescrip) : NULL);
1615     item->argc = newItem->argc;
1616     item->argv = newItem->argv;
1617 
1618     (*nitems)++;
1619 
1620     return 0;
1621 }
1622 
poptBadOption(poptContext con,unsigned int flags)1623 const char * poptBadOption(poptContext con, unsigned int flags)
1624 {
1625     struct optionStackEntry * os = NULL;
1626     const char *badOpt = NULL;
1627 
1628     if (con != NULL) {
1629        /* Stupid hack to return something semi-meaningful from exec failure */
1630        if (con->execFail) {
1631            badOpt = con->execFail;
1632        } else {
1633            os = (flags & POPT_BADOPTION_NOALIAS) ? con->optionStack : con->os;
1634            badOpt = os->argv[os->next - 1];
1635        }
1636     }
1637 
1638     return badOpt;
1639 }
1640 
poptStrerror(const int error)1641 const char * poptStrerror(const int error)
1642 {
1643     switch (error) {
1644       case POPT_ERROR_NOARG:
1645 	return POPT_("missing argument");
1646       case POPT_ERROR_BADOPT:
1647 	return POPT_("unknown option");
1648       case POPT_ERROR_BADOPERATION:
1649 	return POPT_("mutually exclusive logical operations requested");
1650       case POPT_ERROR_NULLARG:
1651 	return POPT_("opt->arg should not be NULL");
1652       case POPT_ERROR_OPTSTOODEEP:
1653 	return POPT_("aliases nested too deeply");
1654       case POPT_ERROR_BADQUOTE:
1655 	return POPT_("error in parameter quoting");
1656       case POPT_ERROR_BADNUMBER:
1657 	return POPT_("invalid numeric value");
1658       case POPT_ERROR_OVERFLOW:
1659 	return POPT_("number too large or too small");
1660       case POPT_ERROR_MALLOC:
1661 	return POPT_("memory allocation failed");
1662       case POPT_ERROR_BADCONFIG:
1663 	return POPT_("config file failed sanity test");
1664       case POPT_ERROR_ERRNO:
1665 	return strerror(errno);
1666       default:
1667 	return POPT_("unknown error");
1668     }
1669 }
1670 
poptStuffArgs(poptContext con,const char ** argv)1671 int poptStuffArgs(poptContext con, const char ** argv)
1672 {
1673     int argc;
1674     int rc;
1675 
1676     if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
1677 	return POPT_ERROR_OPTSTOODEEP;
1678 
1679     for (argc = 0; argv[argc]; argc++)
1680 	{};
1681 
1682     con->os++;
1683     con->os->next = 0;
1684     con->os->nextArg = NULL;
1685     con->os->nextCharArg = NULL;
1686     con->os->currAlias = NULL;
1687     rc = poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
1688     con->os->argb = NULL;
1689     con->os->stuffed = 1;
1690 
1691     return rc;
1692 }
1693 
poptGetInvocationName(poptContext con)1694 const char * poptGetInvocationName(poptContext con)
1695 {
1696     return (con->os->argv ? con->os->argv[0] : "");
1697 }
1698 
poptStrippedArgv(poptContext con,int argc,char ** argv)1699 int poptStrippedArgv(poptContext con, int argc, char ** argv)
1700 {
1701     int numargs = argc;
1702     int j = 1;
1703     int i;
1704 
1705     if (con->arg_strip)
1706     for (i = 1; i < argc; i++) {
1707 	if (PBM_ISSET(i, con->arg_strip))
1708 	    numargs--;
1709     }
1710 
1711     for (i = 1; i < argc; i++) {
1712 	if (con->arg_strip && PBM_ISSET(i, con->arg_strip))
1713 	    continue;
1714 	argv[j] = (j < numargs) ? argv[i] : NULL;
1715 	j++;
1716     }
1717 
1718     return numargs;
1719 }
1720