Commit | Line | Data |
---|---|---|
62402cb1 MP |
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 */ | |
4 | ||
5 | #ifdef HAVE_CONFIG_H | |
6 | #include "config.h" | |
7 | #endif | |
8 | ||
9 | #include <errno.h> | |
10 | #include <ctype.h> | |
11 | #include <fcntl.h> | |
12 | #include <limits.h> | |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <string.h> | |
16 | #include <unistd.h> | |
17 | ||
18 | #if HAVE_ALLOCA_H | |
19 | # include <alloca.h> | |
20 | #endif | |
21 | ||
22 | #include "findme.h" | |
23 | #include "popt.h" | |
24 | #include "poptint.h" | |
25 | ||
26 | #ifndef HAVE_STRERROR | |
27 | static char * strerror(int errno) { | |
28 | extern int sys_nerr; | |
29 | extern char * sys_errlist[]; | |
30 | ||
31 | if ((0 <= errno) && (errno < sys_nerr)) | |
32 | return sys_errlist[errno]; | |
33 | else | |
34 | return POPT_("unknown errno"); | |
35 | } | |
36 | #endif | |
37 | ||
38 | void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) { | |
39 | if (con->execPath) free(con->execPath); | |
40 | con->execPath = strdup(path); | |
41 | con->execAbsolute = allowAbsolute; | |
42 | } | |
43 | ||
44 | static void invokeCallbacks(poptContext con, const struct poptOption * table, | |
45 | int post) { | |
46 | const struct poptOption * opt = table; | |
47 | poptCallbackType cb; | |
48 | ||
49 | while (opt->longName || opt->shortName || opt->arg) { | |
50 | if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { | |
51 | invokeCallbacks(con, opt->arg, post); | |
52 | } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) && | |
53 | ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) || | |
54 | ( post && (opt->argInfo & POPT_CBFLAG_POST)))) { | |
55 | cb = opt->arg; | |
56 | cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE, | |
57 | NULL, NULL, opt->descrip); | |
58 | } | |
59 | opt++; | |
60 | } | |
61 | } | |
62 | ||
63 | poptContext poptGetContext(char * name, int argc, char ** argv, | |
64 | const struct poptOption * options, int flags) { | |
65 | poptContext con = malloc(sizeof(*con)); | |
66 | ||
67 | memset(con, 0, sizeof(*con)); | |
68 | ||
69 | con->os = con->optionStack; | |
70 | con->os->argc = argc; | |
71 | con->os->argv = argv; | |
72 | ||
73 | if (!(flags & POPT_CONTEXT_KEEP_FIRST)) | |
74 | con->os->next = 1; /* skip argv[0] */ | |
75 | ||
76 | con->leftovers = malloc(sizeof(char *) * (argc + 1)); | |
77 | con->options = options; | |
78 | con->finalArgv = malloc(sizeof(*con->finalArgv) * (argc * 2)); | |
79 | con->finalArgvAlloced = argc * 2; | |
80 | con->flags = flags; | |
81 | con->execAbsolute = 1; | |
82 | ||
83 | if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER")) | |
84 | con->flags |= POPT_CONTEXT_POSIXMEHARDER; | |
85 | ||
86 | if (name) | |
87 | con->appName = strcpy(malloc(strlen(name) + 1), name); | |
88 | ||
89 | invokeCallbacks(con, con->options, 0); | |
90 | ||
91 | return con; | |
92 | } | |
93 | ||
94 | void poptResetContext(poptContext con) { | |
95 | con->os = con->optionStack; | |
96 | con->os->currAlias = NULL; | |
97 | con->os->nextCharArg = NULL; | |
98 | con->os->nextArg = NULL; | |
99 | con->os->next = 1; /* skip argv[0] */ | |
100 | ||
101 | con->numLeftovers = 0; | |
102 | con->nextLeftover = 0; | |
103 | con->restLeftover = 0; | |
104 | con->doExec = NULL; | |
105 | con->finalArgvCount = 0; | |
106 | } | |
107 | ||
108 | /* Only one of longName, shortName may be set at a time */ | |
109 | static int handleExec(poptContext con, char * longName, char shortName) { | |
110 | int i; | |
111 | ||
112 | i = con->numExecs - 1; | |
113 | if (longName) { | |
114 | while (i >= 0 && (!con->execs[i].longName || | |
115 | strcmp(con->execs[i].longName, longName))) i--; | |
116 | } else { | |
117 | while (i >= 0 && | |
118 | con->execs[i].shortName != shortName) i--; | |
119 | } | |
120 | ||
121 | if (i < 0) return 0; | |
122 | ||
123 | if (con->flags & POPT_CONTEXT_NO_EXEC) | |
124 | return 1; | |
125 | ||
126 | if (!con->doExec) { | |
127 | con->doExec = con->execs + i; | |
128 | return 1; | |
129 | } | |
130 | ||
131 | /* We already have an exec to do; remember this option for next | |
132 | time 'round */ | |
133 | if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) { | |
134 | con->finalArgvAlloced += 10; | |
135 | con->finalArgv = realloc(con->finalArgv, | |
136 | sizeof(*con->finalArgv) * con->finalArgvAlloced); | |
137 | } | |
138 | ||
139 | i = con->finalArgvCount++; | |
140 | con->finalArgv[i] = malloc((longName ? strlen(longName) : 0) + 3); | |
141 | if (longName) | |
142 | sprintf(con->finalArgv[i], "--%s", longName); | |
143 | else | |
144 | sprintf(con->finalArgv[i], "-%c", shortName); | |
145 | ||
146 | return 1; | |
147 | } | |
148 | ||
149 | /* Only one of longName, shortName may be set at a time */ | |
150 | static int handleAlias(poptContext con, char * longName, char shortName, | |
151 | char * nextCharArg) { | |
152 | int i; | |
153 | ||
154 | if (con->os->currAlias && con->os->currAlias->longName && longName && | |
155 | !strcmp(con->os->currAlias->longName, longName)) | |
156 | return 0; | |
157 | if (con->os->currAlias && shortName == con->os->currAlias->shortName) | |
158 | return 0; | |
159 | ||
160 | i = con->numAliases - 1; | |
161 | if (longName) { | |
162 | while (i >= 0 && (!con->aliases[i].longName || | |
163 | strcmp(con->aliases[i].longName, longName))) i--; | |
164 | } else { | |
165 | while (i >= 0 && | |
166 | con->aliases[i].shortName != shortName) i--; | |
167 | } | |
168 | ||
169 | if (i < 0) return 0; | |
170 | ||
171 | if ((con->os - con->optionStack + 1) | |
172 | == POPT_OPTION_DEPTH) | |
173 | return POPT_ERROR_OPTSTOODEEP; | |
174 | ||
175 | if (nextCharArg && *nextCharArg) | |
176 | con->os->nextCharArg = nextCharArg; | |
177 | ||
178 | con->os++; | |
179 | con->os->next = 0; | |
180 | con->os->stuffed = 0; | |
181 | con->os->nextArg = con->os->nextCharArg = NULL; | |
182 | con->os->currAlias = con->aliases + i; | |
183 | con->os->argc = con->os->currAlias->argc; | |
184 | con->os->argv = con->os->currAlias->argv; | |
185 | ||
186 | return 1; | |
187 | } | |
188 | ||
189 | static void execCommand(poptContext con) { | |
190 | char ** argv; | |
191 | int pos = 0; | |
192 | char * script = con->doExec->script; | |
193 | ||
194 | argv = malloc(sizeof(*argv) * | |
195 | (6 + con->numLeftovers + con->finalArgvCount)); | |
196 | ||
197 | if (!con->execAbsolute && strchr(script, '/')) return; | |
198 | ||
199 | if (!strchr(script, '/') && con->execPath) { | |
200 | argv[pos] = alloca(strlen(con->execPath) + strlen(script) + 2); | |
201 | sprintf(argv[pos], "%s/%s", con->execPath, script); | |
202 | } else { | |
203 | argv[pos] = script; | |
204 | } | |
205 | pos++; | |
206 | ||
207 | argv[pos] = findProgramPath(con->os->argv[0]); | |
208 | if (argv[pos]) pos++; | |
209 | argv[pos++] = ";"; | |
210 | ||
211 | memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount); | |
212 | pos += con->finalArgvCount; | |
213 | ||
214 | if (con->numLeftovers) { | |
215 | argv[pos++] = "--"; | |
216 | memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers); | |
217 | pos += con->numLeftovers; | |
218 | } | |
219 | ||
220 | argv[pos++] = NULL; | |
221 | ||
222 | #ifdef __hpux | |
223 | setresuid(getuid(), getuid(),-1); | |
224 | #else | |
225 | setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */ | |
226 | #endif | |
227 | ||
228 | execvp(argv[0], argv); | |
229 | } | |
230 | ||
231 | static const struct poptOption * findOption(const struct poptOption * table, | |
232 | const char * longName, | |
233 | const char shortName, | |
234 | poptCallbackType * callback, | |
235 | void ** callbackData, | |
236 | int singleDash) { | |
237 | const struct poptOption * opt = table; | |
238 | const struct poptOption * opt2; | |
239 | const struct poptOption * cb = NULL; | |
240 | ||
241 | while (opt->longName || opt->shortName || opt->arg) { | |
242 | if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { | |
243 | opt2 = findOption(opt->arg, longName, shortName, callback, | |
244 | callbackData, singleDash); | |
245 | if (opt2) { | |
246 | if (*callback && !*callbackData) | |
247 | *callbackData = opt->descrip; | |
248 | return opt2; | |
249 | } | |
250 | } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) { | |
251 | cb = opt; | |
252 | } else if (longName && opt->longName && | |
253 | (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) && | |
254 | !strcmp(longName, opt->longName)) { | |
255 | break; | |
256 | } else if (shortName && shortName == opt->shortName) { | |
257 | break; | |
258 | } | |
259 | opt++; | |
260 | } | |
261 | ||
262 | if (!opt->longName && !opt->shortName) return NULL; | |
263 | *callbackData = NULL; | |
264 | *callback = NULL; | |
265 | if (cb) { | |
266 | *callback = cb->arg; | |
267 | if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) | |
268 | *callbackData = cb->descrip; | |
269 | } | |
270 | ||
271 | return opt; | |
272 | } | |
273 | ||
274 | /* returns 'val' element, -1 on last item, POPT_ERROR_* on error */ | |
275 | int poptGetNextOpt(poptContext con) { | |
276 | char * optString, * chptr, * localOptString; | |
277 | char * longArg = NULL; | |
278 | char * origOptString; | |
279 | long aLong; | |
280 | char * end; | |
281 | const struct poptOption * opt = NULL; | |
282 | int done = 0; | |
283 | int i; | |
284 | poptCallbackType cb; | |
285 | void * cbData; | |
286 | int singleDash; | |
287 | ||
288 | while (!done) { | |
289 | while (!con->os->nextCharArg && con->os->next == con->os->argc | |
290 | && con->os > con->optionStack) | |
291 | con->os--; | |
292 | if (!con->os->nextCharArg && con->os->next == con->os->argc) { | |
293 | invokeCallbacks(con, con->options, 1); | |
294 | if (con->doExec) execCommand(con); | |
295 | return -1; | |
296 | } | |
297 | ||
298 | if (!con->os->nextCharArg) { | |
299 | ||
300 | origOptString = con->os->argv[con->os->next++]; | |
301 | ||
302 | if (con->restLeftover || *origOptString != '-') { | |
303 | con->leftovers[con->numLeftovers++] = origOptString; | |
304 | if (con->flags & POPT_CONTEXT_POSIXMEHARDER) | |
305 | con->restLeftover = 1; | |
306 | continue; | |
307 | } | |
308 | ||
309 | /* Make a copy we can hack at */ | |
310 | localOptString = optString = | |
311 | strcpy(alloca(strlen(origOptString) + 1), | |
312 | origOptString); | |
313 | ||
314 | if (!optString[0]) | |
315 | return POPT_ERROR_BADOPT; | |
316 | ||
317 | if (optString[1] == '-' && !optString[2]) { | |
318 | con->restLeftover = 1; | |
319 | continue; | |
320 | } else { | |
321 | optString++; | |
322 | if (*optString == '-') | |
323 | singleDash = 0, optString++; | |
324 | else | |
325 | singleDash = 1; | |
326 | ||
327 | if (handleAlias(con, optString, '\0', NULL)) | |
328 | continue; | |
329 | if (handleExec(con, optString, '\0')) | |
330 | continue; | |
331 | ||
332 | chptr = optString; | |
333 | while (*chptr && *chptr != '=') chptr++; | |
334 | if (*chptr == '=') { | |
335 | longArg = origOptString + (chptr - localOptString) + 1; | |
336 | *chptr = '\0'; | |
337 | } | |
338 | ||
339 | opt = findOption(con->options, optString, '\0', &cb, &cbData, | |
340 | singleDash); | |
341 | if (!opt && !singleDash) return POPT_ERROR_BADOPT; | |
342 | } | |
343 | ||
344 | if (!opt) | |
345 | con->os->nextCharArg = origOptString + 1; | |
346 | } | |
347 | ||
348 | if (con->os->nextCharArg) { | |
349 | origOptString = con->os->nextCharArg; | |
350 | ||
351 | con->os->nextCharArg = NULL; | |
352 | ||
353 | if (handleAlias(con, NULL, *origOptString, | |
354 | origOptString + 1)) { | |
355 | origOptString++; | |
356 | continue; | |
357 | } | |
358 | if (handleExec(con, NULL, *origOptString)) | |
359 | continue; | |
360 | ||
361 | opt = findOption(con->options, NULL, *origOptString, &cb, | |
362 | &cbData, 0); | |
363 | if (!opt) return POPT_ERROR_BADOPT; | |
364 | ||
365 | origOptString++; | |
366 | if (*origOptString) | |
367 | con->os->nextCharArg = origOptString; | |
368 | } | |
369 | ||
370 | if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) | |
371 | *((int *)opt->arg) = 1; | |
372 | else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) { | |
373 | if (longArg) { | |
374 | con->os->nextArg = longArg; | |
375 | } else if (con->os->nextCharArg) { | |
376 | con->os->nextArg = con->os->nextCharArg; | |
377 | con->os->nextCharArg = NULL; | |
378 | } else { | |
379 | while (con->os->next == con->os->argc && | |
380 | con->os > con->optionStack) | |
381 | con->os--; | |
382 | if (con->os->next == con->os->argc) | |
383 | return POPT_ERROR_NOARG; | |
384 | ||
385 | con->os->nextArg = con->os->argv[con->os->next++]; | |
386 | } | |
387 | ||
388 | if (opt->arg) { | |
389 | switch (opt->argInfo & POPT_ARG_MASK) { | |
390 | case POPT_ARG_STRING: | |
391 | *((char **) opt->arg) = con->os->nextArg; | |
392 | break; | |
393 | ||
394 | case POPT_ARG_INT: | |
395 | case POPT_ARG_LONG: | |
396 | aLong = strtol(con->os->nextArg, &end, 0); | |
397 | if (*end) | |
398 | return POPT_ERROR_BADNUMBER; | |
399 | ||
400 | if (aLong == LONG_MIN || aLong == LONG_MAX) | |
401 | return POPT_ERROR_OVERFLOW; | |
402 | if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) { | |
403 | *((long *) opt->arg) = aLong; | |
404 | } else { | |
405 | if (aLong > INT_MAX || aLong < INT_MIN) | |
406 | return POPT_ERROR_OVERFLOW; | |
407 | *((int *) opt->arg) =aLong; | |
408 | } | |
409 | break; | |
410 | ||
411 | default: | |
412 | fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"), | |
413 | opt->argInfo & POPT_ARG_MASK); | |
414 | exit(1); | |
415 | } | |
416 | } | |
417 | } | |
418 | ||
419 | if (cb) | |
420 | cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData); | |
421 | else if (opt->val) | |
422 | done = 1; | |
423 | ||
424 | if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) { | |
425 | con->finalArgvAlloced += 10; | |
426 | con->finalArgv = realloc(con->finalArgv, | |
427 | sizeof(*con->finalArgv) * con->finalArgvAlloced); | |
428 | } | |
429 | ||
430 | i = con->finalArgvCount++; | |
431 | con->finalArgv[i] = | |
432 | malloc((opt->longName ? strlen(opt->longName) : 0) + 3); | |
433 | if (opt->longName) | |
434 | sprintf(con->finalArgv[i], "--%s", opt->longName); | |
435 | else | |
436 | sprintf(con->finalArgv[i], "-%c", opt->shortName); | |
437 | ||
438 | if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) | |
439 | con->finalArgv[con->finalArgvCount++] = strdup(con->os->nextArg); | |
440 | } | |
441 | ||
442 | return opt->val; | |
443 | } | |
444 | ||
445 | char * poptGetOptArg(poptContext con) { | |
446 | char * ret = con->os->nextArg; | |
447 | con->os->nextArg = NULL; | |
448 | return ret; | |
449 | } | |
450 | ||
451 | char * poptGetArg(poptContext con) { | |
452 | if (con->numLeftovers == con->nextLeftover) return NULL; | |
453 | return (con->leftovers[con->nextLeftover++]); | |
454 | } | |
455 | ||
456 | char * poptPeekArg(poptContext con) { | |
457 | if (con->numLeftovers == con->nextLeftover) return NULL; | |
458 | return (con->leftovers[con->nextLeftover]); | |
459 | } | |
460 | ||
461 | char ** poptGetArgs(poptContext con) { | |
462 | if (con->numLeftovers == con->nextLeftover) return NULL; | |
463 | ||
464 | /* some apps like [like RPM ;-) ] need this NULL terminated */ | |
465 | con->leftovers[con->numLeftovers] = NULL; | |
466 | ||
467 | return (con->leftovers + con->nextLeftover); | |
468 | } | |
469 | ||
470 | void poptFreeContext(poptContext con) { | |
471 | int i; | |
472 | ||
473 | for (i = 0; i < con->numAliases; i++) { | |
474 | if (con->aliases[i].longName) free(con->aliases[i].longName); | |
475 | free(con->aliases[i].argv); | |
476 | } | |
477 | ||
478 | for (i = 0; i < con->numExecs; i++) { | |
479 | if (con->execs[i].longName) free(con->execs[i].longName); | |
480 | free(con->execs[i].script); | |
481 | } | |
482 | ||
483 | for (i = 0; i < con->finalArgvCount; i++) | |
484 | free(con->finalArgv[i]); | |
485 | ||
486 | free(con->leftovers); | |
487 | free(con->finalArgv); | |
488 | if (con->appName) free(con->appName); | |
489 | if (con->aliases) free(con->aliases); | |
490 | if (con->otherHelp) free(con->otherHelp); | |
491 | free(con); | |
492 | } | |
493 | ||
494 | int poptAddAlias(poptContext con, struct poptAlias newAlias, int flags) { | |
495 | int aliasNum = con->numAliases++; | |
496 | struct poptAlias * alias; | |
497 | ||
498 | /* SunOS won't realloc(NULL, ...) */ | |
499 | if (!con->aliases) | |
500 | con->aliases = malloc(sizeof(newAlias) * con->numAliases); | |
501 | else | |
502 | con->aliases = realloc(con->aliases, | |
503 | sizeof(newAlias) * con->numAliases); | |
504 | alias = con->aliases + aliasNum; | |
505 | ||
506 | *alias = newAlias; | |
507 | if (alias->longName) | |
508 | alias->longName = strcpy(malloc(strlen(alias->longName) + 1), | |
509 | alias->longName); | |
510 | else | |
511 | alias->longName = NULL; | |
512 | ||
513 | return 0; | |
514 | } | |
515 | ||
516 | char * poptBadOption(poptContext con, int flags) { | |
517 | struct optionStackEntry * os; | |
518 | ||
519 | if (flags & POPT_BADOPTION_NOALIAS) | |
520 | os = con->optionStack; | |
521 | else | |
522 | os = con->os; | |
523 | ||
524 | return os->argv[os->next - 1]; | |
525 | } | |
526 | ||
527 | #define POPT_ERROR_NOARG -10 | |
528 | #define POPT_ERROR_BADOPT -11 | |
529 | #define POPT_ERROR_OPTSTOODEEP -13 | |
530 | #define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */ | |
531 | #define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */ | |
532 | ||
533 | const char * poptStrerror(const int error) { | |
534 | switch (error) { | |
535 | case POPT_ERROR_NOARG: | |
536 | return POPT_("missing argument"); | |
537 | case POPT_ERROR_BADOPT: | |
538 | return POPT_("unknown option"); | |
539 | case POPT_ERROR_OPTSTOODEEP: | |
540 | return POPT_("aliases nested too deeply"); | |
541 | case POPT_ERROR_BADQUOTE: | |
542 | return POPT_("error in paramter quoting"); | |
543 | case POPT_ERROR_BADNUMBER: | |
544 | return POPT_("invalid numeric value"); | |
545 | case POPT_ERROR_OVERFLOW: | |
546 | return POPT_("number too large or too small"); | |
547 | case POPT_ERROR_ERRNO: | |
548 | return strerror(errno); | |
549 | default: | |
550 | return POPT_("unknown error"); | |
551 | } | |
552 | } | |
553 | ||
554 | int poptStuffArgs(poptContext con, char ** argv) { | |
555 | int i; | |
556 | ||
557 | if ((con->os - con->optionStack) == POPT_OPTION_DEPTH) | |
558 | return POPT_ERROR_OPTSTOODEEP; | |
559 | ||
560 | for (i = 0; argv[i]; i++); | |
561 | ||
562 | con->os++; | |
563 | con->os->next = 0; | |
564 | con->os->nextArg = con->os->nextCharArg = NULL; | |
565 | con->os->currAlias = NULL; | |
566 | con->os->argc = i; | |
567 | con->os->argv = argv; | |
568 | con->os->stuffed = 1; | |
569 | ||
570 | return 0; | |
571 | } | |
572 | ||
573 | const char * poptGetInvocationName(poptContext con) { | |
574 | return con->os->argv[0]; | |
575 | } |