s4: import lorikeet-heimdal-200810271034
[sfrench/samba-autobuild/.git] / source4 / heimdal / lib / roken / getarg.c
1 /*
2  * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 RCSID("$Id$");
37 #endif
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include "roken.h"
43 #include "getarg.h"
44
45 #define ISFLAG(X) ((X).type == arg_flag || (X).type == arg_negative_flag)
46
47 static size_t
48 print_arg (char *string,
49            size_t len,
50            int mdoc,
51            int longp,
52            struct getargs *arg,
53            char *(i18n)(const char *))
54 {
55     const char *s;
56
57     *string = '\0';
58
59     if (ISFLAG(*arg) || (!longp && arg->type == arg_counter))
60         return 0;
61
62     if(mdoc){
63         if(longp)
64             strlcat(string, "= Ns", len);
65         strlcat(string, " Ar ", len);
66     } else {
67         if (longp)
68             strlcat (string, "=", len);
69         else
70             strlcat (string, " ", len);
71     }
72
73     if (arg->arg_help)
74         s = (*i18n)(arg->arg_help);
75     else if (arg->type == arg_integer || arg->type == arg_counter)
76         s = "integer";
77     else if (arg->type == arg_string)
78         s = "string";
79     else if (arg->type == arg_strings)
80         s = "strings";
81     else if (arg->type == arg_double)
82         s = "float";
83     else
84         s = "<undefined>";
85
86     strlcat(string, s, len);
87     return 1 + strlen(s);
88 }
89
90 static void
91 mandoc_template(struct getargs *args,
92                 size_t num_args,
93                 const char *progname,
94                 const char *extra_string,
95                 char *(i18n)(const char *))
96 {
97     int i;
98     char timestr[64], cmd[64];
99     char buf[128];
100     const char *p;
101     time_t t;
102
103     printf(".\\\" Things to fix:\n");
104     printf(".\\\"   * correct section, and operating system\n");
105     printf(".\\\"   * remove Op from mandatory flags\n");
106     printf(".\\\"   * use better macros for arguments (like .Pa for files)\n");
107     printf(".\\\"\n");
108     t = time(NULL);
109     strftime(timestr, sizeof(timestr), "%B %e, %Y", localtime(&t));
110     printf(".Dd %s\n", timestr);
111     p = strrchr(progname, '/');
112     if(p) p++; else p = progname;
113     strlcpy(cmd, p, sizeof(cmd));
114     strupr(cmd);
115
116     printf(".Dt %s SECTION\n", cmd);
117     printf(".Os OPERATING_SYSTEM\n");
118     printf(".Sh NAME\n");
119     printf(".Nm %s\n", p);
120     printf(".Nd\n");
121     printf("in search of a description\n");
122     printf(".Sh SYNOPSIS\n");
123     printf(".Nm\n");
124     for(i = 0; i < num_args; i++){
125         /* we seem to hit a limit on number of arguments if doing
126            short and long flags with arguments -- split on two lines */
127         if(ISFLAG(args[i]) ||
128            args[i].short_name == 0 || args[i].long_name == NULL) {
129             printf(".Op ");
130
131             if(args[i].short_name) {
132                 print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);
133                 printf("Fl %c%s", args[i].short_name, buf);
134                 if(args[i].long_name)
135                     printf(" | ");
136             }
137             if(args[i].long_name) {
138                 print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);
139                 printf("Fl -%s%s%s",
140                        args[i].type == arg_negative_flag ? "no-" : "",
141                        args[i].long_name, buf);
142             }
143             printf("\n");
144         } else {
145             print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);
146             printf(".Oo Fl %c%s \\*(Ba Xo\n", args[i].short_name, buf);
147             print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);
148             printf(".Fl -%s%s\n.Xc\n.Oc\n", args[i].long_name, buf);
149         }
150     /*
151             if(args[i].type == arg_strings)
152                 fprintf (stderr, "...");
153                 */
154     }
155     if (extra_string && *extra_string)
156         printf (".Ar %s\n", extra_string);
157     printf(".Sh DESCRIPTION\n");
158     printf("Supported options:\n");
159     printf(".Bl -tag -width Ds\n");
160     for(i = 0; i < num_args; i++){
161         printf(".It Xo\n");
162         if(args[i].short_name){
163             printf(".Fl %c", args[i].short_name);
164             print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);
165             printf("%s", buf);
166             if(args[i].long_name)
167                 printf(" ,");
168             printf("\n");
169         }
170         if(args[i].long_name){
171             printf(".Fl -%s%s",
172                    args[i].type == arg_negative_flag ? "no-" : "",
173                    args[i].long_name);
174             print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);
175             printf("%s\n", buf);
176         }
177         printf(".Xc\n");
178         if(args[i].help)
179             printf("%s\n", args[i].help);
180     /*
181             if(args[i].type == arg_strings)
182                 fprintf (stderr, "...");
183                 */
184     }
185     printf(".El\n");
186     printf(".\\\".Sh ENVIRONMENT\n");
187     printf(".\\\".Sh FILES\n");
188     printf(".\\\".Sh EXAMPLES\n");
189     printf(".\\\".Sh DIAGNOSTICS\n");
190     printf(".\\\".Sh SEE ALSO\n");
191     printf(".\\\".Sh STANDARDS\n");
192     printf(".\\\".Sh HISTORY\n");
193     printf(".\\\".Sh AUTHORS\n");
194     printf(".\\\".Sh BUGS\n");
195 }
196
197 static int
198 check_column(FILE *f, int col, int len, int columns)
199 {
200     if(col + len > columns) {
201         fprintf(f, "\n");
202         col = fprintf(f, "  ");
203     }
204     return col;
205 }
206
207 static char *
208 builtin_i18n(const char *str)
209 {
210     return rk_UNCONST(str);
211 }
212
213 void ROKEN_LIB_FUNCTION
214 arg_printusage (struct getargs *args,
215                 size_t num_args,
216                 const char *progname,
217                 const char *extra_string)
218 {
219     return arg_printusage_i18n(args, num_args, "Usage",
220                                progname, extra_string, builtin_i18n);
221 }
222
223 void ROKEN_LIB_FUNCTION
224 arg_printusage_i18n (struct getargs *args,
225                      size_t num_args,
226                      const char *usage,
227                      const char *progname,
228                      const char *extra_string,
229                      char *(i18n)(const char *))
230 {
231     int i;
232     size_t max_len = 0;
233     char buf[128];
234     int col = 0, columns;
235     struct winsize ws;
236
237     if (progname == NULL)
238         progname = getprogname();
239
240     if (i18n == NULL)
241         i18n = builtin_i18n;
242
243     if(getenv("GETARGMANDOC")){
244         mandoc_template(args, num_args, progname, extra_string, i18n);
245         return;
246     }
247     if(get_window_size(2, &ws) == 0)
248         columns = ws.ws_col;
249     else
250         columns = 80;
251     col = 0;
252     col += fprintf (stderr, "%s: %s", usage, progname);
253     buf[0] = '\0';
254     for (i = 0; i < num_args; ++i) {
255         if(args[i].short_name && ISFLAG(args[i])) {
256             char s[2];
257             if(buf[0] == '\0')
258                 strlcpy(buf, "[-", sizeof(buf));
259             s[0] = args[i].short_name;
260             s[1] = '\0';
261             strlcat(buf, s, sizeof(buf));
262         }
263     }
264     if(buf[0] != '\0') {
265         strlcat(buf, "]", sizeof(buf));
266         col = check_column(stderr, col, strlen(buf) + 1, columns);
267         col += fprintf(stderr, " %s", buf);
268     }
269
270     for (i = 0; i < num_args; ++i) {
271         size_t len = 0;
272
273         if (args[i].long_name) {
274             buf[0] = '\0';
275             strlcat(buf, "[--", sizeof(buf));
276             len += 2;
277             if(args[i].type == arg_negative_flag) {
278                 strlcat(buf, "no-", sizeof(buf));
279                 len += 3;
280             }
281             strlcat(buf, args[i].long_name, sizeof(buf));
282             len += strlen(args[i].long_name);
283             len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
284                              0, 1, &args[i], i18n);
285             strlcat(buf, "]", sizeof(buf));
286             if(args[i].type == arg_strings)
287                 strlcat(buf, "...", sizeof(buf));
288             col = check_column(stderr, col, strlen(buf) + 1, columns);
289             col += fprintf(stderr, " %s", buf);
290         }
291         if (args[i].short_name && !ISFLAG(args[i])) {
292             snprintf(buf, sizeof(buf), "[-%c", args[i].short_name);
293             len += 2;
294             len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
295                              0, 0, &args[i], i18n);
296             strlcat(buf, "]", sizeof(buf));
297             if(args[i].type == arg_strings)
298                 strlcat(buf, "...", sizeof(buf));
299             col = check_column(stderr, col, strlen(buf) + 1, columns);
300             col += fprintf(stderr, " %s", buf);
301         }
302         if (args[i].long_name && args[i].short_name)
303             len += 2; /* ", " */
304         max_len = max(max_len, len);
305     }
306     if (extra_string) {
307         check_column(stderr, col, strlen(extra_string) + 1, columns);
308         fprintf (stderr, " %s\n", extra_string);
309     } else
310         fprintf (stderr, "\n");
311     for (i = 0; i < num_args; ++i) {
312         if (args[i].help) {
313             size_t count = 0;
314
315             if (args[i].short_name) {
316                 count += fprintf (stderr, "-%c", args[i].short_name);
317                 print_arg (buf, sizeof(buf), 0, 0, &args[i], i18n);
318                 count += fprintf(stderr, "%s", buf);
319             }
320             if (args[i].short_name && args[i].long_name)
321                 count += fprintf (stderr, ", ");
322             if (args[i].long_name) {
323                 count += fprintf (stderr, "--");
324                 if (args[i].type == arg_negative_flag)
325                     count += fprintf (stderr, "no-");
326                 count += fprintf (stderr, "%s", args[i].long_name);
327                 print_arg (buf, sizeof(buf), 0, 1, &args[i], i18n);
328                 count += fprintf(stderr, "%s", buf);
329             }
330             while(count++ <= max_len)
331                 putc (' ', stderr);
332             fprintf (stderr, "%s\n", (*i18n)(args[i].help));
333         }
334     }
335 }
336
337 static int
338 add_string(getarg_strings *s, char *value)
339 {
340     char **strings;
341
342     strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings));
343     if (strings == NULL) {
344         free(s->strings);
345         s->strings = NULL;
346         s->num_strings = 0;
347         return ENOMEM;
348     }
349     s->strings = strings;
350     s->strings[s->num_strings] = value;
351     s->num_strings++;
352     return 0;
353 }
354
355 static int
356 arg_match_long(struct getargs *args, size_t num_args,
357                char *argv, int argc, char **rargv, int *goptind)
358 {
359     int i;
360     char *goptarg = NULL;
361     int negate = 0;
362     int partial_match = 0;
363     struct getargs *partial = NULL;
364     struct getargs *current = NULL;
365     int argv_len;
366     char *p;
367     int p_len;
368
369     argv_len = strlen(argv);
370     p = strchr (argv, '=');
371     if (p != NULL)
372         argv_len = p - argv;
373
374     for (i = 0; i < num_args; ++i) {
375         if(args[i].long_name) {
376             int len = strlen(args[i].long_name);
377             p = argv;
378             p_len = argv_len;
379             negate = 0;
380
381             for (;;) {
382                 if (strncmp (args[i].long_name, p, p_len) == 0) {
383                     if(p_len == len)
384                         current = &args[i];
385                     else {
386                         ++partial_match;
387                         partial = &args[i];
388                     }
389                     goptarg  = p + p_len;
390                 } else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) {
391                     negate = !negate;
392                     p += 3;
393                     p_len -= 3;
394                     continue;
395                 }
396                 break;
397             }
398             if (current)
399                 break;
400         }
401     }
402     if (current == NULL) {
403         if (partial_match == 1)
404             current = partial;
405         else
406             return ARG_ERR_NO_MATCH;
407     }
408
409     if(*goptarg == '\0'
410        && !ISFLAG(*current)
411        && current->type != arg_collect
412        && current->type != arg_counter)
413         return ARG_ERR_NO_MATCH;
414     switch(current->type){
415     case arg_integer:
416     {
417         int tmp;
418         if(sscanf(goptarg + 1, "%d", &tmp) != 1)
419             return ARG_ERR_BAD_ARG;
420         *(int*)current->value = tmp;
421         return 0;
422     }
423     case arg_string:
424     {
425         *(char**)current->value = goptarg + 1;
426         return 0;
427     }
428     case arg_strings:
429     {
430         return add_string((getarg_strings*)current->value, goptarg + 1);
431     }
432     case arg_flag:
433     case arg_negative_flag:
434     {
435         int *flag = current->value;
436         if(*goptarg == '\0' ||
437            strcmp(goptarg + 1, "yes") == 0 ||
438            strcmp(goptarg + 1, "true") == 0){
439             *flag = !negate;
440             return 0;
441         } else if (*goptarg && strcmp(goptarg + 1, "maybe") == 0) {
442 #ifdef HAVE_RANDOM
443             *flag = random() & 1;
444 #else
445             *flag = rand() & 1;
446 #endif
447         } else {
448             *flag = negate;
449             return 0;
450         }
451         return ARG_ERR_BAD_ARG;
452     }
453     case arg_counter :
454     {
455         int val;
456
457         if (*goptarg == '\0')
458             val = 1;
459         else if(sscanf(goptarg + 1, "%d", &val) != 1)
460             return ARG_ERR_BAD_ARG;
461         *(int *)current->value += val;
462         return 0;
463     }
464     case arg_double:
465     {
466         double tmp;
467         if(sscanf(goptarg + 1, "%lf", &tmp) != 1)
468             return ARG_ERR_BAD_ARG;
469         *(double*)current->value = tmp;
470         return 0;
471     }
472     case arg_collect:{
473         struct getarg_collect_info *c = current->value;
474         int o = argv - rargv[*goptind];
475         return (*c->func)(FALSE, argc, rargv, goptind, &o, c->data);
476     }
477
478     default:
479         abort ();
480     }
481 }
482
483 static int
484 arg_match_short (struct getargs *args, size_t num_args,
485                  char *argv, int argc, char **rargv, int *goptind)
486 {
487     int j, k;
488
489     for(j = 1; j > 0 && j < strlen(rargv[*goptind]); j++) {
490         for(k = 0; k < num_args; k++) {
491             char *goptarg;
492
493             if(args[k].short_name == 0)
494                 continue;
495             if(argv[j] == args[k].short_name) {
496                 if(args[k].type == arg_flag) {
497                     *(int*)args[k].value = 1;
498                     break;
499                 }
500                 if(args[k].type == arg_negative_flag) {
501                     *(int*)args[k].value = 0;
502                     break;
503                 }
504                 if(args[k].type == arg_counter) {
505                     ++*(int *)args[k].value;
506                     break;
507                 }
508                 if(args[k].type == arg_collect) {
509                     struct getarg_collect_info *c = args[k].value;
510
511                     if((*c->func)(TRUE, argc, rargv, goptind, &j, c->data))
512                         return ARG_ERR_BAD_ARG;
513                     break;
514                 }
515
516                 if(argv[j + 1])
517                     goptarg = &argv[j + 1];
518                 else {
519                     ++*goptind;
520                     goptarg = rargv[*goptind];
521                 }
522                 if(goptarg == NULL) {
523                     --*goptind;
524                     return ARG_ERR_NO_ARG;
525                 }
526                 if(args[k].type == arg_integer) {
527                     int tmp;
528                     if(sscanf(goptarg, "%d", &tmp) != 1)
529                         return ARG_ERR_BAD_ARG;
530                     *(int*)args[k].value = tmp;
531                     return 0;
532                 } else if(args[k].type == arg_string) {
533                     *(char**)args[k].value = goptarg;
534                     return 0;
535                 } else if(args[k].type == arg_strings) {
536                     return add_string((getarg_strings*)args[k].value, goptarg);
537                 } else if(args[k].type == arg_double) {
538                     double tmp;
539                     if(sscanf(goptarg, "%lf", &tmp) != 1)
540                         return ARG_ERR_BAD_ARG;
541                     *(double*)args[k].value = tmp;
542                     return 0;
543                 }
544                 return ARG_ERR_BAD_ARG;
545             }
546         }
547         if (k == num_args)
548             return ARG_ERR_NO_MATCH;
549     }
550     return 0;
551 }
552
553 int ROKEN_LIB_FUNCTION
554 getarg(struct getargs *args, size_t num_args,
555        int argc, char **argv, int *goptind)
556 {
557     int i;
558     int ret = 0;
559
560 #if defined(HAVE_SRANDOMDEV)
561     srandomdev();
562 #elif defined(HAVE_RANDOM)
563     srandom(time(NULL));
564 #else
565     srand (time(NULL));
566 #endif
567     (*goptind)++;
568     for(i = *goptind; i < argc; i++) {
569         if(argv[i][0] != '-')
570             break;
571         if(argv[i][1] == '-'){
572             if(argv[i][2] == 0){
573                 i++;
574                 break;
575             }
576             ret = arg_match_long (args, num_args, argv[i] + 2,
577                                   argc, argv, &i);
578         } else {
579             ret = arg_match_short (args, num_args, argv[i],
580                                    argc, argv, &i);
581         }
582         if(ret)
583             break;
584     }
585     *goptind = i;
586     return ret;
587 }
588
589 void ROKEN_LIB_FUNCTION
590 free_getarg_strings (getarg_strings *s)
591 {
592     free (s->strings);
593 }
594
595 #if TEST
596 int foo_flag = 2;
597 int flag1 = 0;
598 int flag2 = 0;
599 int bar_int;
600 char *baz_string;
601
602 struct getargs args[] = {
603     { NULL, '1', arg_flag, &flag1, "one", NULL },
604     { NULL, '2', arg_flag, &flag2, "two", NULL },
605     { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL },
606     { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"},
607     { "baz", 'x', arg_string, &baz_string, "baz", "name" },
608 };
609
610 int main(int argc, char **argv)
611 {
612     int goptind = 0;
613     while(getarg(args, 5, argc, argv, &goptind))
614         printf("Bad arg: %s\n", argv[goptind]);
615     printf("flag1 = %d\n", flag1);
616     printf("flag2 = %d\n", flag2);
617     printf("foo_flag = %d\n", foo_flag);
618     printf("bar_int = %d\n", bar_int);
619     printf("baz_flag = %s\n", baz_string);
620     arg_printusage (args, 5, argv[0], "nothing here");
621 }
622 #endif