1 /* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
2 file accompanying popt source distributions, available from
3 ftp://ftp.redhat.com/pub/code/popt */
10 static char * strerror(int errno) {
12 extern char * sys_errlist[];
14 if ((0 <= errno) && (errno < sys_nerr))
15 return sys_errlist[errno];
17 return POPT_("unknown errno");
21 void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
22 if (con->execPath) xfree(con->execPath);
23 con->execPath = xstrdup(path);
24 con->execAbsolute = allowAbsolute;
27 static void invokeCallbacks(poptContext con, const struct poptOption * table,
29 const struct poptOption * opt = table;
32 while (opt->longName || opt->shortName || opt->arg) {
33 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
34 invokeCallbacks(con, opt->arg, post);
35 } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
36 ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
37 ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
38 cb = (poptCallbackType)opt->arg;
39 cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
40 NULL, NULL, opt->descrip);
46 poptContext poptGetContext(const char * name, int argc, const char ** argv,
47 const struct poptOption * options, int flags) {
48 poptContext con = malloc(sizeof(*con));
50 memset(con, 0, sizeof(*con));
52 con->os = con->optionStack;
57 if (!(flags & POPT_CONTEXT_KEEP_FIRST))
58 con->os->next = 1; /* skip argv[0] */
60 con->leftovers = calloc( (argc + 1), sizeof(char *) );
61 con->options = options;
67 con->finalArgvAlloced = argc * 2;
68 con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
69 con->execAbsolute = 1;
70 con->arg_strip = NULL;
72 if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
73 con->flags |= POPT_CONTEXT_POSIXMEHARDER;
76 con->appName = strcpy(malloc(strlen(name) + 1), name);
78 invokeCallbacks(con, con->options, 0);
83 static void cleanOSE(struct optionStackEntry *os)
99 void poptResetContext(poptContext con) {
102 while (con->os > con->optionStack) {
106 PBM_FREE(con->os->argb);
107 con->os->argb = NULL;
109 con->os->currAlias = NULL;
110 con->os->nextCharArg = NULL;
111 con->os->nextArg = NULL;
112 con->os->next = 1; /* skip argv[0] */
114 con->numLeftovers = 0;
115 con->nextLeftover = 0;
116 con->restLeftover = 0;
119 for (i = 0; i < con->finalArgvCount; i++) {
120 if (con->finalArgv[i]) {
121 xfree(con->finalArgv[i]);
122 con->finalArgv[i] = NULL;
126 con->finalArgvCount = 0;
128 if (con->arg_strip) {
129 PBM_FREE(con->arg_strip);
130 con->arg_strip = NULL;
134 /* Only one of longName, shortName may be set at a time */
135 static int handleExec(poptContext con, char * longName, char shortName) {
138 i = con->numExecs - 1;
140 while (i >= 0 && (!con->execs[i].longName ||
141 strcmp(con->execs[i].longName, longName))) i--;
144 con->execs[i].shortName != shortName) i--;
149 if (con->flags & POPT_CONTEXT_NO_EXEC)
152 if (con->doExec == NULL) {
153 con->doExec = con->execs + i;
157 /* We already have an exec to do; remember this option for next
159 if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
160 con->finalArgvAlloced += 10;
161 con->finalArgv = realloc(con->finalArgv,
162 sizeof(*con->finalArgv) * con->finalArgvAlloced);
165 i = con->finalArgvCount++;
166 { char *s = malloc((longName ? strlen(longName) : 0) + 3);
168 sprintf(s, "--%s", longName);
170 sprintf(s, "-%c", shortName);
171 con->finalArgv[i] = s;
177 /* Only one of longName, shortName may be set at a time */
178 static int handleAlias(poptContext con, const char * longName, char shortName,
179 /*@keep@*/ const char * nextCharArg) {
182 if (con->os->currAlias && con->os->currAlias->longName && longName &&
183 !strcmp(con->os->currAlias->longName, longName))
185 if (con->os->currAlias && shortName &&
186 shortName == con->os->currAlias->shortName)
189 i = con->numAliases - 1;
191 while (i >= 0 && (!con->aliases[i].longName ||
192 strcmp(con->aliases[i].longName, longName))) i--;
195 con->aliases[i].shortName != shortName) i--;
200 if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
201 return POPT_ERROR_OPTSTOODEEP;
203 if (nextCharArg && *nextCharArg)
204 con->os->nextCharArg = nextCharArg;
208 con->os->stuffed = 0;
209 con->os->nextArg = NULL;
210 con->os->nextCharArg = NULL;
211 con->os->currAlias = con->aliases + i;
212 poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
213 &con->os->argc, &con->os->argv);
214 con->os->argb = NULL;
219 static void execCommand(poptContext con) {
222 const char * script = con->doExec->script;
224 argv = malloc(sizeof(*argv) *
225 (6 + con->numLeftovers + con->finalArgvCount));
227 if (!con->execAbsolute && strchr(script, '/')) return;
229 if (!strchr(script, '/') && con->execPath) {
230 char *s = alloca(strlen(con->execPath) + strlen(script) + 2);
231 sprintf(s, "%s/%s", con->execPath, script);
238 argv[pos] = findProgramPath(con->os->argv[0]);
239 if (argv[pos]) pos++;
242 memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
243 pos += con->finalArgvCount;
245 if (con->numLeftovers) {
247 memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
248 pos += con->numLeftovers;
254 setresuid(getuid(), getuid(),-1);
257 * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
258 * XXX sez' Timur Bakeyev <mc@bat.ru>
259 * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
261 #if defined(HAVE_SETUID)
263 #elif defined (HAVE_SETREUID)
264 setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
266 ; /* Can't drop privileges */
270 execvp(argv[0], (char *const *)argv);
273 /*@observer@*/ static const struct poptOption *
274 findOption(const struct poptOption * table, const char * longName,
276 /*@out@*/ poptCallbackType * callback, /*@out@*/ const void ** callbackData,
279 const struct poptOption * opt = table;
280 const struct poptOption * opt2;
281 const struct poptOption * cb = NULL;
283 /* This happens when a single - is given */
284 if (singleDash && !shortName && !*longName)
287 while (opt->longName || opt->shortName || opt->arg) {
288 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
289 opt2 = findOption(opt->arg, longName, shortName, callback,
290 callbackData, singleDash);
292 if (*callback && !*callbackData)
293 *callbackData = opt->descrip;
296 } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
298 } else if (longName && opt->longName &&
299 (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
300 !strcmp(longName, opt->longName)) {
302 } else if (shortName && shortName == opt->shortName) {
308 if (!opt->longName && !opt->shortName) return NULL;
309 *callbackData = NULL;
312 *callback = (poptCallbackType)cb->arg;
313 if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
314 *callbackData = cb->descrip;
320 static const char *findNextArg(poptContext con, unsigned argx, int delete)
322 struct optionStackEntry * os = con->os;
328 while (os->next == os->argc && os > con->optionStack) os--;
329 if (os->next == os->argc && os == con->optionStack) break;
330 for (i = os->next; i < os->argc; i++) {
331 if (os->argb && PBM_ISSET(i, os->argb)) continue;
332 if (*os->argv[i] == '-') continue;
333 if (--argx > 0) continue;
336 if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
337 PBM_SET(i, os->argb);
341 if (os > con->optionStack) os--;
342 } while (arg == NULL);
346 static /*@only@*/ const char * expandNextArg(poptContext con, const char * s)
351 size_t tn = strlen(s) + 1;
354 te = t = malloc(tn);;
355 while ((c = *s++) != '\0') {
357 #if 0 /* XXX can't do this */
358 case '\\': /* escape */
363 if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
365 if ((a = findNextArg(con, 1, 1)) == NULL)
374 strncpy(te, a, alen); te += alen;
376 /*@notreached@*/ break;
383 t = realloc(t, strlen(t)+1); /* XXX memory leak, hard to plug */
387 static void poptStripArg(poptContext con, int which)
389 if(con->arg_strip == NULL) {
390 con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
392 PBM_SET(which, con->arg_strip);
395 /* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
396 int poptGetNextOpt(poptContext con)
398 const struct poptOption * opt = NULL;
402 const char * origOptString = NULL;
403 poptCallbackType cb = NULL;
404 const void * cbData = NULL;
405 const char * longArg = NULL;
408 while (!con->os->nextCharArg && con->os->next == con->os->argc
409 && con->os > con->optionStack) {
412 if (!con->os->nextCharArg && con->os->next == con->os->argc) {
413 invokeCallbacks(con, con->options, 1);
414 if (con->doExec) execCommand(con);
418 /* Process next long option */
419 if (!con->os->nextCharArg) {
420 char * localOptString, * optString;
423 if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
427 thisopt=con->os->next;
428 origOptString = con->os->argv[con->os->next++];
430 if (con->restLeftover || *origOptString != '-') {
431 con->leftovers[con->numLeftovers++] = origOptString;
432 if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
433 con->restLeftover = 1;
437 /* Make a copy we can hack at */
438 localOptString = optString =
439 strcpy(alloca(strlen(origOptString) + 1),
443 return POPT_ERROR_BADOPT;
445 if (optString[1] == '-' && !optString[2]) {
446 con->restLeftover = 1;
453 if (*optString == '-')
454 singleDash = 0, optString++;
458 /* XXX aliases with arg substitution need "--alias=arg" */
459 if (handleAlias(con, optString, '\0', NULL))
461 if (handleExec(con, optString, '\0'))
464 /* Check for "--long=arg" option. */
465 for (oe = optString; *oe && *oe != '='; oe++)
469 /* XXX longArg is mapped back to persistent storage. */
470 longArg = origOptString + (oe - localOptString);
473 opt = findOption(con->options, optString, '\0', &cb, &cbData,
475 if (!opt && !singleDash)
476 return POPT_ERROR_BADOPT;
480 con->os->nextCharArg = origOptString + 1;
482 if(con->os == con->optionStack &&
483 opt->argInfo & POPT_ARGFLAG_STRIP) {
485 poptStripArg(con, thisopt);
490 /* Process next short option */
491 if (con->os->nextCharArg) {
492 origOptString = con->os->nextCharArg;
494 con->os->nextCharArg = NULL;
496 if (handleAlias(con, NULL, *origOptString,
497 origOptString + 1)) {
501 if (handleExec(con, NULL, *origOptString))
504 opt = findOption(con->options, NULL, *origOptString, &cb,
507 return POPT_ERROR_BADOPT;
511 con->os->nextCharArg = origOptString;
514 if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
515 *((int *)opt->arg) = 1;
516 } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
518 *((int *) opt->arg) = opt->val;
519 } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
520 if (con->os->nextArg) {
521 xfree(con->os->nextArg);
522 con->os->nextArg = NULL;
525 con->os->nextArg = expandNextArg(con, longArg);
526 } else if (con->os->nextCharArg) {
527 con->os->nextArg = expandNextArg(con, con->os->nextCharArg);
528 con->os->nextCharArg = NULL;
530 while (con->os->next == con->os->argc &&
531 con->os > con->optionStack) {
534 if (con->os->next == con->os->argc)
535 return POPT_ERROR_NOARG;
537 /* make sure this isn't part of a short arg or the
538 result of an alias expansion */
539 if(con->os == con->optionStack &&
540 opt->argInfo & POPT_ARGFLAG_STRIP &&
542 poptStripArg(con, con->os->next);
545 con->os->nextArg = expandNextArg(con, con->os->argv[con->os->next++]);
552 switch (opt->argInfo & POPT_ARG_MASK) {
553 case POPT_ARG_STRING:
554 /* XXX memory leak, hard to plug */
555 *((const char **) opt->arg) = xstrdup(con->os->nextArg);
560 aLong = strtol(con->os->nextArg, &end, 0);
561 if (!(end && *end == '\0'))
562 return POPT_ERROR_BADNUMBER;
564 if (aLong == LONG_MIN || aLong == LONG_MAX)
565 return POPT_ERROR_OVERFLOW;
566 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
567 *((long *) opt->arg) = aLong;
569 if (aLong > INT_MAX || aLong < INT_MIN)
570 return POPT_ERROR_OVERFLOW;
571 *((int *) opt->arg) = aLong;
576 fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
577 opt->argInfo & POPT_ARG_MASK);
584 cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
585 else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
588 if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
589 con->finalArgvAlloced += 10;
590 con->finalArgv = realloc(con->finalArgv,
591 sizeof(*con->finalArgv) * con->finalArgvAlloced);
594 { char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
596 sprintf(s, "--%s", opt->longName);
598 sprintf(s, "-%c", opt->shortName);
599 con->finalArgv[con->finalArgvCount++] = s;
602 if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
603 && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) {
604 con->finalArgv[con->finalArgvCount++] = xstrdup(con->os->nextArg);
611 const char * poptGetOptArg(poptContext con) {
612 const char * ret = con->os->nextArg;
613 con->os->nextArg = NULL;
617 const char * poptGetArg(poptContext con) {
618 if (con->numLeftovers == con->nextLeftover) return NULL;
619 return con->leftovers[con->nextLeftover++];
622 const char * poptPeekArg(poptContext con) {
623 if (con->numLeftovers == con->nextLeftover) return NULL;
624 return con->leftovers[con->nextLeftover];
627 const char ** poptGetArgs(poptContext con) {
628 if (con->numLeftovers == con->nextLeftover) return NULL;
630 /* some apps like [like RPM ;-) ] need this NULL terminated */
631 con->leftovers[con->numLeftovers] = NULL;
633 return (con->leftovers + con->nextLeftover);
636 void poptFreeContext(poptContext con) {
639 poptResetContext(con);
640 if (con->os->argb) free(con->os->argb);
642 for (i = 0; i < con->numAliases; i++) {
643 if (con->aliases[i].longName) xfree(con->aliases[i].longName);
644 free(con->aliases[i].argv);
647 for (i = 0; i < con->numExecs; i++) {
648 if (con->execs[i].longName) xfree(con->execs[i].longName);
649 xfree(con->execs[i].script);
651 if (con->execs) xfree(con->execs);
653 free(con->leftovers);
654 free(con->finalArgv);
655 if (con->appName) xfree(con->appName);
656 if (con->aliases) free(con->aliases);
657 if (con->otherHelp) xfree(con->otherHelp);
658 if (con->execPath) xfree(con->execPath);
659 if (con->arg_strip) PBM_FREE(con->arg_strip);
664 int poptAddAlias(poptContext con, struct poptAlias newAlias,
665 /*@unused@*/ int flags)
667 int aliasNum = con->numAliases++;
668 struct poptAlias * alias;
670 /* SunOS won't realloc(NULL, ...) */
672 con->aliases = malloc(sizeof(newAlias) * con->numAliases);
674 con->aliases = realloc(con->aliases,
675 sizeof(newAlias) * con->numAliases);
676 alias = con->aliases + aliasNum;
678 alias->longName = (newAlias.longName)
679 ? strcpy(malloc(strlen(newAlias.longName) + 1), newAlias.longName)
681 alias->shortName = newAlias.shortName;
682 alias->argc = newAlias.argc;
683 alias->argv = newAlias.argv;
688 const char * poptBadOption(poptContext con, int flags) {
689 struct optionStackEntry * os;
691 if (flags & POPT_BADOPTION_NOALIAS)
692 os = con->optionStack;
696 return os->argv[os->next - 1];
699 #define POPT_ERROR_NOARG -10
700 #define POPT_ERROR_BADOPT -11
701 #define POPT_ERROR_OPTSTOODEEP -13
702 #define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
703 #define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
705 const char *const poptStrerror(const int error) {
707 case POPT_ERROR_NOARG:
708 return POPT_("missing argument");
709 case POPT_ERROR_BADOPT:
710 return POPT_("unknown option");
711 case POPT_ERROR_OPTSTOODEEP:
712 return POPT_("aliases nested too deeply");
713 case POPT_ERROR_BADQUOTE:
714 return POPT_("error in paramter quoting");
715 case POPT_ERROR_BADNUMBER:
716 return POPT_("invalid numeric value");
717 case POPT_ERROR_OVERFLOW:
718 return POPT_("number too large or too small");
719 case POPT_ERROR_ERRNO:
720 return strerror(errno);
722 return POPT_("unknown error");
726 int poptStuffArgs(poptContext con, const char ** argv) {
729 if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
730 return POPT_ERROR_OPTSTOODEEP;
732 for (argc = 0; argv[argc]; argc++)
737 con->os->nextArg = NULL;
738 con->os->nextCharArg = NULL;
739 con->os->currAlias = NULL;
740 poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
741 con->os->argb = NULL;
742 con->os->stuffed = 1;
747 const char * poptGetInvocationName(poptContext con) {
748 return con->os->argv[0];
751 int poptStrippedArgv(poptContext con, int argc, char **argv)
753 int i,j=1, numargs=argc;
755 for(i=1; i<argc; i++) {
756 if(PBM_ISSET(i, con->arg_strip)) {
761 for(i=1; i<argc; i++) {
762 if(PBM_ISSET(i, con->arg_strip)) {