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