* Add this nice filesystem testing tool that I've recently
[dragonfly.git] / contrib / com_err / getarg.c
1 /*
2  * Copyright (c) 1997, 1998 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. All advertising materials mentioning features or use of this software 
18  *    must display the following acknowledgement: 
19  *      This product includes software developed by Kungliga Tekniska 
20  *      Högskolan and its contributors. 
21  *
22  * 4. Neither the name of the Institute nor the names of its contributors 
23  *    may be used to endorse or promote products derived from this software 
24  *    without specific prior written permission. 
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
36  * SUCH DAMAGE. 
37  */
38
39 #if 0
40 RCSID("$Id: getarg.c,v 1.25 1998/11/22 09:45:05 assar Exp $");
41 #endif
42
43 #include <sys/ttycom.h>
44 #include <time.h>
45 #include <stdio.h>
46 #include "getarg.h"
47
48 #define ISFLAG(X) ((X).type == arg_flag || (X).type == arg_negative_flag)
49
50 static size_t
51 print_arg (char *string, size_t len, int mdoc, int longp, struct getargs *arg)
52 {
53     const char *s;
54
55     *string = '\0';
56
57     if (ISFLAG(*arg))
58         return 0;
59
60     if(mdoc){
61         if(longp)
62             strncat(string, "= Ns", len);
63         strncat(string, " Ar ", len);
64     }else
65         if (longp)
66             strncat (string, "=", len);
67         else
68             strncat (string, " ", len);
69
70     if (arg->arg_help)
71         s = arg->arg_help;
72     else if (arg->type == arg_integer)
73         s = "number";
74     else if (arg->type == arg_string)
75         s = "string";
76     else
77         s = "<undefined>";
78
79     strncat(string, s, len);
80     return 1 + strlen(s);
81 }
82
83 static int
84 check_column(FILE *f, int col, int len, int columns)
85 {
86     if(col + len > columns) {
87         fprintf(f, "\n");
88         col = fprintf(f, "  ");
89     }
90     return col;
91 }
92
93 void
94 arg_printusage (struct getargs *args,
95                 size_t num_args,
96                 const char *progname,
97                 const char *extra_string)
98 {
99     int i;
100     size_t max_len = 0;
101     char buf[128];
102     int col = 0, columns;
103     struct winsize ws;
104
105     columns = 80;
106     col = 0;
107     col += fprintf (stderr, "Usage: %s", progname);
108     for (i = 0; i < num_args; ++i) {
109         size_t len = 0;
110
111         if (args[i].long_name) {
112             buf[0] = '\0';
113             strncat(buf, "[--", sizeof(buf));
114             len += 2;
115             if(args[i].type == arg_negative_flag) {
116                 strncat(buf, "no-", sizeof(buf));
117                 len += 3;
118             }
119             strncat(buf, args[i].long_name, sizeof(buf));
120             len += strlen(args[i].long_name);
121             len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf), 
122                              0, 1, &args[i]);
123             strncat(buf, "]", sizeof(buf));
124             if(args[i].type == arg_strings)
125                 strncat(buf, "...", sizeof(buf));
126             col = check_column(stderr, col, strlen(buf) + 1, columns);
127             col += fprintf(stderr, " %s", buf);
128         }
129         if (args[i].short_name) {
130             snprintf(buf, sizeof(buf), "[-%c", args[i].short_name);
131             len += 2;
132             len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf), 
133                              0, 0, &args[i]);
134             strncat(buf, "]", sizeof(buf));
135             if(args[i].type == arg_strings)
136                 strncat(buf, "...", sizeof(buf));
137             col = check_column(stderr, col, strlen(buf) + 1, columns);
138             col += fprintf(stderr, " %s", buf);
139         }
140         if (args[i].long_name && args[i].short_name)
141             len += 2; /* ", " */
142         max_len = max(max_len, len);
143     }
144     if (extra_string) {
145         col = check_column(stderr, col, strlen(extra_string) + 1, columns);
146         fprintf (stderr, " %s\n", extra_string);
147     } else
148         fprintf (stderr, "\n");
149     for (i = 0; i < num_args; ++i) {
150         if (args[i].help) {
151             size_t count = 0;
152
153             if (args[i].short_name) {
154                 count += fprintf (stderr, "-%c", args[i].short_name);
155                 print_arg (buf, sizeof(buf), 0, 0, &args[i]);
156                 count += fprintf(stderr, "%s", buf);
157             }
158             if (args[i].short_name && args[i].long_name)
159                 count += fprintf (stderr, ", ");
160             if (args[i].long_name) {
161                 count += fprintf (stderr, "--");
162                 if (args[i].type == arg_negative_flag)
163                     count += fprintf (stderr, "no-");
164                 count += fprintf (stderr, "%s", args[i].long_name);
165                 print_arg (buf, sizeof(buf), 0, 1, &args[i]);
166                 count += fprintf(stderr, "%s", buf);
167             }
168             while(count++ <= max_len)
169                 putc (' ', stderr);
170             fprintf (stderr, "%s\n", args[i].help);
171         }
172     }
173 }
174
175 static void
176 add_string(getarg_strings *s, char *value)
177 {
178     s->strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings));
179     s->strings[s->num_strings] = value;
180     s->num_strings++;
181 }
182
183 static int
184 arg_match_long(struct getargs *args, size_t num_args,
185                char *argv)
186 {
187     int i;
188     char *optarg = NULL;
189     int negate = 0;
190     int partial_match = 0;
191     struct getargs *partial = NULL;
192     struct getargs *current = NULL;
193     int argv_len;
194     char *p;
195
196     argv_len = strlen(argv);
197     p = strchr (argv, '=');
198     if (p != NULL)
199         argv_len = p - argv;
200
201     for (i = 0; i < num_args; ++i) {
202         if(args[i].long_name) {
203             int len = strlen(args[i].long_name);
204             char *p = argv;
205             int p_len = argv_len;
206             negate = 0;
207
208             for (;;) {
209                 if (strncmp (args[i].long_name, p, p_len) == 0) {
210                     if(p_len == len)
211                         current = &args[i];
212                     else {
213                         ++partial_match;
214                         partial = &args[i];
215                     }
216                     optarg  = p + p_len;
217                 } else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) {
218                     negate = !negate;
219                     p += 3;
220                     p_len -= 3;
221                     continue;
222                 }
223                 break;
224             }
225             if (current)
226                 break;
227         }
228     }
229     if (current == NULL) {
230         if (partial_match == 1)
231             current = partial;
232         else
233             return ARG_ERR_NO_MATCH;
234     }
235     
236     if(*optarg == '\0' && !ISFLAG(*current))
237         return ARG_ERR_NO_MATCH;
238     switch(current->type){
239     case arg_integer:
240     {
241         int tmp;
242         if(sscanf(optarg + 1, "%d", &tmp) != 1)
243             return ARG_ERR_BAD_ARG;
244         *(int*)current->value = tmp;
245         return 0;
246     }
247     case arg_string:
248     {
249         *(char**)current->value = optarg + 1;
250         return 0;
251     }
252     case arg_strings:
253     {
254         add_string((getarg_strings*)current->value, optarg + 1);
255         return 0;
256     }
257     case arg_flag:
258     case arg_negative_flag:
259     {
260         int *flag = current->value;
261         if(*optarg == '\0' ||
262            strcmp(optarg + 1, "yes") == 0 || 
263            strcmp(optarg + 1, "true") == 0){
264             *flag = !negate;
265             return 0;
266         } else if (*optarg && strcmp(optarg + 1, "maybe") == 0) {
267             *flag = rand() & 1;
268         } else {
269             *flag = negate;
270             return 0;
271         }
272         return ARG_ERR_BAD_ARG;
273     }
274     default:
275         abort ();
276     }
277 }
278
279 int
280 getarg(struct getargs *args, size_t num_args, 
281        int argc, char **argv, int *optind)
282 {
283     int i, j, k;
284     int ret = 0;
285
286     srand (time(NULL));
287     (*optind)++;
288     for(i = *optind; i < argc; i++) {
289         if(argv[i][0] != '-')
290             break;
291         if(argv[i][1] == '-'){
292             if(argv[i][2] == 0){
293                 i++;
294                 break;
295             }
296             ret = arg_match_long (args, num_args, argv[i] + 2);
297             if(ret)
298                 return ret;
299         }else{
300             for(j = 1; argv[i][j]; j++) {
301                 for(k = 0; k < num_args; k++) {
302                     char *optarg;
303                     if(args[k].short_name == 0)
304                         continue;
305                     if(argv[i][j] == args[k].short_name){
306                         if(args[k].type == arg_flag){
307                             *(int*)args[k].value = 1;
308                             break;
309                         }
310                         if(args[k].type == arg_negative_flag){
311                             *(int*)args[k].value = 0;
312                             break;
313                         }
314                         if(argv[i][j + 1])
315                             optarg = &argv[i][j + 1];
316                         else{
317                             i++;
318                             optarg = argv[i];
319                         }
320                         if(optarg == NULL)
321                             return ARG_ERR_NO_ARG;
322                         if(args[k].type == arg_integer){
323                             int tmp;
324                             if(sscanf(optarg, "%d", &tmp) != 1)
325                                 return ARG_ERR_BAD_ARG;
326                             *(int*)args[k].value = tmp;
327                             goto out;
328                         }else if(args[k].type == arg_string){
329                             *(char**)args[k].value = optarg;
330                             goto out;
331                         }else if(args[k].type == arg_strings){
332                             add_string((getarg_strings*)args[k].value, optarg);
333                             goto out;
334                         }
335                         return ARG_ERR_BAD_ARG;
336                     }
337                         
338                 }
339                 if (k == num_args)
340                     return ARG_ERR_NO_MATCH;
341             }
342         out:;
343         }
344     }
345     *optind = i;
346     return 0;
347 }
348
349 #if TEST
350 int foo_flag = 2;
351 int flag1 = 0;
352 int flag2 = 0;
353 int bar_int;
354 char *baz_string;
355
356 struct getargs args[] = {
357     { NULL, '1', arg_flag, &flag1, "one", NULL },
358     { NULL, '2', arg_flag, &flag2, "two", NULL },
359     { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL },
360     { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"},
361     { "baz", 'x', arg_string, &baz_string, "baz", "name" },
362 };
363
364 int main(int argc, char **argv)
365 {
366     int optind = 0;
367     while(getarg(args, 5, argc, argv, &optind))
368         printf("Bad arg: %s\n", argv[optind]);
369     printf("flag1 = %d\n", flag1);  
370     printf("flag2 = %d\n", flag2);  
371     printf("foo_flag = %d\n", foo_flag);  
372     printf("bar_int = %d\n", bar_int);
373     printf("baz_flag = %s\n", baz_string);
374     arg_printusage (args, 5, argv[0], "nothing here");
375 }
376 #endif