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$");
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 <stdint.h>
39#include <fnmatch.h>
40#include <dirent.h>
41#ifdef WITH_BSNMP
42#include <bsnmp/asn1.h>
43#include <bsnmp/snmp.h>
44#include <bsnmp/snmpclient.h>
45#endif
46
47#include "atmconfig.h"
48#include "private.h"
49
50/* verbosity level */
51static int verbose;
52
53/* notitle option */
54static int notitle;
55
56/* need to put heading before next output */
57static int need_heading;
58
59/*
60 * TOP LEVEL commands
61 */
62static void help_func(int argc, char *argv[]) __dead2;
63
64static const struct cmdtab static_main_tab[] = {
65	{ "help",	NULL,		help_func },
66	{ "options",	NULL,		NULL },
67	{ "commands",	NULL,		NULL },
68	{ "diag",	diag_tab,	NULL },
69	{ "natm",	natm_tab,	NULL },
70	{ NULL,		NULL,		NULL }
71};
72
73static struct cmdtab *main_tab = NULL;
74static size_t main_tab_size = sizeof(static_main_tab) /
75	sizeof(static_main_tab[0]);
76
77static int
78substr(const char *s1, const char *s2)
79{
80	return (strlen(s1) <= strlen(s2) && strncmp(s1, s2, strlen(s1)) == 0);
81}
82
83/*
84 * Current help file state
85 */
86struct help_file {
87	int	file_state;	/* 0:looking for main file, 1:found, 2:other */
88	const char *p_start;	/* current path pointer */
89	const char *p_end;	/* end of current path in path */
90	char	*dirname;	/* directory name */
91	DIR	*dir;		/* open directory */
92	char	*fname;		/* current filename */
93	FILE	*fp;		/* open file */
94	char	line[LINE_MAX];	/* current line */
95	u_int	fcnt;		/* count of files found */
96};
97
98struct help_pos {
99	off_t	pos;		/* file position */
100	u_int	fcnt;		/* number of file */
101	char	*fname;		/* name of file */
102	const char *p_start;	/* current path pointer */
103	const char *p_end;	/* end of current path in path */
104};
105
106static int
107help_next_file(struct help_file *hp)
108{
109	const char *fpat;
110	struct dirent *ent;
111
112	if (hp->file_state == 3)
113		return (-1);
114
115	if (hp->file_state == 0)
116		fpat = FILE_HELP;
117	else
118		fpat = FILE_HELP_OTHERS;
119
120	if (hp->file_state == 0 || hp->file_state == 1) {
121		/* start from beginning */
122		hp->p_start = PATH_HELP;
123		hp->file_state++;
124	}
125
126  try_file:
127	if (hp->dir != NULL) {
128		/* directory open (must be state 2) */
129		while ((ent = readdir(hp->dir)) != NULL) {
130			if (fnmatch(fpat, ent->d_name, FNM_NOESCAPE) != 0)
131				continue;
132			if (asprintf(&hp->fname, "%s/%s", hp->dirname,
133			    ent->d_name) == -1)
134				err(1, NULL);
135			if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
136				hp->fcnt++;
137				return (0);
138			}
139			free(hp->fname);
140		}
141		/* end of directory */
142		closedir(hp->dir);
143		hp->dir = NULL;
144		free(hp->dirname);
145		goto next_path;
146	}
147
148	/* nothing open - advanc to new path element */
149  try_path:
150	for (hp->p_end = hp->p_start; *hp->p_end != '\0' &&
151	    *hp->p_end != ':'; hp->p_end++)
152		;
153
154	if (asprintf(&hp->dirname, "%.*s", (int)(hp->p_end - hp->p_start),
155	    hp->p_start) == -1)
156		err(1, NULL);
157
158	if (hp->file_state == 1) {
159		/* just try to open */
160		if (asprintf(&hp->fname, "%s/%s", hp->dirname, fpat) == -1)
161			err(1, NULL);
162		if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
163			hp->fcnt++;
164			return (0);
165		}
166		free(hp->fname);
167
168		goto next_path;
169	}
170
171	/* open directory */
172	if ((hp->dir = opendir(hp->dirname)) != NULL)
173		goto try_file;
174
175	free(hp->dirname);
176
177  next_path:
178	hp->p_start = hp->p_end;
179	if (*hp->p_start == '\0') {
180		/* end of path */
181		if (hp->file_state == 1)
182			errx(1, "help file not found");
183		return (-1);
184	}
185	hp->p_start++;
186	goto try_path;
187
188}
189
190/*
191 * Save current file position
192 */
193static void
194help_file_tell(struct help_file *hp, struct help_pos *pos)
195{
196	if (pos->fname != NULL)
197		free(pos->fname);
198	if ((pos->fname = strdup(hp->fname)) == NULL)
199		err(1, NULL);
200	pos->fcnt = hp->fcnt;
201	pos->p_start = hp->p_start;
202	pos->p_end = hp->p_end;
203	if ((pos->pos = ftello(hp->fp)) == -1)
204		err(1, "%s", pos->fname);
205}
206
207/*
208 * Go to that position
209 *
210 * We can go either to the original help file or back in the current file.
211 */
212static void
213help_file_seek(struct help_file *hp, struct help_pos *pos)
214{
215	hp->p_start = pos->p_start;
216	hp->p_end = pos->p_end;
217	hp->fcnt = pos->fcnt;
218
219	if (hp->dir != NULL) {
220		free(hp->dirname);
221		closedir(hp->dir);
222		hp->dir = NULL;
223	}
224
225	if (hp->fp != NULL &&strcmp(hp->fname, pos->fname) != 0) {
226		free(hp->fname);
227		fclose(hp->fp);
228		hp->fp = NULL;
229	}
230	if (hp->fp == NULL) {
231		if ((hp->fname = strdup(pos->fname)) == NULL)
232			err(1, NULL);
233		if ((hp->fp = fopen(hp->fname, "r")) == NULL)
234			err(1, "reopen %s", hp->fname);
235	}
236	if (fseeko(hp->fp, pos->pos, SEEK_SET) == -1)
237		err(1, "seek %s", hp->fname);
238
239	if (pos->fcnt == 1)
240		/* go back to state 1 */
241		hp->file_state = 1;
242	else
243		/* lock */
244		hp->file_state = 3;
245}
246
247/*
248 * Rewind to position 0
249 */
250static void
251help_file_rewind(struct help_file *hp)
252{
253
254	if (hp->file_state == 1) {
255		if (fseeko(hp->fp, (off_t)0, SEEK_SET) == -1)
256			err(1, "rewind help file");
257		return;
258	}
259
260	if (hp->dir != NULL) {
261		free(hp->dirname);
262		closedir(hp->dir);
263		hp->dir = NULL;
264	}
265
266	if (hp->fp != NULL) {
267		free(hp->fname);
268		fclose(hp->fp);
269		hp->fp = NULL;
270	}
271	memset(hp, 0, sizeof(*hp));
272}
273
274/*
275 * Get next line from a help file
276 */
277static const char *
278help_next_line(struct help_file *hp)
279{
280	for (;;) {
281		if (hp->fp != NULL) {
282			if (fgets(hp->line, sizeof(hp->line), hp->fp) != NULL)
283				return (hp->line);
284			if (ferror(hp->fp))
285				err(1, "%s", hp->fname);
286			free(hp->fname);
287
288			fclose(hp->fp);
289			hp->fp = NULL;
290		}
291		if (help_next_file(hp) == -1)
292			return (NULL);
293	}
294
295}
296
297/*
298 * This function prints the available 0-level help topics from all
299 * other help files by scanning the files. It assumes, that this is called
300 * only from the main help file.
301 */
302static void
303help_get_0topics(struct help_file *hp)
304{
305	struct help_pos save;
306	const char *line;
307
308	memset(&save, 0, sizeof(save));
309	help_file_tell(hp, &save);
310
311	help_file_rewind(hp);
312	while ((line = help_next_line(hp)) != NULL) {
313		if (line[0] == '^' && line[1] == '^')
314			printf("%s", line + 2);
315	}
316	help_file_seek(hp, &save);
317}
318
319/*
320 * Function to print help. The help argument is in argv[0] here.
321 */
322static void
323help_func(int argc, char *argv[])
324{
325	struct help_file hfile;
326	struct help_pos match, last_match;
327	const char *line;
328	char key[100];
329	int level;
330	int i, has_sub_topics;
331
332	memset(&hfile, 0, sizeof(hfile));
333	memset(&match, 0, sizeof(match));
334	memset(&last_match, 0, sizeof(last_match));
335
336	if (argc == 0) {
337		/* only 'help' - show intro */
338		if ((argv[0] = strdup("intro")) == NULL)
339			err(1, NULL);
340		argc = 1;
341	}
342
343	optind = 0;
344	match.pos = -1;
345	last_match.pos = -1;
346	for (;;) {
347		/* read next line */
348		if ((line = help_next_line(&hfile)) == NULL) {
349			/* EOF */
350			level = 999;
351			goto stop;
352		}
353		if (line[0] != '^' || line[1] == '^')
354			continue;
355
356		if (sscanf(line + 1, "%d%99s", &level, key) != 2)
357			errx(1, "error in help file '%s'", line);
358
359		if (level < optind) {
360  stop:
361			/* next higher level entry - stop this level */
362			if (match.pos == -1) {
363				/* not found */
364				goto not_found;
365			}
366			/* go back to the match */
367			help_file_seek(&hfile, &match);
368			last_match = match;
369			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