main.c revision 270824
1247835Skib/*
2247835Skib * Copyright (c) 2001-2003
3247835Skib *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4247835Skib * 	All rights reserved.
5247835Skib *
6247835Skib * Redistribution and use in source and binary forms, with or without
7247835Skib * modification, are permitted provided that the following conditions
8247835Skib * are met:
9247835Skib * 1. Redistributions of source code must retain the above copyright
10247835Skib *    notice, this list of conditions and the following disclaimer.
11247835Skib * 2. Redistributions in binary form must reproduce the above copyright
12247835Skib *    notice, this list of conditions and the following disclaimer in the
13247835Skib *    documentation and/or other materials provided with the distribution.
14247835Skib *
15247835Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16247835Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17247835Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18247835Skib * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19247835Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20247835Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21247835Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22247835Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23247835Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24247835Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25247835Skib * SUCH DAMAGE.
26247835Skib *
27247835Skib * Author: Hartmut Brandt <harti@freebsd.org>
28247835Skib */
29247835Skib#include <sys/cdefs.h>
30247835Skib__FBSDID("$FreeBSD: stable/10/sbin/atm/atmconfig/main.c 270824 2014-08-29 18:26:55Z ngie $");
31247835Skib
32247835Skib#include <sys/types.h>
33247835Skib#include <sys/sysctl.h>
34247835Skib#include <netdb.h>
35247835Skib#include <stdarg.h>
36247835Skib#include <ctype.h>
37247835Skib#include <limits.h>
38247835Skib#include <stdint.h>
39247835Skib#include <fnmatch.h>
40247835Skib#include <dirent.h>
41247835Skib#ifdef WITH_BSNMP
42247835Skib#include <bsnmp/asn1.h>
43247835Skib#include <bsnmp/snmp.h>
44247835Skib#include <bsnmp/snmpclient.h>
45247835Skib#endif
46247835Skib
47247835Skib#include "atmconfig.h"
48247835Skib#include "private.h"
49247835Skib
50247835Skib/* verbosity level */
51247835Skibstatic int verbose;
52247835Skib
53247835Skib/* notitle option */
54247835Skibstatic int notitle;
55247835Skib
56247835Skib/* need to put heading before next output */
57247835Skibstatic int need_heading;
58247835Skib
59247835Skib/*
60247835Skib * TOP LEVEL commands
61247835Skib */
62247835Skibstatic void help_func(int argc, char *argv[]) __dead2;
63247835Skib
64247835Skibstatic const struct cmdtab static_main_tab[] = {
65247835Skib	{ "help",	NULL,		help_func },
66247835Skib	{ "options",	NULL,		NULL },
67247835Skib	{ "commands",	NULL,		NULL },
68247835Skib	{ "diag",	diag_tab,	NULL },
69247835Skib	{ "natm",	natm_tab,	NULL },
70247835Skib	{ NULL,		NULL,		NULL }
71247835Skib};
72247835Skib
73247835Skibstatic struct cmdtab *main_tab = NULL;
74247835Skibstatic size_t main_tab_size = sizeof(static_main_tab) /
75247835Skib	sizeof(static_main_tab[0]);
76247835Skib
77247835Skibstatic int
78247835Skibsubstr(const char *s1, const char *s2)
79247835Skib{
80247835Skib	return (strlen(s1) <= strlen(s2) && strncmp(s1, s2, strlen(s1)) == 0);
81247835Skib}
82247835Skib
83247835Skib/*
84247835Skib * Current help file state
85247835Skib */
86247835Skibstruct help_file {
87247835Skib	int	file_state;	/* 0:looking for main file, 1:found, 2:other */
88247835Skib	const char *p_start;	/* current path pointer */
89247835Skib	const char *p_end;	/* end of current path in path */
90247835Skib	char	*dirname;	/* directory name */
91247835Skib	DIR	*dir;		/* open directory */
92247835Skib	char	*fname;		/* current filename */
93247835Skib	FILE	*fp;		/* open file */
94247835Skib	char	line[LINE_MAX];	/* current line */
95247835Skib	u_int	fcnt;		/* count of files found */
96247835Skib};
97247835Skib
98247835Skibstruct help_pos {
99247835Skib	off_t	pos;		/* file position */
100247835Skib	u_int	fcnt;		/* number of file */
101247835Skib	char	*fname;		/* name of file */
102247835Skib	const char *p_start;	/* current path pointer */
103247835Skib	const char *p_end;	/* end of current path in path */
104247835Skib};
105247835Skib
106247835Skibstatic int
107247835Skibhelp_next_file(struct help_file *hp)
108247835Skib{
109247835Skib	const char *fpat;
110247835Skib	struct dirent *ent;
111247835Skib
112247835Skib	if (hp->file_state == 3)
113247835Skib		return (-1);
114247835Skib
115247835Skib	if (hp->file_state == 0)
116247835Skib		fpat = FILE_HELP;
117247835Skib	else
118247835Skib		fpat = FILE_HELP_OTHERS;
119247835Skib
120247835Skib	if (hp->file_state == 0 || hp->file_state == 1) {
121247835Skib		/* start from beginning */
122247835Skib		hp->p_start = PATH_HELP;
123247835Skib		hp->file_state++;
124247835Skib	}
125247835Skib
126247835Skib  try_file:
127247835Skib	if (hp->dir != NULL) {
128247835Skib		/* directory open (must be state 2) */
129247835Skib		while ((ent = readdir(hp->dir)) != NULL) {
130247835Skib			if (fnmatch(fpat, ent->d_name, FNM_NOESCAPE) != 0)
131247835Skib				continue;
132247835Skib			if (asprintf(&hp->fname, "%s/%s", hp->dirname,
133247835Skib			    ent->d_name) == -1)
134247835Skib				err(1, NULL);
135247835Skib			if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
136247835Skib				hp->fcnt++;
137247835Skib				return (0);
138247835Skib			}
139247835Skib			free(hp->fname);
140247835Skib		}
141247835Skib		/* end of directory */
142247835Skib		closedir(hp->dir);
143247835Skib		hp->dir = NULL;
144247835Skib		free(hp->dirname);
145247835Skib		goto next_path;
146247835Skib	}
147247835Skib
148247835Skib	/* nothing open - advanc to new path element */
149247835Skib  try_path:
150247835Skib	for (hp->p_end = hp->p_start; *hp->p_end != '\0' &&
151247835Skib	    *hp->p_end != ':'; hp->p_end++)
152247835Skib		;
153247835Skib
154247835Skib	if (asprintf(&hp->dirname, "%.*s", (int)(hp->p_end - hp->p_start),
155247835Skib	    hp->p_start) == -1)
156247835Skib		err(1, NULL);
157247835Skib
158247835Skib	if (hp->file_state == 1) {
159247835Skib		/* just try to open */
160247835Skib		if (asprintf(&hp->fname, "%s/%s", hp->dirname, fpat) == -1)
161247835Skib			err(1, NULL);
162247835Skib		if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
163247835Skib			hp->fcnt++;
164247835Skib			return (0);
165247835Skib		}
166247835Skib		free(hp->fname);
167247835Skib
168247835Skib		goto next_path;
169247835Skib	}
170247835Skib
171247835Skib	/* open directory */
172247835Skib	if ((hp->dir = opendir(hp->dirname)) != NULL)
173247835Skib		goto try_file;
174247835Skib
175247835Skib	free(hp->dirname);
176247835Skib
177247835Skib  next_path:
178247835Skib	hp->p_start = hp->p_end;
179247835Skib	if (*hp->p_start == '\0') {
180247835Skib		/* end of path */
181247835Skib		if (hp->file_state == 1)
182247835Skib			errx(1, "help file not found");
183247835Skib		return (-1);
184247835Skib	}
185247835Skib	hp->p_start++;
186247835Skib	goto try_path;
187247835Skib
188247835Skib}
189247835Skib
190247835Skib/*
191247835Skib * Save current file position
192247835Skib */
193247835Skibstatic void
194247835Skibhelp_file_tell(struct help_file *hp, struct help_pos *pos)
195247835Skib{
196247835Skib	if (pos->fname != NULL)
197247835Skib		free(pos->fname);
198247835Skib	if ((pos->fname = strdup(hp->fname)) == NULL)
199247835Skib		err(1, NULL);
200247835Skib	pos->fcnt = hp->fcnt;
201247835Skib	pos->p_start = hp->p_start;
202247835Skib	pos->p_end = hp->p_end;
203247835Skib	if ((pos->pos = ftello(hp->fp)) == -1)
204247835Skib		err(1, "%s", pos->fname);
205247835Skib}
206247835Skib
207247835Skib/*
208247835Skib * Go to that position
209247835Skib *
210247835Skib * We can go either to the original help file or back in the current file.
211247835Skib */
212247835Skibstatic void
213247835Skibhelp_file_seek(struct help_file *hp, struct help_pos *pos)
214247835Skib{
215247835Skib	hp->p_start = pos->p_start;
216247835Skib	hp->p_end = pos->p_end;
217247835Skib	hp->fcnt = pos->fcnt;
218247835Skib
219247835Skib	if (hp->dir != NULL) {
220247835Skib		free(hp->dirname);
221247835Skib		closedir(hp->dir);
222247835Skib		hp->dir = NULL;
223247835Skib	}
224247835Skib
225247835Skib	if (hp->fp != NULL &&strcmp(hp->fname, pos->fname) != 0) {
226247835Skib		free(hp->fname);
227247835Skib		fclose(hp->fp);
228247835Skib		hp->fp = NULL;
229247835Skib	}
230247835Skib	if (hp->fp == NULL) {
231247835Skib		if ((hp->fname = strdup(pos->fname)) == NULL)
232247835Skib			err(1, NULL);
233247835Skib		if ((hp->fp = fopen(hp->fname, "r")) == NULL)
234247835Skib			err(1, "reopen %s", hp->fname);
235247835Skib	}
236247835Skib	if (fseeko(hp->fp, pos->pos, SEEK_SET) == -1)
237247835Skib		err(1, "seek %s", hp->fname);
238247835Skib
239247835Skib	if (pos->fcnt == 1)
240247835Skib		/* go back to state 1 */
241247835Skib		hp->file_state = 1;
242247835Skib	else
243247835Skib		/* lock */
244247835Skib		hp->file_state = 3;
245247835Skib}
246247835Skib
247247835Skib/*
248247835Skib * Rewind to position 0
249247835Skib */
250247835Skibstatic void
251247835Skibhelp_file_rewind(struct help_file *hp)
252247835Skib{
253247835Skib
254247835Skib	if (hp->file_state == 1) {
255247835Skib		if (fseeko(hp->fp, (off_t)0, SEEK_SET) == -1)
256247835Skib			err(1, "rewind help file");
257247835Skib		return;
258247835Skib	}
259247835Skib
260247835Skib	if (hp->dir != NULL) {
261247835Skib		free(hp->dirname);
262247835Skib		closedir(hp->dir);
263247835Skib		hp->dir = NULL;
264247835Skib	}
265247835Skib
266247835Skib	if (hp->fp != NULL) {
267247835Skib		free(hp->fname);
268247835Skib		fclose(hp->fp);
269247835Skib		hp->fp = NULL;
270247835Skib	}
271247835Skib	memset(hp, 0, sizeof(*hp));
272247835Skib}
273247835Skib
274247835Skib/*
275247835Skib * Get next line from a help file
276247835Skib */
277247835Skibstatic const char *
278247835Skibhelp_next_line(struct help_file *hp)
279247835Skib{
280247835Skib	for (;;) {
281247835Skib		if (hp->fp != NULL) {
282247835Skib			if (fgets(hp->line, sizeof(hp->line), hp->fp) != NULL)
283247835Skib				return (hp->line);
284247835Skib			if (ferror(hp->fp))
285247835Skib				err(1, "%s", hp->fname);
286247835Skib			free(hp->fname);
287247835Skib
288248084Sattilio			fclose(hp->fp);
289247835Skib			hp->fp = NULL;
290247835Skib		}
291254649Skib		if (help_next_file(hp) == -1)
292247835Skib			return (NULL);
293247835Skib	}
294247835Skib
295247835Skib}
296247835Skib
297247835Skib/*
298247835Skib * This function prints the available 0-level help topics from all
299247835Skib * other help files by scanning the files. It assumes, that this is called
300247835Skib * only from the main help file.
301247835Skib */
302247835Skibstatic void
303247835Skibhelp_get_0topics(struct help_file *hp)
304247835Skib{
305254138Sattilio	struct help_pos save;
306247835Skib	const char *line;
307247835Skib
308247835Skib	memset(&save, 0, sizeof(save));
309247835Skib	help_file_tell(hp, &save);
310247835Skib
311247835Skib	help_file_rewind(hp);
312247835Skib	while ((line = help_next_line(hp)) != NULL) {
313247835Skib		if (line[0] == '^' && line[1] == '^')
314248084Sattilio			printf("%s", line + 2);
315247835Skib	}
316247835Skib	help_file_seek(hp, &save);
317247835Skib}
318247835Skib
319247835Skib/*
320247835Skib * Function to print help. The help argument is in argv[0] here.
321247835Skib */
322247835Skibstatic void
323247835Skibhelp_func(int argc, char *argv[])
324248084Sattilio{
325247835Skib	struct help_file hfile;
326247835Skib	struct help_pos match, last_match;
327247835Skib	const char *line;
328247835Skib	char key[100];
329247835Skib	int level;
330247835Skib	int i, has_sub_topics;
331247835Skib
332247835Skib	memset(&hfile, 0, sizeof(hfile));
333247835Skib	memset(&match, 0, sizeof(match));
334247835Skib	memset(&last_match, 0, sizeof(last_match));
335247835Skib
336247835Skib	if (argc == 0) {
337247835Skib		/* only 'help' - show intro */
338247835Skib		if ((argv[0] = strdup("intro")) == NULL)
339247835Skib			err(1, NULL);
340247835Skib		argc = 1;
341247835Skib	}
342247835Skib
343247835Skib	optind = 0;
344247835Skib	match.pos = -1;
345247835Skib	last_match.pos = -1;
346247835Skib	for (;;) {
347247835Skib		/* read next line */
348248084Sattilio		if ((line = help_next_line(&hfile)) == NULL) {
349247835Skib			/* EOF */
350247835Skib			level = 999;
351247835Skib			goto stop;
352247835Skib		}
353247835Skib		if (line[0] != '^' || line[1] == '^')
354254649Skib			continue;
355247835Skib
356254875Sdumbbell		if (sscanf(line + 1, "%d%99s", &level, key) != 2)
357247835Skib			errx(1, "error in help file '%s'", line);
358254138Sattilio
359247835Skib		if (level < optind) {
360247835Skib  stop:
361248084Sattilio			/* next higher level entry - stop this level */
362247835Skib			if (match.pos == -1) {
363247835Skib				/* not found */
364247835Skib				goto not_found;
365247835Skib			}
366247835Skib			/* go back to the match */
367247835Skib			help_file_seek(&hfile, &match);
368247835Skib			last_match = match;
369247835Skib			memset(&match, 0, sizeof(match));
370			match.pos = -1;
371
372			/* go to next key */
373			if (++optind >= argc)
374				break;
375		}
376		if (level == optind) {
377			if (substr(argv[optind], key)) {
378				if (match.pos != -1) {
379					printf("Ambiguous topic.");
380					goto list_topics;
381				}
382				help_file_tell(&hfile, &match);
383			}
384		}
385	}
386
387	/* before breaking above we have seeked back to the matching point */
388	for (;;) {
389		if ((line = help_next_line(&hfile)) == NULL)
390			break;
391
392		if (line[0] == '#')
393			continue;
394		if (line[0] == '^') {
395			if (line[1] == '^')
396				continue;
397			break;
398		}
399		if (strncmp(line, "$MAIN", 5) == 0) {
400			help_get_0topics(&hfile);
401			continue;
402		}
403		printf("%s", line);
404	}
405
406	exit(0);
407
408  not_found:
409	printf("Topic not found.");
410
411  list_topics:
412	printf(" Use one of:\natmconfig help");
413	for (i = 0; i < optind; i++)
414		printf(" %s", argv[i]);
415
416	printf(" [");
417
418	/* list all the keys at this level */
419	if (last_match.pos == -1)
420		/* go back to start of help */
421		help_file_rewind(&hfile);
422	else
423		help_file_seek(&hfile, &last_match);
424
425	has_sub_topics = 0;
426	while ((line = help_next_line(&hfile)) != NULL) {
427		if (line[0] == '#' || line[0] != '^' || line[1] == '^')
428			continue;
429
430		if (sscanf(line + 1, "%d%99s", &level, key) != 2)
431			errx(1, "error in help file '%s'", line);
432
433		if (level < optind)
434			break;
435		if (level == optind) {
436			has_sub_topics = 1;
437			printf(" %s", key);
438		}
439	}
440	printf(" ].");
441	if (!has_sub_topics)
442		printf(" No sub-topics found.");
443	printf("\n");
444	exit(1);
445}
446
447#ifdef WITH_BSNMP
448/*
449 * Parse a server specification
450 *
451 * syntax is [trans::][community@][server][:port]
452 */
453static void
454parse_server(char *name)
455{
456	char *p, *s = name;
457
458	/* look for a double colon */
459	for (p = s; *p != '\0'; p++) {
460		if (*p == '\\' && p[1] != '\0') {
461			p++;
462			continue;
463		}
464		if (*p == ':' && p[1] == ':')
465			break;
466	}
467	if (*p != '\0') {
468		if (p > s) {
469			if (p - s == 3 && strncmp(s, "udp", 3) == 0)
470				snmp_client.trans = SNMP_TRANS_UDP;
471			else if (p - s == 6 && strncmp(s, "stream", 6) == 0)
472				snmp_client.trans = SNMP_TRANS_LOC_STREAM;
473			else if (p - s == 5 && strncmp(s, "dgram", 5) == 0)
474				snmp_client.trans = SNMP_TRANS_LOC_DGRAM;
475			else
476				errx(1, "unknown SNMP transport '%.*s'",
477				    (int)(p - s), s);
478		}
479		s = p + 2;
480	}
481
482	/* look for a @ */
483	for (p = s; *p != '\0'; p++) {
484		if (*p == '\\' && p[1] != '\0') {
485			p++;
486			continue;
487		}
488		if (*p == '@')
489			break;
490	}
491
492	if (*p != '\0') {
493		if (p - s > SNMP_COMMUNITY_MAXLEN)
494			err(1, "community string too long");
495		strncpy(snmp_client.read_community, s, p - s);
496		snmp_client.read_community[p - s] = '\0';
497		strncpy(snmp_client.write_community, s, p - s);
498		snmp_client.write_community[p - s] = '\0';
499		s = p + 1;
500	}
501
502	/* look for a colon */
503	for (p = s; *p != '\0'; p++) {
504		if (*p == '\\' && p[1] != '\0') {
505			p++;
506			continue;
507		}
508		if (*p == ':')
509			break;
510	}
511
512	if (*p == ':') {
513		if (p > s) {
514			*p = '\0';
515			snmp_client_set_host(&snmp_client, s);
516			*p = ':';
517		}
518		snmp_client_set_port(&snmp_client, p + 1);
519	} else if (p > s)
520		snmp_client_set_host(&snmp_client, s);
521}
522#endif
523
524int
525main(int argc, char *argv[])
526{
527	int opt, i;
528	const struct cmdtab *match, *cc, *tab;
529
530#ifdef WITH_BSNMP
531	snmp_client_init(&snmp_client);
532	snmp_client.trans = SNMP_TRANS_LOC_STREAM;
533	snmp_client_set_host(&snmp_client, PATH_ILMI_SOCK);
534#endif
535
536#ifdef WITH_BSNMP
537#define	OPTSTR	"htvs:"
538#else
539#define OPTSTR	"htv"
540#endif
541
542	while ((opt = getopt(argc, argv, OPTSTR)) != -1)
543		switch (opt) {
544
545		  case 'h':
546			help_func(0, argv);
547
548#ifdef WITH_BSNMP
549		  case 's':
550			parse_server(optarg);
551			break;
552#endif
553
554		  case 'v':
555			verbose++;
556			break;
557
558		  case 't':
559			notitle = 1;
560			break;
561		}
562
563	if (argv[optind] == NULL)
564		help_func(0, argv);
565
566	argc -= optind;
567	argv += optind;
568
569	if ((main_tab = malloc(sizeof(static_main_tab))) == NULL)
570		err(1, NULL);
571	memcpy(main_tab, static_main_tab, sizeof(static_main_tab));
572
573#ifdef WITH_BSNMP
574	/* XXX while this is compiled in */
575	device_register();
576#endif
577
578	cc = main_tab;
579	i = 0;
580	for (;;) {
581		/*
582		 * Scan the table for a match
583		 */
584		tab = cc;
585		match = NULL;
586		while (cc->string != NULL) {
587			if (substr(argv[i], cc->string)) {
588				if (match != NULL) {
589					printf("Ambiguous option '%s'",
590					    argv[i]);
591					cc = tab;
592					goto subopts;
593				}
594				match = cc;
595			}
596			cc++;
597		}
598		if ((cc = match) == NULL) {
599			printf("Unknown option '%s'", argv[i]);
600			cc = tab;
601			goto subopts;
602		}
603
604		/*
605		 * Have a match. If there is no subtable, there must
606		 * be either a handler or the command is only a help entry.
607		 */
608		if (cc->sub == NULL) {
609			if (cc->func != NULL)
610				break;
611			printf("Unknown option '%s'", argv[i]);
612			cc = tab;
613			goto subopts;
614		}
615
616		/*
617		 * Look at the next argument. If it doesn't exist or it
618		 * looks like a switch, terminate the scan here.
619		 */
620		if (argv[i + 1] == NULL || argv[i + 1][0] == '-') {
621			if (cc->func != NULL)
622				break;
623			printf("Need sub-option for '%s'", argv[i]);
624			cc = cc->sub;
625			goto subopts;
626		}
627
628		cc = cc->sub;
629		i++;
630	}
631
632	argc -= i + 1;
633	argv += i + 1;
634
635	(*cc->func)(argc, argv);
636
637	return (0);
638
639  subopts:
640	printf(". Select one of:\n");
641	while (cc->string != NULL) {
642		if (cc->func != NULL || cc->sub != NULL)
643			printf("%s ", cc->string);
644		cc++;
645	}
646	printf("\n");
647
648	return (1);
649}
650
651void
652verb(const char *fmt, ...)
653{
654	va_list ap;
655
656	if (verbose) {
657		va_start(ap, fmt);
658		vfprintf(stderr, fmt, ap);
659		fprintf(stderr, "\n");
660		va_end(ap);
661	}
662}
663
664void
665heading(const char *fmt, ...)
666{
667	va_list ap;
668
669	if (need_heading) {
670		need_heading = 0;
671		if (!notitle) {
672			va_start(ap, fmt);
673			fprintf(stdout, fmt, ap);
674			va_end(ap);
675		}
676	}
677}
678
679void
680heading_init(void)
681{
682	need_heading = 1;
683}
684
685/*
686 * stringify an enumerated value
687 */
688const char *
689penum(int32_t value, const struct penum *strtab, char *buf)
690{
691	while (strtab->str != NULL) {
692		if (strtab->value == value) {
693			strcpy(buf, strtab->str);
694			return (buf);
695		}
696		strtab++;
697	}
698	warnx("illegal value for enumerated variable '%d'", value);
699	strcpy(buf, "?");
700	return (buf);
701}
702
703/*
704 * And the other way 'round
705 */
706int
707pparse(int32_t *val, const struct penum *tab, const char *str)
708{
709
710	while (tab->str != NULL) {
711		if (strcmp(tab->str, str) == 0) {
712			*val = tab->value;
713			return (0);
714		}
715		tab++;
716	}
717	return (-1);
718}
719
720/*
721 * Parse command line options
722 */
723int
724parse_options(int *pargc, char ***pargv, const struct option *opts)
725{
726	const struct option *o, *m;
727	char *arg;
728	u_long ularg, ularg1;
729	long larg;
730	char *end;
731
732	if (*pargc == 0)
733		return (-1);
734	arg = (*pargv)[0];
735	if (arg[0] != '-' || arg[1] == '\0')
736		return (-1);
737	if (arg[1] == '-' && arg[2] == '\0') {
738		(*pargv)++;
739		(*pargc)--;
740		return (-1);
741	}
742
743	m = NULL;
744	for (o = opts; o->optstr != NULL; o++) {
745		if (strlen(arg + 1) <= strlen(o->optstr) &&
746		    strncmp(arg + 1, o->optstr, strlen(arg + 1)) == 0) {
747			if (m != NULL)
748				errx(1, "ambiguous option '%s'", arg);
749			m = o;
750		}
751	}
752	if (m == NULL)
753		errx(1, "unknown option '%s'", arg);
754
755	(*pargv)++;
756	(*pargc)--;
757
758	if (m->opttype == OPT_NONE)
759		return (m - opts);
760
761	if (m->opttype == OPT_SIMPLE) {
762		*(int *)m->optarg = 1;
763		return (m - opts);
764	}
765
766	if (*pargc == 0)
767		errx(1, "option requires argument '%s'", arg);
768	optarg = *(*pargv)++;
769	(*pargc)--;
770
771	switch (m->opttype) {
772
773	  case OPT_UINT:
774		ularg = strtoul(optarg, &end, 0);
775		if (*end != '\0')
776			errx(1, "bad unsigned integer argument for '%s'", arg);
777		if (ularg > UINT_MAX)
778			errx(1, "argument to large for option '%s'", arg);
779		*(u_int *)m->optarg = (u_int)ularg;
780		break;
781
782	  case OPT_INT:
783		larg = strtol(optarg, &end, 0);
784		if (*end != '\0')
785			errx(1, "bad integer argument for '%s'", arg);
786		if (larg > INT_MAX || larg < INT_MIN)
787			errx(1, "argument out of range for option '%s'", arg);
788		*(int *)m->optarg = (int)larg;
789		break;
790
791	  case OPT_UINT32:
792		ularg = strtoul(optarg, &end, 0);
793		if (*end != '\0')
794			errx(1, "bad unsigned integer argument for '%s'", arg);
795		if (ularg > UINT32_MAX)
796			errx(1, "argument to large for option '%s'", arg);
797		*(uint32_t *)m->optarg = (uint32_t)ularg;
798		break;
799
800	  case OPT_INT32:
801		larg = strtol(optarg, &end, 0);
802		if (*end != '\0')
803			errx(1, "bad integer argument for '%s'", arg);
804		if (larg > INT32_MAX || larg < INT32_MIN)
805			errx(1, "argument out of range for option '%s'", arg);
806		*(int32_t *)m->optarg = (int32_t)larg;
807		break;
808
809	  case OPT_UINT64:
810		*(uint64_t *)m->optarg = strtoull(optarg, &end, 0);
811		if (*end != '\0')
812			errx(1, "bad unsigned integer argument for '%s'", arg);
813		break;
814
815	  case OPT_INT64:
816		*(int64_t *)m->optarg = strtoll(optarg, &end, 0);
817		if (*end != '\0')
818			errx(1, "bad integer argument for '%s'", arg);
819		break;
820
821	  case OPT_FLAG:
822		if (strcasecmp(optarg, "enable") == 0 ||
823		    strcasecmp(optarg, "yes") == 0 ||
824		    strcasecmp(optarg, "true") == 0 ||
825		    strcasecmp(optarg, "on") == 0 ||
826		    strcmp(optarg, "1") == 0)
827			*(int *)m->optarg = 1;
828		else if (strcasecmp(optarg, "disable") == 0 ||
829		    strcasecmp(optarg, "no") == 0 ||
830		    strcasecmp(optarg, "false") == 0 ||
831		    strcasecmp(optarg, "off") == 0 ||
832		    strcmp(optarg, "0") == 0)
833			*(int *)m->optarg = 0;
834		else
835			errx(1, "bad boolean argument to '%s'", arg);
836		break;
837
838	  case OPT_VCI:
839		ularg = strtoul(optarg, &end, 0);
840		if (*end == '.') {
841			ularg1 = strtoul(end + 1, &end, 0);
842		} else {
843			ularg1 = ularg;
844			ularg = 0;
845		}
846		if (*end != '\0')
847			errx(1, "bad VCI value for option '%s'", arg);
848		if (ularg > 0xff)
849			errx(1, "VPI value too large for option '%s'", arg);
850		if (ularg1 > 0xffff)
851			errx(1, "VCI value too large for option '%s'", arg);
852		((u_int *)m->optarg)[0] = ularg;
853		((u_int *)m->optarg)[1] = ularg1;
854		break;
855
856	  case OPT_STRING:
857		if (m->optarg != NULL)
858			*(const char **)m->optarg = optarg;
859		break;
860
861	  default:
862		errx(1, "(internal) bad option type %u for '%s'",
863		    m->opttype, arg);
864	}
865	return (m - opts);
866}
867
868/*
869 * for compiled-in modules
870 */
871void
872register_module(const struct amodule *mod)
873{
874	main_tab_size++;
875	if ((main_tab = realloc(main_tab, main_tab_size * sizeof(main_tab[0])))
876	    == NULL)
877		err(1, NULL);
878	main_tab[main_tab_size - 2] = *mod->cmd;
879	memset(&main_tab[main_tab_size - 1], 0, sizeof(main_tab[0]));
880}
881