Deleted Added
full compact
main.c (119172) main.c (132493)
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
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 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 */
29#include <sys/cdefs.h>
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
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 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 */
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sbin/atm/atmconfig/main.c 119172 2003-08-20 08:25:36Z harti $");
30__FBSDID("$FreeBSD: head/sbin/atm/atmconfig/main.c 132493 2004-07-21 09:42:56Z harti $");
31
32#include <sys/types.h>
33#include <sys/sysctl.h>
34#include <netdb.h>
35#include <stdarg.h>
36#include <ctype.h>
37#include <limits.h>
31
32#include <sys/types.h>
33#include <sys/sysctl.h>
34#include <netdb.h>
35#include <stdarg.h>
36#include <ctype.h>
37#include <limits.h>
38#include <inttypes.h>
38#include <stdint.h>
39#include <fnmatch.h>
40#include <dirent.h>
39#include "atmconfig.h"
40#include "private.h"
41
42/* verbosity level */
43int verbose;
44
45/* notitle option */
46static int notitle;
47
48/* need to put heading before next output */
49static int need_heading;
50
51/*
52 * TOP LEVEL commands
53 */
54static void help_func(int argc, char *argv[]) __dead2;
55
56static const struct cmdtab main_tab[] = {
57 { "help", NULL, help_func },
58 { "options", NULL, NULL },
59 { "commands", NULL, NULL },
60 { "diag", diag_tab, NULL },
61 { "natm", natm_tab, NULL },
62 { NULL, NULL, NULL }
63};
64
65static int
66substr(const char *s1, const char *s2)
67{
68 return (strlen(s1) <= strlen(s2) && strncmp(s1, s2, strlen(s1)) == 0);
69}
70
71/*
41#include "atmconfig.h"
42#include "private.h"
43
44/* verbosity level */
45int verbose;
46
47/* notitle option */
48static int notitle;
49
50/* need to put heading before next output */
51static int need_heading;
52
53/*
54 * TOP LEVEL commands
55 */
56static void help_func(int argc, char *argv[]) __dead2;
57
58static const struct cmdtab main_tab[] = {
59 { "help", NULL, help_func },
60 { "options", NULL, NULL },
61 { "commands", NULL, NULL },
62 { "diag", diag_tab, NULL },
63 { "natm", natm_tab, NULL },
64 { NULL, NULL, NULL }
65};
66
67static int
68substr(const char *s1, const char *s2)
69{
70 return (strlen(s1) <= strlen(s2) && strncmp(s1, s2, strlen(s1)) == 0);
71}
72
73/*
74 * Current help file state
75 */
76struct help_file {
77 int file_state; /* 0:looking for main file, 1:found, 2:other */
78 const char *p_start; /* current path pointer */
79 const char *p_end; /* end of current path in path */
80 char *dirname; /* directory name */
81 DIR *dir; /* open directory */
82 char *fname; /* current filename */
83 FILE *fp; /* open file */
84 char line[LINE_MAX]; /* current line */
85 u_int fcnt; /* count of files found */
86};
87
88struct help_pos {
89 off_t pos; /* file position */
90 u_int fcnt; /* number of file */
91 char *fname; /* name of file */
92 const char *p_start; /* current path pointer */
93 const char *p_end; /* end of current path in path */
94};
95
96static int
97help_next_file(struct help_file *hp)
98{
99 const char *fpat;
100 struct dirent *ent;
101
102 if (hp->file_state == 3)
103 return (-1);
104
105 if (hp->file_state == 0)
106 fpat = FILE_HELP;
107 else
108 fpat = FILE_HELP_OTHERS;
109
110 if (hp->file_state == 0 || hp->file_state == 1) {
111 /* start from beginning */
112 hp->p_start = PATH_HELP;
113 hp->file_state++;
114 }
115
116 try_file:
117 if (hp->dir != NULL) {
118 /* directory open (must be state 2) */
119 while ((ent = readdir(hp->dir)) != NULL) {
120 if (fnmatch(fpat, ent->d_name, FNM_NOESCAPE) != 0)
121 continue;
122 if (asprintf(&hp->fname, "%s/%s", hp->dirname,
123 ent->d_name) == -1)
124 err(1, NULL);
125 if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
126 hp->fcnt++;
127 return (0);
128 }
129 free(hp->fname);
130 }
131 /* end of directory */
132 closedir(hp->dir);
133 hp->dir = NULL;
134 free(hp->dirname);
135 goto next_path;
136 }
137
138 /* nothing open - advanc to new path element */
139 try_path:
140 for (hp->p_end = hp->p_start; *hp->p_end != '\0' &&
141 *hp->p_end != ':'; hp->p_end++)
142 ;
143
144 if (asprintf(&hp->dirname, "%.*s", (int)(hp->p_end - hp->p_start),
145 hp->p_start) == -1)
146 err(1, NULL);
147
148 if (hp->file_state == 1) {
149 /* just try to open */
150 if (asprintf(&hp->fname, "%s/%s", hp->dirname, fpat) == -1)
151 err(1, NULL);
152 if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
153 hp->fcnt++;
154 return (0);
155 }
156 free(hp->fname);
157
158 goto next_path;
159 }
160
161 /* open directory */
162 if ((hp->dir = opendir(hp->dirname)) != NULL)
163 goto try_file;
164
165 free(hp->dirname);
166
167 next_path:
168 hp->p_start = hp->p_end;
169 if (*hp->p_start == '\0') {
170 /* end of path */
171 if (hp->file_state == 1)
172 errx(1, "help file not found");
173 return (-1);
174 }
175 hp->p_start++;
176 goto try_path;
177
178}
179
180/*
181 * Save current file position
182 */
183static void
184help_file_tell(struct help_file *hp, struct help_pos *pos)
185{
186 if (pos->fname != NULL)
187 free(pos->fname);
188 if ((pos->fname = strdup(hp->fname)) == NULL)
189 err(1, NULL);
190 pos->fcnt = hp->fcnt;
191 pos->p_start = hp->p_start;
192 pos->p_end = hp->p_end;
193 if ((pos->pos = ftello(hp->fp)) == -1)
194 err(1, "%s", pos->fname);
195}
196
197/*
198 * Go to that position
199 *
200 * We can go either to the original help file or back in the current file.
201 */
202static void
203help_file_seek(struct help_file *hp, struct help_pos *pos)
204{
205 hp->p_start = pos->p_start;
206 hp->p_end = pos->p_end;
207 hp->fcnt = pos->fcnt;
208
209 if (hp->dir != NULL) {
210 free(hp->dirname);
211 closedir(hp->dir);
212 hp->dir = NULL;
213 }
214
215 if (hp->fp != NULL &&strcmp(hp->fname, pos->fname) != 0) {
216 free(hp->fname);
217 fclose(hp->fp);
218 hp->fp = NULL;
219 }
220 if (hp->fp == NULL) {
221 if ((hp->fname = strdup(pos->fname)) == NULL)
222 err(1, NULL);
223 if ((hp->fp = fopen(hp->fname, "r")) == NULL)
224 err(1, "reopen %s", hp->fname);
225 }
226 if (fseeko(hp->fp, pos->pos, SEEK_SET) == -1)
227 err(1, "seek %s", hp->fname);
228
229 if (pos->fcnt == 1)
230 /* go back to state 1 */
231 hp->file_state = 1;
232 else
233 /* lock */
234 hp->file_state = 3;
235}
236
237/*
238 * Rewind to position 0
239 */
240static void
241help_file_rewind(struct help_file *hp)
242{
243
244 if (hp->file_state == 1) {
245 if (fseeko(hp->fp, (off_t)0, SEEK_SET) == -1)
246 err(1, "rewind help file");
247 return;
248 }
249
250 if (hp->dir != NULL) {
251 free(hp->dirname);
252 closedir(hp->dir);
253 hp->dir = NULL;
254 }
255
256 if (hp->fp != NULL) {
257 free(hp->fname);
258 fclose(hp->fp);
259 hp->fp = NULL;
260 }
261 memset(hp, 0, sizeof(*hp));
262}
263
264/*
265 * Get next line from a help file
266 */
267static const char *
268help_next_line(struct help_file *hp)
269{
270 for (;;) {
271 if (hp->fp != NULL) {
272 if (fgets(hp->line, sizeof(hp->line), hp->fp) != NULL)
273 return (hp->line);
274 if (ferror(hp->fp))
275 err(1, "%s", hp->fname);
276 free(hp->fname);
277
278 fclose(hp->fp);
279 hp->fp = NULL;
280 }
281 if (help_next_file(hp) == -1)
282 return (NULL);
283 }
284
285}
286
287/*
288 * This function prints the available 0-level help topics from all
289 * other help files by scanning the files. It assumes, that this is called
290 * only from the main help file.
291 */
292static void
293help_get_0topics(struct help_file *hp)
294{
295 struct help_pos save;
296 const char *line;
297
298 memset(&save, 0, sizeof(save));
299 help_file_tell(hp, &save);
300
301 help_file_rewind(hp);
302 while ((line = help_next_line(hp)) != NULL) {
303 if (line[0] == '^' && line[1] == '^')
304 printf("%s", line + 2);
305 }
306 help_file_seek(hp, &save);
307}
308
309/*
72 * Function to print help. The help argument is in argv[0] here.
73 */
74static void
75help_func(int argc, char *argv[])
76{
310 * Function to print help. The help argument is in argv[0] here.
311 */
312static void
313help_func(int argc, char *argv[])
314{
77 FILE *hp;
78 const char *start, *end;
79 char *fname;
80 off_t match, last_match;
81 char line[LINE_MAX];
315 struct help_file hfile;
316 struct help_pos match, last_match;
317 const char *line;
82 char key[100];
318 char key[100];
83 int level, i;
319 int level;
320 int i, has_sub_topics;
84
321
85 /*
86 * Find the help file
87 */
88 hp = NULL;
89 for (start = PATH_HELP; *start != '\0'; start = end + 1) {
90 for (end = start; *end != ':' && *end != '\0'; end++)
91 ;
92 if (start == end) {
93 if (asprintf(&fname, "%s", FILE_HELP) == -1)
94 err(1, NULL);
95 } else {
96 if (asprintf(&fname, "%.*s/%s", (int)(end - start),
97 start, FILE_HELP) == -1)
98 err(1, NULL);
99 }
100 if ((hp = fopen(fname, "r")) != NULL)
101 break;
102 free(fname);
103 }
104 if (hp == NULL)
105 errx(1, "help file not found");
322 memset(&hfile, 0, sizeof(hfile));
323 memset(&match, 0, sizeof(match));
324 memset(&last_match, 0, sizeof(last_match));
106
107 if (argc == 0) {
325
326 if (argc == 0) {
327 /* only 'help' - show intro */
108 if ((argv[0] = strdup("intro")) == NULL)
109 err(1, NULL);
110 argc = 1;
111 }
112
113 optind = 0;
328 if ((argv[0] = strdup("intro")) == NULL)
329 err(1, NULL);
330 argc = 1;
331 }
332
333 optind = 0;
114 match = -1;
115 last_match = -1;
334 match.pos = -1;
335 last_match.pos = -1;
116 for (;;) {
117 /* read next line */
336 for (;;) {
337 /* read next line */
118 if (fgets(line, sizeof(line), hp) == NULL) {
119 if (ferror(hp))
120 err(1, fname);
338 if ((line = help_next_line(&hfile)) == NULL) {
121 /* EOF */
339 /* EOF */
122 clearerr(hp);
123 level = 999;
124 goto stop;
125 }
340 level = 999;
341 goto stop;
342 }
126 if (line[0] != '^')
343 if (line[0] != '^' || line[1] == '^')
127 continue;
128
129 if (sscanf(line + 1, "%d%99s", &level, key) != 2)
130 errx(1, "error in help file '%s'", line);
131
132 if (level < optind) {
133 stop:
134 /* next higher level entry - stop this level */
344 continue;
345
346 if (sscanf(line + 1, "%d%99s", &level, key) != 2)
347 errx(1, "error in help file '%s'", line);
348
349 if (level < optind) {
350 stop:
351 /* next higher level entry - stop this level */
135 if (match == -1) {
352 if (match.pos == -1) {
136 /* not found */
137 goto not_found;
138 }
139 /* go back to the match */
353 /* not found */
354 goto not_found;
355 }
356 /* go back to the match */
140 if (fseeko(hp, match, SEEK_SET) == -1)
141 err(1, fname);
357 help_file_seek(&hfile, &match);
142 last_match = match;
358 last_match = match;
143 match = -1;
359 memset(&match, 0, sizeof(match));
360 match.pos = -1;
144
145 /* go to next key */
146 if (++optind >= argc)
147 break;
148 }
149 if (level == optind) {
150 if (substr(argv[optind], key)) {
361
362 /* go to next key */
363 if (++optind >= argc)
364 break;
365 }
366 if (level == optind) {
367 if (substr(argv[optind], key)) {
151 if (match != -1) {
368 if (match.pos != -1) {
152 printf("Ambiguous topic.");
153 goto list_topics;
154 }
369 printf("Ambiguous topic.");
370 goto list_topics;
371 }
155 if ((match = ftello(hp)) == -1)
156 err(1, fname);
372 help_file_tell(&hfile, &match);
157 }
158 }
159 }
373 }
374 }
375 }
160 if (last_match == -1) {
161 if (fseek(hp, 0L, SEEK_SET) == -1)
162 err(1, fname);
163 } else {
164 if (fseeko(hp, last_match, SEEK_SET) == -1)
165 err(1, fname);
166 }
167
376
377 /* before breaking above we have seeked back to the matching point */
168 for (;;) {
378 for (;;) {
169 if (fgets(line, sizeof(line), hp) == NULL) {
170 if (ferror(hp))
171 err(1, fname);
379 if ((line = help_next_line(&hfile)) == NULL)
172 break;
380 break;
173 }
381
174 if (line[0] == '#')
175 continue;
382 if (line[0] == '#')
383 continue;
176 if (line[0] == '^')
384 if (line[0] == '^') {
385 if (line[1] == '^')
386 continue;
177 break;
387 break;
388 }
389 if (strncmp(line, "$MAIN", 5) == 0) {
390 help_get_0topics(&hfile);
391 continue;
392 }
178 printf("%s", line);
179 }
180
181 exit(0);
182
183 not_found:
184 printf("Topic not found.");
185
186 list_topics:
393 printf("%s", line);
394 }
395
396 exit(0);
397
398 not_found:
399 printf("Topic not found.");
400
401 list_topics:
187 printf(" Use one of:\natmconfig");
402 printf(" Use one of:\natmconfig help");
188 for (i = 0; i < optind; i++)
189 printf(" %s", argv[i]);
403 for (i = 0; i < optind; i++)
404 printf(" %s", argv[i]);
405
190 printf(" [");
406 printf(" [");
407
191 /* list all the keys at this level */
408 /* list all the keys at this level */
192 if (last_match == -1) {
193 if (fseek(hp, 0L, SEEK_SET) == -1)
194 err(1, fname);
195 } else {
196 if (fseeko(hp, last_match, SEEK_SET) == -1)
197 err(1, fname);
198 }
409 if (last_match.pos == -1)
410 /* go back to start of help */
411 help_file_rewind(&hfile);
412 else
413 help_file_seek(&hfile, &last_match);
199
414
200 for (;;) {
201 /* read next line */
202 if (fgets(line, sizeof(line), hp) == NULL) {
203 if (ferror(hp))
204 err(1, fname);
205 break;
206 }
207 if (line[0] == '#' || line[0] != '^')
415 has_sub_topics = 0;
416 while ((line = help_next_line(&hfile)) != NULL) {
417 if (line[0] == '#' || line[0] != '^' || line[1] == '^')
208 continue;
209
210 if (sscanf(line + 1, "%d%99s", &level, key) != 2)
211 errx(1, "error in help file '%s'", line);
212
213 if (level < optind)
214 break;
418 continue;
419
420 if (sscanf(line + 1, "%d%99s", &level, key) != 2)
421 errx(1, "error in help file '%s'", line);
422
423 if (level < optind)
424 break;
215 if (level == optind)
425 if (level == optind) {
426 has_sub_topics = 1;
216 printf(" %s", key);
427 printf(" %s", key);
428 }
217 }
429 }
218 printf(" ]\n");
430 printf(" ].");
431 if (!has_sub_topics)
432 printf(" No sub-topics found.");
433 printf("\n");
219 exit(1);
220}
221
222int
223main(int argc, char *argv[])
224{
225 int opt, i;
226 const struct cmdtab *match, *cc, *tab;
227
228 while ((opt = getopt(argc, argv, "htv")) != -1)
229 switch (opt) {
230
231 case 'h':
232 help_func(0, argv);
233
234 case 'v':
235 verbose++;
236 break;
237
238 case 't':
239 notitle = 1;
240 break;
241 }
242
243 if (argv[optind] == NULL)
244 help_func(0, argv);
245
246 argc -= optind;
247 argv += optind;
248
249 cc = main_tab;
250 i = 0;
251 for (;;) {
252 /*
253 * Scan the table for a match
254 */
255 tab = cc;
256 match = NULL;
257 while (cc->string != NULL) {
258 if (substr(argv[i], cc->string)) {
259 if (match != NULL) {
260 printf("Ambiguous option '%s'",
261 argv[i]);
262 cc = tab;
263 goto subopts;
264 }
265 match = cc;
266 }
267 cc++;
268 }
269 if ((cc = match) == NULL) {
270 printf("Unknown option '%s'", argv[i]);
271 cc = tab;
272 goto subopts;
273 }
274
275 /*
276 * Have a match. If there is no subtable, there must
277 * be either a handler or the command is only a help entry.
278 */
279 if (cc->sub == NULL) {
280 if (cc->func != NULL)
281 break;
282 printf("Unknown option '%s'", argv[i]);
283 cc = tab;
284 goto subopts;
285 }
286
287 /*
288 * Look at the next argument. If it doesn't exist or it
289 * looks like a switch, terminate the scan here.
290 */
291 if (argv[i + 1] == NULL || argv[i + 1][0] == '-') {
292 if (cc->func != NULL)
293 break;
294 printf("Need sub-option for '%s'", argv[i]);
295 cc = cc->sub;
296 goto subopts;
297 }
298
299 cc = cc->sub;
300 i++;
301 }
302
303 argc -= i + 1;
304 argv += i + 1;
305
306 (*cc->func)(argc, argv);
307
308 return (0);
309
310 subopts:
311 printf(". Select one of:\n");
312 while (cc->string != NULL) {
313 if (cc->func != NULL || cc->sub != NULL)
314 printf("%s ", cc->string);
315 cc++;
316 }
317 printf("\n");
318
319 return (1);
320}
321
322void
323verb(const char *fmt, ...)
324{
325 va_list ap;
326
327 if (verbose) {
328 va_start(ap, fmt);
329 vfprintf(stderr, fmt, ap);
330 fprintf(stderr, "\n");
331 va_end(ap);
332 }
333}
334
335void
336heading(const char *fmt, ...)
337{
338 va_list ap;
339
340 if (need_heading) {
341 need_heading = 0;
342 if (!notitle) {
343 va_start(ap, fmt);
344 fprintf(stdout, fmt, ap);
345 va_end(ap);
346 }
347 }
348}
349
350void
351heading_init(void)
352{
353 need_heading = 1;
354}
355
356/*
357 * stringify an enumerated value
358 */
359const char *
360penum(int32_t value, const struct penum *strtab, char *buf)
361{
362 while (strtab->str != NULL) {
363 if (strtab->value == value) {
364 strcpy(buf, strtab->str);
365 return (buf);
366 }
367 strtab++;
368 }
369 warnx("illegal value for enumerated variable '%d'", value);
370 strcpy(buf, "?");
371 return (buf);
372}
373
374/*
375 * Parse command line options
376 */
377int
378parse_options(int *pargc, char ***pargv, const struct option *opts)
379{
380 const struct option *o, *m;
381 char *arg;
382 u_long ularg, ularg1;
383 long larg;
384 char *end;
385
386 if (*pargc == 0)
387 return (-1);
388 arg = (*pargv)[0];
389 if (arg[0] != '-' || arg[1] == '\0')
390 return (-1);
391 if (arg[1] == '-' && arg[2] == '\0') {
392 (*pargv)++;
393 (*pargc)--;
394 return (-1);
395 }
396
397 m = NULL;
398 for (o = opts; o->optstr != NULL; o++) {
399 if (strlen(arg + 1) <= strlen(o->optstr) &&
400 strncmp(arg + 1, o->optstr, strlen(arg + 1)) == 0) {
401 if (m != NULL)
402 errx(1, "ambiguous option '%s'", arg);
403 m = o;
404 }
405 }
406 if (m == NULL)
407 errx(1, "unknown option '%s'", arg);
408
409 (*pargv)++;
410 (*pargc)--;
411
412 if (m->opttype == OPT_NONE)
413 return (m - opts);
414
415 if (m->opttype == OPT_SIMPLE) {
416 *(int *)m->optarg = 1;
417 return (m - opts);
418 }
419
420 if (*pargc == 0)
421 errx(1, "option requires argument '%s'", arg);
422 optarg = *(*pargv)++;
423 (*pargc)--;
424
425 switch (m->opttype) {
426
427 case OPT_UINT:
428 ularg = strtoul(optarg, &end, 0);
429 if (*end != '\0')
430 errx(1, "bad unsigned integer argument for '%s'", arg);
431 if (ularg > UINT_MAX)
432 errx(1, "argument to large for option '%s'", arg);
433 *(u_int *)m->optarg = (u_int)ularg;
434 break;
435
436 case OPT_INT:
437 larg = strtol(optarg, &end, 0);
438 if (*end != '\0')
439 errx(1, "bad integer argument for '%s'", arg);
440 if (larg > INT_MAX || larg < INT_MIN)
441 errx(1, "argument out of range for option '%s'", arg);
442 *(int *)m->optarg = (int)larg;
443 break;
444
445 case OPT_UINT32:
446 ularg = strtoul(optarg, &end, 0);
447 if (*end != '\0')
448 errx(1, "bad unsigned integer argument for '%s'", arg);
449 if (ularg > UINT32_MAX)
450 errx(1, "argument to large for option '%s'", arg);
451 *(uint32_t *)m->optarg = (uint32_t)ularg;
452 break;
453
454 case OPT_INT32:
455 larg = strtol(optarg, &end, 0);
456 if (*end != '\0')
457 errx(1, "bad integer argument for '%s'", arg);
458 if (larg > INT32_MAX || larg < INT32_MIN)
459 errx(1, "argument out of range for option '%s'", arg);
460 *(int32_t *)m->optarg = (int32_t)larg;
461 break;
462
463 case OPT_UINT64:
464 *(uint64_t *)m->optarg = strtoull(optarg, &end, 0);
465 if (*end != '\0')
466 errx(1, "bad unsigned integer argument for '%s'", arg);
467 break;
468
469 case OPT_INT64:
470 *(int64_t *)m->optarg = strtoll(optarg, &end, 0);
471 if (*end != '\0')
472 errx(1, "bad integer argument for '%s'", arg);
473 break;
474
475 case OPT_FLAG:
476 if (strcasecmp(optarg, "enable") == 0 ||
477 strcasecmp(optarg, "yes") == 0 ||
478 strcasecmp(optarg, "true") == 0 ||
479 strcasecmp(optarg, "on") == 0 ||
480 strcmp(optarg, "1") == 0)
481 *(int *)m->optarg = 1;
482 else if (strcasecmp(optarg, "disable") == 0 ||
483 strcasecmp(optarg, "no") == 0 ||
484 strcasecmp(optarg, "false") == 0 ||
485 strcasecmp(optarg, "off") == 0 ||
486 strcmp(optarg, "0") == 0)
487 *(int *)m->optarg = 0;
488 else
489 errx(1, "bad boolean argument to '%s'", arg);
490 break;
491
492 case OPT_VCI:
493 ularg = strtoul(optarg, &end, 0);
494 if (*end == '.') {
495 ularg1 = strtoul(end + 1, &end, 0);
496 } else {
497 ularg1 = ularg;
498 ularg = 0;
499 }
500 if (*end != '\0')
501 errx(1, "bad VCI value for option '%s'", arg);
502 if (ularg > 0xff)
503 errx(1, "VPI value too large for option '%s'", arg);
504 if (ularg1 > 0xffff)
505 errx(1, "VCI value too large for option '%s'", arg);
506 ((u_int *)m->optarg)[0] = ularg;
507 ((u_int *)m->optarg)[1] = ularg1;
508 break;
509
510 case OPT_STRING:
511 if (m->optarg != NULL)
512 *(const char **)m->optarg = optarg;
513 break;
514
515 default:
516 errx(1, "(internal) bad option type %u for '%s'",
517 m->opttype, arg);
518 }
519 return (m - opts);
520}
434 exit(1);
435}
436
437int
438main(int argc, char *argv[])
439{
440 int opt, i;
441 const struct cmdtab *match, *cc, *tab;
442
443 while ((opt = getopt(argc, argv, "htv")) != -1)
444 switch (opt) {
445
446 case 'h':
447 help_func(0, argv);
448
449 case 'v':
450 verbose++;
451 break;
452
453 case 't':
454 notitle = 1;
455 break;
456 }
457
458 if (argv[optind] == NULL)
459 help_func(0, argv);
460
461 argc -= optind;
462 argv += optind;
463
464 cc = main_tab;
465 i = 0;
466 for (;;) {
467 /*
468 * Scan the table for a match
469 */
470 tab = cc;
471 match = NULL;
472 while (cc->string != NULL) {
473 if (substr(argv[i], cc->string)) {
474 if (match != NULL) {
475 printf("Ambiguous option '%s'",
476 argv[i]);
477 cc = tab;
478 goto subopts;
479 }
480 match = cc;
481 }
482 cc++;
483 }
484 if ((cc = match) == NULL) {
485 printf("Unknown option '%s'", argv[i]);
486 cc = tab;
487 goto subopts;
488 }
489
490 /*
491 * Have a match. If there is no subtable, there must
492 * be either a handler or the command is only a help entry.
493 */
494 if (cc->sub == NULL) {
495 if (cc->func != NULL)
496 break;
497 printf("Unknown option '%s'", argv[i]);
498 cc = tab;
499 goto subopts;
500 }
501
502 /*
503 * Look at the next argument. If it doesn't exist or it
504 * looks like a switch, terminate the scan here.
505 */
506 if (argv[i + 1] == NULL || argv[i + 1][0] == '-') {
507 if (cc->func != NULL)
508 break;
509 printf("Need sub-option for '%s'", argv[i]);
510 cc = cc->sub;
511 goto subopts;
512 }
513
514 cc = cc->sub;
515 i++;
516 }
517
518 argc -= i + 1;
519 argv += i + 1;
520
521 (*cc->func)(argc, argv);
522
523 return (0);
524
525 subopts:
526 printf(". Select one of:\n");
527 while (cc->string != NULL) {
528 if (cc->func != NULL || cc->sub != NULL)
529 printf("%s ", cc->string);
530 cc++;
531 }
532 printf("\n");
533
534 return (1);
535}
536
537void
538verb(const char *fmt, ...)
539{
540 va_list ap;
541
542 if (verbose) {
543 va_start(ap, fmt);
544 vfprintf(stderr, fmt, ap);
545 fprintf(stderr, "\n");
546 va_end(ap);
547 }
548}
549
550void
551heading(const char *fmt, ...)
552{
553 va_list ap;
554
555 if (need_heading) {
556 need_heading = 0;
557 if (!notitle) {
558 va_start(ap, fmt);
559 fprintf(stdout, fmt, ap);
560 va_end(ap);
561 }
562 }
563}
564
565void
566heading_init(void)
567{
568 need_heading = 1;
569}
570
571/*
572 * stringify an enumerated value
573 */
574const char *
575penum(int32_t value, const struct penum *strtab, char *buf)
576{
577 while (strtab->str != NULL) {
578 if (strtab->value == value) {
579 strcpy(buf, strtab->str);
580 return (buf);
581 }
582 strtab++;
583 }
584 warnx("illegal value for enumerated variable '%d'", value);
585 strcpy(buf, "?");
586 return (buf);
587}
588
589/*
590 * Parse command line options
591 */
592int
593parse_options(int *pargc, char ***pargv, const struct option *opts)
594{
595 const struct option *o, *m;
596 char *arg;
597 u_long ularg, ularg1;
598 long larg;
599 char *end;
600
601 if (*pargc == 0)
602 return (-1);
603 arg = (*pargv)[0];
604 if (arg[0] != '-' || arg[1] == '\0')
605 return (-1);
606 if (arg[1] == '-' && arg[2] == '\0') {
607 (*pargv)++;
608 (*pargc)--;
609 return (-1);
610 }
611
612 m = NULL;
613 for (o = opts; o->optstr != NULL; o++) {
614 if (strlen(arg + 1) <= strlen(o->optstr) &&
615 strncmp(arg + 1, o->optstr, strlen(arg + 1)) == 0) {
616 if (m != NULL)
617 errx(1, "ambiguous option '%s'", arg);
618 m = o;
619 }
620 }
621 if (m == NULL)
622 errx(1, "unknown option '%s'", arg);
623
624 (*pargv)++;
625 (*pargc)--;
626
627 if (m->opttype == OPT_NONE)
628 return (m - opts);
629
630 if (m->opttype == OPT_SIMPLE) {
631 *(int *)m->optarg = 1;
632 return (m - opts);
633 }
634
635 if (*pargc == 0)
636 errx(1, "option requires argument '%s'", arg);
637 optarg = *(*pargv)++;
638 (*pargc)--;
639
640 switch (m->opttype) {
641
642 case OPT_UINT:
643 ularg = strtoul(optarg, &end, 0);
644 if (*end != '\0')
645 errx(1, "bad unsigned integer argument for '%s'", arg);
646 if (ularg > UINT_MAX)
647 errx(1, "argument to large for option '%s'", arg);
648 *(u_int *)m->optarg = (u_int)ularg;
649 break;
650
651 case OPT_INT:
652 larg = strtol(optarg, &end, 0);
653 if (*end != '\0')
654 errx(1, "bad integer argument for '%s'", arg);
655 if (larg > INT_MAX || larg < INT_MIN)
656 errx(1, "argument out of range for option '%s'", arg);
657 *(int *)m->optarg = (int)larg;
658 break;
659
660 case OPT_UINT32:
661 ularg = strtoul(optarg, &end, 0);
662 if (*end != '\0')
663 errx(1, "bad unsigned integer argument for '%s'", arg);
664 if (ularg > UINT32_MAX)
665 errx(1, "argument to large for option '%s'", arg);
666 *(uint32_t *)m->optarg = (uint32_t)ularg;
667 break;
668
669 case OPT_INT32:
670 larg = strtol(optarg, &end, 0);
671 if (*end != '\0')
672 errx(1, "bad integer argument for '%s'", arg);
673 if (larg > INT32_MAX || larg < INT32_MIN)
674 errx(1, "argument out of range for option '%s'", arg);
675 *(int32_t *)m->optarg = (int32_t)larg;
676 break;
677
678 case OPT_UINT64:
679 *(uint64_t *)m->optarg = strtoull(optarg, &end, 0);
680 if (*end != '\0')
681 errx(1, "bad unsigned integer argument for '%s'", arg);
682 break;
683
684 case OPT_INT64:
685 *(int64_t *)m->optarg = strtoll(optarg, &end, 0);
686 if (*end != '\0')
687 errx(1, "bad integer argument for '%s'", arg);
688 break;
689
690 case OPT_FLAG:
691 if (strcasecmp(optarg, "enable") == 0 ||
692 strcasecmp(optarg, "yes") == 0 ||
693 strcasecmp(optarg, "true") == 0 ||
694 strcasecmp(optarg, "on") == 0 ||
695 strcmp(optarg, "1") == 0)
696 *(int *)m->optarg = 1;
697 else if (strcasecmp(optarg, "disable") == 0 ||
698 strcasecmp(optarg, "no") == 0 ||
699 strcasecmp(optarg, "false") == 0 ||
700 strcasecmp(optarg, "off") == 0 ||
701 strcmp(optarg, "0") == 0)
702 *(int *)m->optarg = 0;
703 else
704 errx(1, "bad boolean argument to '%s'", arg);
705 break;
706
707 case OPT_VCI:
708 ularg = strtoul(optarg, &end, 0);
709 if (*end == '.') {
710 ularg1 = strtoul(end + 1, &end, 0);
711 } else {
712 ularg1 = ularg;
713 ularg = 0;
714 }
715 if (*end != '\0')
716 errx(1, "bad VCI value for option '%s'", arg);
717 if (ularg > 0xff)
718 errx(1, "VPI value too large for option '%s'", arg);
719 if (ularg1 > 0xffff)
720 errx(1, "VCI value too large for option '%s'", arg);
721 ((u_int *)m->optarg)[0] = ularg;
722 ((u_int *)m->optarg)[1] = ularg1;
723 break;
724
725 case OPT_STRING:
726 if (m->optarg != NULL)
727 *(const char **)m->optarg = optarg;
728 break;
729
730 default:
731 errx(1, "(internal) bad option type %u for '%s'",
732 m->opttype, arg);
733 }
734 return (m - opts);
735}