* color.c [USE_NCURSES]: Eliminate division by COLOR, it's 0 in
[midnight-commander.git] / src / popt.c
blob65b81b38e0ac5e6a40e35963f74757f270c8115d
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 #ifdef HAVE_UNISTD_H
18 # include <unistd.h>
19 #endif
21 #include "findme.h"
22 #include "popt.h"
23 #include "poptint.h"
25 #ifndef HAVE_STRERROR
26 static char * strerror(int errno) {
27 extern int sys_nerr;
28 extern char * sys_errlist[];
30 if ((0 <= errno) && (errno < sys_nerr))
31 return sys_errlist[errno];
32 else
33 return POPT_("unknown errno");
35 #endif
37 void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
38 if (con->execPath) free(con->execPath);
39 con->execPath = strdup(path);
40 con->execAbsolute = allowAbsolute;
43 static void invokeCallbacks(poptContext con, const struct poptOption * table,
44 int post) {
45 const struct poptOption * opt = table;
46 poptCallbackType cb;
48 while (opt->longName || opt->shortName || opt->arg) {
49 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
50 invokeCallbacks(con, opt->arg, post);
51 } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
52 ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
53 ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
54 cb = (poptCallbackType)opt->arg;
55 cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
56 NULL, NULL, opt->descrip);
58 opt++;
62 poptContext poptGetContext(char * name, int argc, char ** argv,
63 const struct poptOption * options, int flags) {
64 poptContext con = malloc(sizeof(*con));
66 memset(con, 0, sizeof(*con));
68 con->os = con->optionStack;
69 con->os->argc = argc;
70 con->os->argv = argv;
72 if (!(flags & POPT_CONTEXT_KEEP_FIRST))
73 con->os->next = 1; /* skip argv[0] */
75 con->leftovers = malloc(sizeof(char *) * (argc + 1));
76 con->options = options;
77 con->finalArgv = malloc(sizeof(*con->finalArgv) * (argc * 2));
78 con->finalArgvAlloced = argc * 2;
79 con->flags = flags;
80 con->execAbsolute = 1;
82 if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
83 con->flags |= POPT_CONTEXT_POSIXMEHARDER;
85 if (name)
86 con->appName = strcpy(malloc(strlen(name) + 1), name);
88 invokeCallbacks(con, con->options, 0);
90 return con;
93 void poptResetContext(poptContext con) {
94 int i;
96 con->os = con->optionStack;
97 con->os->currAlias = NULL;
98 con->os->nextCharArg = NULL;
99 con->os->nextArg = NULL;
100 con->os->next = 1; /* skip argv[0] */
102 con->numLeftovers = 0;
103 con->nextLeftover = 0;
104 con->restLeftover = 0;
105 con->doExec = NULL;
107 for (i = 0; i < con->finalArgvCount; i++)
108 free(con->finalArgv[i]);
110 con->finalArgvCount = 0;
113 /* Only one of longName, shortName may be set at a time */
114 static int handleExec(poptContext con, char * longName, char shortName) {
115 int i;
117 i = con->numExecs - 1;
118 if (longName) {
119 while (i >= 0 && (!con->execs[i].longName ||
120 strcmp(con->execs[i].longName, longName))) i--;
121 } else {
122 while (i >= 0 &&
123 con->execs[i].shortName != shortName) i--;
126 if (i < 0) return 0;
128 if (con->flags & POPT_CONTEXT_NO_EXEC)
129 return 1;
131 if (!con->doExec) {
132 con->doExec = con->execs + i;
133 return 1;
136 /* We already have an exec to do; remember this option for next
137 time 'round */
138 if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
139 con->finalArgvAlloced += 10;
140 con->finalArgv = realloc(con->finalArgv,
141 sizeof(*con->finalArgv) * con->finalArgvAlloced);
144 i = con->finalArgvCount++;
145 con->finalArgv[i] = malloc((longName ? strlen(longName) : 0) + 3);
146 if (longName)
147 sprintf(con->finalArgv[i], "--%s", longName);
148 else
149 sprintf(con->finalArgv[i], "-%c", shortName);
151 return 1;
154 /* Only one of longName, shortName may be set at a time */
155 static int handleAlias(poptContext con, char * longName, char shortName,
156 char * nextCharArg) {
157 int i;
159 if (con->os->currAlias && con->os->currAlias->longName && longName &&
160 !strcmp(con->os->currAlias->longName, longName))
161 return 0;
162 if (con->os->currAlias && shortName &&
163 shortName == con->os->currAlias->shortName)
164 return 0;
166 i = con->numAliases - 1;
167 if (longName) {
168 while (i >= 0 && (!con->aliases[i].longName ||
169 strcmp(con->aliases[i].longName, longName))) i--;
170 } else {
171 while (i >= 0 &&
172 con->aliases[i].shortName != shortName) i--;
175 if (i < 0) return 0;
177 if ((con->os - con->optionStack + 1)
178 == POPT_OPTION_DEPTH)
179 return POPT_ERROR_OPTSTOODEEP;
181 if (nextCharArg && *nextCharArg)
182 con->os->nextCharArg = nextCharArg;
184 con->os++;
185 con->os->next = 0;
186 con->os->stuffed = 0;
187 con->os->nextArg = con->os->nextCharArg = NULL;
188 con->os->currAlias = con->aliases + i;
189 con->os->argc = con->os->currAlias->argc;
190 con->os->argv = con->os->currAlias->argv;
192 return 1;
195 static void execCommand(poptContext con) {
196 char ** argv;
197 int pos = 0;
198 char * script = con->doExec->script;
200 argv = malloc(sizeof(*argv) *
201 (6 + con->numLeftovers + con->finalArgvCount));
203 if (!con->execAbsolute && strchr(script, '/')) return;
205 if (!strchr(script, '/') && con->execPath) {
206 argv[pos] = alloca(strlen(con->execPath) + strlen(script) + 2);
207 sprintf(argv[pos], "%s/%s", con->execPath, script);
208 } else {
209 argv[pos] = script;
211 pos++;
213 argv[pos] = findProgramPath(con->os->argv[0]);
214 if (argv[pos]) pos++;
215 argv[pos++] = ";";
217 memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
218 pos += con->finalArgvCount;
220 if (con->numLeftovers) {
221 argv[pos++] = "--";
222 memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
223 pos += con->numLeftovers;
226 argv[pos++] = NULL;
228 #ifdef __hpux
229 setresuid(getuid(), getuid(),-1);
230 #else
232 * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
233 * XXX sez' Timur Bakeyev <mc@bat.ru>
234 * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
236 #if defined(HAVE_SETUID)
237 setuid(getuid());
238 #elif defined (HAVE_SETREUID)
239 setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
240 #else
241 ; /* Cannot drop privileges */
242 #endif
243 #endif
245 execvp(argv[0], argv);
248 static const struct poptOption * findOption(const struct poptOption * table,
249 const char * longName,
250 char shortName,
251 poptCallbackType * callback,
252 void ** callbackData,
253 int singleDash) {
254 const struct poptOption * opt = table;
255 const struct poptOption * opt2;
256 const struct poptOption * cb = NULL;
258 /* This happens when a single - is given */
259 if (singleDash && !shortName && !*longName)
260 shortName = '-';
262 while (opt->longName || opt->shortName || opt->arg) {
263 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
264 opt2 = findOption(opt->arg, longName, shortName, callback,
265 callbackData, singleDash);
266 if (opt2) {
267 if (*callback && !*callbackData)
268 *callbackData = opt->descrip;
269 return opt2;
271 } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
272 cb = opt;
273 } else if (longName && opt->longName &&
274 (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
275 !strcmp(longName, opt->longName)) {
276 break;
277 } else if (shortName && shortName == opt->shortName) {
278 break;
280 opt++;
283 if (!opt->longName && !opt->shortName) return NULL;
284 *callbackData = NULL;
285 *callback = NULL;
286 if (cb) {
287 *callback = (poptCallbackType)cb->arg;
288 if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
289 *callbackData = cb->descrip;
292 return opt;
295 /* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
296 int poptGetNextOpt(poptContext con) {
297 char * optString, * chptr, * localOptString;
298 char * longArg = NULL;
299 char * origOptString;
300 long aLong;
301 char * end;
302 const struct poptOption * opt = NULL;
303 int done = 0;
304 int i;
305 poptCallbackType cb;
306 void * cbData;
307 int singleDash;
309 while (!done) {
310 while (!con->os->nextCharArg && con->os->next == con->os->argc
311 && con->os > con->optionStack)
312 con->os--;
313 if (!con->os->nextCharArg && con->os->next == con->os->argc) {
314 invokeCallbacks(con, con->options, 1);
315 if (con->doExec) execCommand(con);
316 return -1;
319 if (!con->os->nextCharArg) {
321 origOptString = con->os->argv[con->os->next++];
323 if (con->restLeftover || *origOptString != '-') {
324 con->leftovers[con->numLeftovers++] = origOptString;
325 if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
326 con->restLeftover = 1;
327 continue;
330 /* Make a copy we can hack at */
331 localOptString = optString =
332 strcpy(alloca(strlen(origOptString) + 1),
333 origOptString);
335 if (!optString[0])
336 return POPT_ERROR_BADOPT;
338 if (optString[1] == '-' && !optString[2]) {
339 con->restLeftover = 1;
340 continue;
341 } else {
342 optString++;
343 if (*optString == '-')
344 singleDash = 0, optString++;
345 else
346 singleDash = 1;
348 if (handleAlias(con, optString, '\0', NULL))
349 continue;
350 if (handleExec(con, optString, '\0'))
351 continue;
353 chptr = optString;
354 while (*chptr && *chptr != '=') chptr++;
355 if (*chptr == '=') {
356 longArg = origOptString + (chptr - localOptString) + 1;
357 *chptr = '\0';
360 opt = findOption(con->options, optString, '\0', &cb, &cbData,
361 singleDash);
362 if (!opt && !singleDash) return POPT_ERROR_BADOPT;
365 if (!opt)
366 con->os->nextCharArg = origOptString + 1;
369 if (con->os->nextCharArg) {
370 origOptString = con->os->nextCharArg;
372 con->os->nextCharArg = NULL;
374 if (handleAlias(con, NULL, *origOptString,
375 origOptString + 1)) {
376 origOptString++;
377 continue;
379 if (handleExec(con, NULL, *origOptString))
380 continue;
382 opt = findOption(con->options, NULL, *origOptString, &cb,
383 &cbData, 0);
384 if (!opt) return POPT_ERROR_BADOPT;
386 origOptString++;
387 if (*origOptString)
388 con->os->nextCharArg = origOptString;
391 if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
392 *((int *)opt->arg) = 1;
393 } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
394 if (opt->arg) *((int *) opt->arg) = opt->val;
395 } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
396 if (longArg) {
397 con->os->nextArg = longArg;
398 } else if (con->os->nextCharArg) {
399 con->os->nextArg = con->os->nextCharArg;
400 con->os->nextCharArg = NULL;
401 } else {
402 while (con->os->next == con->os->argc &&
403 con->os > con->optionStack)
404 con->os--;
405 if (con->os->next == con->os->argc)
406 return POPT_ERROR_NOARG;
408 con->os->nextArg = con->os->argv[con->os->next++];
411 if (opt->arg) {
412 switch (opt->argInfo & POPT_ARG_MASK) {
413 case POPT_ARG_STRING:
414 *((char **) opt->arg) = con->os->nextArg;
415 break;
417 case POPT_ARG_INT:
418 case POPT_ARG_LONG:
419 aLong = strtol(con->os->nextArg, &end, 0);
420 if (!(end && *end == '\0'))
421 return POPT_ERROR_BADNUMBER;
423 if (aLong == LONG_MIN || aLong == LONG_MAX)
424 return POPT_ERROR_OVERFLOW;
425 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
426 *((long *) opt->arg) = aLong;
427 } else {
428 if (aLong > INT_MAX || aLong < INT_MIN)
429 return POPT_ERROR_OVERFLOW;
430 *((int *) opt->arg) =aLong;
432 break;
434 default:
435 fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
436 opt->argInfo & POPT_ARG_MASK);
437 exit(1);
442 if (cb)
443 cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
444 else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
445 done = 1;
447 if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
448 con->finalArgvAlloced += 10;
449 con->finalArgv = realloc(con->finalArgv,
450 sizeof(*con->finalArgv) * con->finalArgvAlloced);
453 i = con->finalArgvCount++;
454 con->finalArgv[i] =
455 malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
456 if (opt->longName)
457 sprintf(con->finalArgv[i], "--%s", opt->longName);
458 else
459 sprintf(con->finalArgv[i], "-%c", opt->shortName);
461 if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
462 && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
463 con->finalArgv[con->finalArgvCount++] = strdup(con->os->nextArg);
466 return opt->val;
469 char * poptGetOptArg(poptContext con) {
470 char * ret = con->os->nextArg;
471 con->os->nextArg = NULL;
472 return ret;
475 char * poptGetArg(poptContext con) {
476 if (con->numLeftovers == con->nextLeftover) return NULL;
477 return (con->leftovers[con->nextLeftover++]);
480 char * poptPeekArg(poptContext con) {
481 if (con->numLeftovers == con->nextLeftover) return NULL;
482 return (con->leftovers[con->nextLeftover]);
485 char ** poptGetArgs(poptContext con) {
486 if (con->numLeftovers == con->nextLeftover) return NULL;
488 /* some apps like [like RPM ;-) ] need this NULL terminated */
489 con->leftovers[con->numLeftovers] = NULL;
491 return (con->leftovers + con->nextLeftover);
494 void poptFreeContext(poptContext con) {
495 int i;
497 for (i = 0; i < con->numAliases; i++) {
498 if (con->aliases[i].longName) free(con->aliases[i].longName);
499 free(con->aliases[i].argv);
502 for (i = 0; i < con->numExecs; i++) {
503 if (con->execs[i].longName) free(con->execs[i].longName);
504 free(con->execs[i].script);
507 for (i = 0; i < con->finalArgvCount; i++)
508 free(con->finalArgv[i]);
510 free(con->leftovers);
511 free(con->finalArgv);
512 if (con->appName) free(con->appName);
513 if (con->aliases) free(con->aliases);
514 if (con->otherHelp) free(con->otherHelp);
515 if (con->execPath) free(con->execPath);
516 free(con);
519 int poptAddAlias(poptContext con, struct poptAlias newAlias, int flags) {
520 int aliasNum = con->numAliases++;
521 struct poptAlias * alias;
523 /* SunOS won't realloc(NULL, ...) */
524 if (!con->aliases)
525 con->aliases = malloc(sizeof(newAlias) * con->numAliases);
526 else
527 con->aliases = realloc(con->aliases,
528 sizeof(newAlias) * con->numAliases);
529 alias = con->aliases + aliasNum;
531 *alias = newAlias;
532 if (alias->longName)
533 alias->longName = strcpy(malloc(strlen(alias->longName) + 1),
534 alias->longName);
535 else
536 alias->longName = NULL;
538 return 0;
541 char * poptBadOption(poptContext con, int flags) {
542 struct optionStackEntry * os;
544 if (flags & POPT_BADOPTION_NOALIAS)
545 os = con->optionStack;
546 else
547 os = con->os;
549 return os->argv[os->next - 1];
552 #define POPT_ERROR_NOARG -10
553 #define POPT_ERROR_BADOPT -11
554 #define POPT_ERROR_OPTSTOODEEP -13
555 #define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
556 #define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
558 const char * poptStrerror(const int error) {
559 switch (error) {
560 case POPT_ERROR_NOARG:
561 return POPT_("missing argument");
562 case POPT_ERROR_BADOPT:
563 return POPT_("unknown option");
564 case POPT_ERROR_OPTSTOODEEP:
565 return POPT_("aliases nested too deeply");
566 case POPT_ERROR_BADQUOTE:
567 return POPT_("error in paramter quoting");
568 case POPT_ERROR_BADNUMBER:
569 return POPT_("invalid numeric value");
570 case POPT_ERROR_OVERFLOW:
571 return POPT_("number too large or too small");
572 case POPT_ERROR_ERRNO:
573 return strerror(errno);
574 default:
575 return POPT_("unknown error");
579 int poptStuffArgs(poptContext con, char ** argv) {
580 int i;
582 if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
583 return POPT_ERROR_OPTSTOODEEP;
585 for (i = 0; argv[i]; i++);
587 con->os++;
588 con->os->next = 0;
589 con->os->nextArg = con->os->nextCharArg = NULL;
590 con->os->currAlias = NULL;
591 con->os->argc = i;
592 con->os->argv = argv;
593 con->os->stuffed = 1;
595 return 0;
598 const char * poptGetInvocationName(poptContext con) {
599 return con->os->argv[0];