1999-09-09 Federico Mena Quintero <federico@redhat.com>
[midnight-commander.git] / src / popt.c
blob39e18d7b3a533436bb167737d7663ec8947998ba
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 */
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif
9 #include "poptalloca.h"
10 #include <errno.h>
11 #include <ctype.h>
12 #include <fcntl.h>
13 #include <limits.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
19 #include "findme.h"
20 #include "popt.h"
21 #include "poptint.h"
23 #ifndef HAVE_STRERROR
24 static char * strerror(int errno) {
25 extern int sys_nerr;
26 extern char * sys_errlist[];
28 if ((0 <= errno) && (errno < sys_nerr))
29 return sys_errlist[errno];
30 else
31 return POPT_("unknown errno");
33 #endif
35 void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
36 if (con->execPath) free(con->execPath);
37 con->execPath = strdup(path);
38 con->execAbsolute = allowAbsolute;
41 static void invokeCallbacks(poptContext con, const struct poptOption * table,
42 int post) {
43 const struct poptOption * opt = table;
44 poptCallbackType cb;
46 while (opt->longName || opt->shortName || opt->arg) {
47 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
48 invokeCallbacks(con, opt->arg, post);
49 } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
50 ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
51 ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
52 cb = (poptCallbackType)opt->arg;
53 cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
54 NULL, NULL, opt->descrip);
56 opt++;
60 poptContext poptGetContext(char * name, int argc, char ** argv,
61 const struct poptOption * options, int flags) {
62 poptContext con = malloc(sizeof(*con));
64 memset(con, 0, sizeof(*con));
66 con->os = con->optionStack;
67 con->os->argc = argc;
68 con->os->argv = argv;
70 if (!(flags & POPT_CONTEXT_KEEP_FIRST))
71 con->os->next = 1; /* skip argv[0] */
73 con->leftovers = malloc(sizeof(char *) * (argc + 1));
74 con->options = options;
75 con->finalArgv = malloc(sizeof(*con->finalArgv) * (argc * 2));
76 con->finalArgvAlloced = argc * 2;
77 con->flags = flags;
78 con->execAbsolute = 1;
80 if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
81 con->flags |= POPT_CONTEXT_POSIXMEHARDER;
83 if (name)
84 con->appName = strcpy(malloc(strlen(name) + 1), name);
86 invokeCallbacks(con, con->options, 0);
88 return con;
91 void poptResetContext(poptContext con) {
92 int i;
94 con->os = con->optionStack;
95 con->os->currAlias = NULL;
96 con->os->nextCharArg = NULL;
97 con->os->nextArg = NULL;
98 con->os->next = 1; /* skip argv[0] */
100 con->numLeftovers = 0;
101 con->nextLeftover = 0;
102 con->restLeftover = 0;
103 con->doExec = NULL;
105 for (i = 0; i < con->finalArgvCount; i++)
106 free(con->finalArgv[i]);
108 con->finalArgvCount = 0;
111 /* Only one of longName, shortName may be set at a time */
112 static int handleExec(poptContext con, char * longName, char shortName) {
113 int i;
115 i = con->numExecs - 1;
116 if (longName) {
117 while (i >= 0 && (!con->execs[i].longName ||
118 strcmp(con->execs[i].longName, longName))) i--;
119 } else {
120 while (i >= 0 &&
121 con->execs[i].shortName != shortName) i--;
124 if (i < 0) return 0;
126 if (con->flags & POPT_CONTEXT_NO_EXEC)
127 return 1;
129 if (!con->doExec) {
130 con->doExec = con->execs + i;
131 return 1;
134 /* We already have an exec to do; remember this option for next
135 time 'round */
136 if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
137 con->finalArgvAlloced += 10;
138 con->finalArgv = realloc(con->finalArgv,
139 sizeof(*con->finalArgv) * con->finalArgvAlloced);
142 i = con->finalArgvCount++;
143 con->finalArgv[i] = malloc((longName ? strlen(longName) : 0) + 3);
144 if (longName)
145 sprintf(con->finalArgv[i], "--%s", longName);
146 else
147 sprintf(con->finalArgv[i], "-%c", shortName);
149 return 1;
152 /* Only one of longName, shortName may be set at a time */
153 static int handleAlias(poptContext con, char * longName, char shortName,
154 char * nextCharArg) {
155 int i;
157 if (con->os->currAlias && con->os->currAlias->longName && longName &&
158 !strcmp(con->os->currAlias->longName, longName))
159 return 0;
160 if (con->os->currAlias && shortName &&
161 shortName == con->os->currAlias->shortName)
162 return 0;
164 i = con->numAliases - 1;
165 if (longName) {
166 while (i >= 0 && (!con->aliases[i].longName ||
167 strcmp(con->aliases[i].longName, longName))) i--;
168 } else {
169 while (i >= 0 &&
170 con->aliases[i].shortName != shortName) i--;
173 if (i < 0) return 0;
175 if ((con->os - con->optionStack + 1)
176 == POPT_OPTION_DEPTH)
177 return POPT_ERROR_OPTSTOODEEP;
179 if (nextCharArg && *nextCharArg)
180 con->os->nextCharArg = nextCharArg;
182 con->os++;
183 con->os->next = 0;
184 con->os->stuffed = 0;
185 con->os->nextArg = con->os->nextCharArg = NULL;
186 con->os->currAlias = con->aliases + i;
187 con->os->argc = con->os->currAlias->argc;
188 con->os->argv = con->os->currAlias->argv;
190 return 1;
193 static void execCommand(poptContext con) {
194 char ** argv;
195 int pos = 0;
196 char * script = con->doExec->script;
198 argv = malloc(sizeof(*argv) *
199 (6 + con->numLeftovers + con->finalArgvCount));
201 if (!con->execAbsolute && strchr(script, '/')) return;
203 if (!strchr(script, '/') && con->execPath) {
204 argv[pos] = alloca(strlen(con->execPath) + strlen(script) + 2);
205 sprintf(argv[pos], "%s/%s", con->execPath, script);
206 } else {
207 argv[pos] = script;
209 pos++;
211 argv[pos] = findProgramPath(con->os->argv[0]);
212 if (argv[pos]) pos++;
213 argv[pos++] = ";";
215 memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
216 pos += con->finalArgvCount;
218 if (con->numLeftovers) {
219 argv[pos++] = "--";
220 memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
221 pos += con->numLeftovers;
224 argv[pos++] = NULL;
226 #ifdef __hpux
227 setresuid(getuid(), getuid(),-1);
228 #else
230 * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
231 * XXX sez' Timur Bakeyev <mc@bat.ru>
232 * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
234 #if defined(HAVE_SETUID)
235 setuid(getuid());
236 #elif defined (HAVE_SETREUID)
237 setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
238 #else
239 ; /* Can't drop privileges */
240 #endif
241 #endif
243 execvp(argv[0], argv);
246 static const struct poptOption * findOption(const struct poptOption * table,
247 const char * longName,
248 char shortName,
249 poptCallbackType * callback,
250 void ** callbackData,
251 int singleDash) {
252 const struct poptOption * opt = table;
253 const struct poptOption * opt2;
254 const struct poptOption * cb = NULL;
256 /* This happens when a single - is given */
257 if (singleDash && !shortName && !*longName)
258 shortName = '-';
260 while (opt->longName || opt->shortName || opt->arg) {
261 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
262 opt2 = findOption(opt->arg, longName, shortName, callback,
263 callbackData, singleDash);
264 if (opt2) {
265 if (*callback && !*callbackData)
266 *callbackData = opt->descrip;
267 return opt2;
269 } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
270 cb = opt;
271 } else if (longName && opt->longName &&
272 (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
273 !strcmp(longName, opt->longName)) {
274 break;
275 } else if (shortName && shortName == opt->shortName) {
276 break;
278 opt++;
281 if (!opt->longName && !opt->shortName) return NULL;
282 *callbackData = NULL;
283 *callback = NULL;
284 if (cb) {
285 *callback = (poptCallbackType)cb->arg;
286 if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
287 *callbackData = cb->descrip;
290 return opt;
293 /* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
294 int poptGetNextOpt(poptContext con) {
295 char * optString, * chptr, * localOptString;
296 char * longArg = NULL;
297 char * origOptString;
298 long aLong;
299 char * end;
300 const struct poptOption * opt = NULL;
301 int done = 0;
302 int i;
303 poptCallbackType cb;
304 void * cbData;
305 int singleDash;
307 while (!done) {
308 while (!con->os->nextCharArg && con->os->next == con->os->argc
309 && con->os > con->optionStack)
310 con->os--;
311 if (!con->os->nextCharArg && con->os->next == con->os->argc) {
312 invokeCallbacks(con, con->options, 1);
313 if (con->doExec) execCommand(con);
314 return -1;
317 if (!con->os->nextCharArg) {
319 origOptString = con->os->argv[con->os->next++];
321 if (con->restLeftover || *origOptString != '-') {
322 con->leftovers[con->numLeftovers++] = origOptString;
323 if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
324 con->restLeftover = 1;
325 continue;
328 /* Make a copy we can hack at */
329 localOptString = optString =
330 strcpy(alloca(strlen(origOptString) + 1),
331 origOptString);
333 if (!optString[0])
334 return POPT_ERROR_BADOPT;
336 if (optString[1] == '-' && !optString[2]) {
337 con->restLeftover = 1;
338 continue;
339 } else {
340 optString++;
341 if (*optString == '-')
342 singleDash = 0, optString++;
343 else
344 singleDash = 1;
346 if (handleAlias(con, optString, '\0', NULL))
347 continue;
348 if (handleExec(con, optString, '\0'))
349 continue;
351 chptr = optString;
352 while (*chptr && *chptr != '=') chptr++;
353 if (*chptr == '=') {
354 longArg = origOptString + (chptr - localOptString) + 1;
355 *chptr = '\0';
358 opt = findOption(con->options, optString, '\0', &cb, &cbData,
359 singleDash);
360 if (!opt && !singleDash) return POPT_ERROR_BADOPT;
363 if (!opt)
364 con->os->nextCharArg = origOptString + 1;
367 if (con->os->nextCharArg) {
368 origOptString = con->os->nextCharArg;
370 con->os->nextCharArg = NULL;
372 if (handleAlias(con, NULL, *origOptString,
373 origOptString + 1)) {
374 origOptString++;
375 continue;
377 if (handleExec(con, NULL, *origOptString))
378 continue;
380 opt = findOption(con->options, NULL, *origOptString, &cb,
381 &cbData, 0);
382 if (!opt) return POPT_ERROR_BADOPT;
384 origOptString++;
385 if (*origOptString)
386 con->os->nextCharArg = origOptString;
389 if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
390 *((int *)opt->arg) = 1;
391 } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
392 if (opt->arg) *((int *) opt->arg) = opt->val;
393 } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
394 if (longArg) {
395 con->os->nextArg = longArg;
396 } else if (con->os->nextCharArg) {
397 con->os->nextArg = con->os->nextCharArg;
398 con->os->nextCharArg = NULL;
399 } else {
400 while (con->os->next == con->os->argc &&
401 con->os > con->optionStack)
402 con->os--;
403 if (con->os->next == con->os->argc)
404 return POPT_ERROR_NOARG;
406 con->os->nextArg = con->os->argv[con->os->next++];
409 if (opt->arg) {
410 switch (opt->argInfo & POPT_ARG_MASK) {
411 case POPT_ARG_STRING:
412 *((char **) opt->arg) = con->os->nextArg;
413 break;
415 case POPT_ARG_INT:
416 case POPT_ARG_LONG:
417 aLong = strtol(con->os->nextArg, &end, 0);
418 if (!(end && *end == '\0'))
419 return POPT_ERROR_BADNUMBER;
421 if (aLong == LONG_MIN || aLong == LONG_MAX)
422 return POPT_ERROR_OVERFLOW;
423 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
424 *((long *) opt->arg) = aLong;
425 } else {
426 if (aLong > INT_MAX || aLong < INT_MIN)
427 return POPT_ERROR_OVERFLOW;
428 *((int *) opt->arg) =aLong;
430 break;
432 default:
433 fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
434 opt->argInfo & POPT_ARG_MASK);
435 exit(1);
440 if (cb)
441 cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
442 else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
443 done = 1;
445 if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
446 con->finalArgvAlloced += 10;
447 con->finalArgv = realloc(con->finalArgv,
448 sizeof(*con->finalArgv) * con->finalArgvAlloced);
451 i = con->finalArgvCount++;
452 con->finalArgv[i] =
453 malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
454 if (opt->longName)
455 sprintf(con->finalArgv[i], "--%s", opt->longName);
456 else
457 sprintf(con->finalArgv[i], "-%c", opt->shortName);
459 if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
460 && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
461 con->finalArgv[con->finalArgvCount++] = strdup(con->os->nextArg);
464 return opt->val;
467 char * poptGetOptArg(poptContext con) {
468 char * ret = con->os->nextArg;
469 con->os->nextArg = NULL;
470 return ret;
473 char * poptGetArg(poptContext con) {
474 if (con->numLeftovers == con->nextLeftover) return NULL;
475 return (con->leftovers[con->nextLeftover++]);
478 char * poptPeekArg(poptContext con) {
479 if (con->numLeftovers == con->nextLeftover) return NULL;
480 return (con->leftovers[con->nextLeftover]);
483 char ** poptGetArgs(poptContext con) {
484 if (con->numLeftovers == con->nextLeftover) return NULL;
486 /* some apps like [like RPM ;-) ] need this NULL terminated */
487 con->leftovers[con->numLeftovers] = NULL;
489 return (con->leftovers + con->nextLeftover);
492 void poptFreeContext(poptContext con) {
493 int i;
495 for (i = 0; i < con->numAliases; i++) {
496 if (con->aliases[i].longName) free(con->aliases[i].longName);
497 free(con->aliases[i].argv);
500 for (i = 0; i < con->numExecs; i++) {
501 if (con->execs[i].longName) free(con->execs[i].longName);
502 free(con->execs[i].script);
505 for (i = 0; i < con->finalArgvCount; i++)
506 free(con->finalArgv[i]);
508 free(con->leftovers);
509 free(con->finalArgv);
510 if (con->appName) free(con->appName);
511 if (con->aliases) free(con->aliases);
512 if (con->otherHelp) free(con->otherHelp);
513 if (con->execPath) free(con->execPath);
514 free(con);
517 int poptAddAlias(poptContext con, struct poptAlias newAlias, int flags) {
518 int aliasNum = con->numAliases++;
519 struct poptAlias * alias;
521 /* SunOS won't realloc(NULL, ...) */
522 if (!con->aliases)
523 con->aliases = malloc(sizeof(newAlias) * con->numAliases);
524 else
525 con->aliases = realloc(con->aliases,
526 sizeof(newAlias) * con->numAliases);
527 alias = con->aliases + aliasNum;
529 *alias = newAlias;
530 if (alias->longName)
531 alias->longName = strcpy(malloc(strlen(alias->longName) + 1),
532 alias->longName);
533 else
534 alias->longName = NULL;
536 return 0;
539 char * poptBadOption(poptContext con, int flags) {
540 struct optionStackEntry * os;
542 if (flags & POPT_BADOPTION_NOALIAS)
543 os = con->optionStack;
544 else
545 os = con->os;
547 return os->argv[os->next - 1];
550 #define POPT_ERROR_NOARG -10
551 #define POPT_ERROR_BADOPT -11
552 #define POPT_ERROR_OPTSTOODEEP -13
553 #define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
554 #define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
556 const char * poptStrerror(const int error) {
557 switch (error) {
558 case POPT_ERROR_NOARG:
559 return POPT_("missing argument");
560 case POPT_ERROR_BADOPT:
561 return POPT_("unknown option");
562 case POPT_ERROR_OPTSTOODEEP:
563 return POPT_("aliases nested too deeply");
564 case POPT_ERROR_BADQUOTE:
565 return POPT_("error in paramter quoting");
566 case POPT_ERROR_BADNUMBER:
567 return POPT_("invalid numeric value");
568 case POPT_ERROR_OVERFLOW:
569 return POPT_("number too large or too small");
570 case POPT_ERROR_ERRNO:
571 return strerror(errno);
572 default:
573 return POPT_("unknown error");
577 int poptStuffArgs(poptContext con, char ** argv) {
578 int i;
580 if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
581 return POPT_ERROR_OPTSTOODEEP;
583 for (i = 0; argv[i]; i++);
585 con->os++;
586 con->os->next = 0;
587 con->os->nextArg = con->os->nextCharArg = NULL;
588 con->os->currAlias = NULL;
589 con->os->argc = i;
590 con->os->argv = argv;
591 con->os->stuffed = 1;
593 return 0;
596 const char * poptGetInvocationName(poptContext con) {
597 return con->os->argv[0];