1139826Simp/*
253541Sshin * Copyright (c) 2001-2003
353541Sshin *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
453541Sshin * 	All rights reserved.
553541Sshin *
653541Sshin * Redistribution and use in source and binary forms, with or without
753541Sshin * modification, are permitted provided that the following conditions
853541Sshin * are met:
953541Sshin * 1. Redistributions of source code must retain the above copyright
1053541Sshin *    notice, this list of conditions and the following disclaimer.
1153541Sshin * 2. Redistributions in binary form must reproduce the above copyright
1253541Sshin *    notice, this list of conditions and the following disclaimer in the
1353541Sshin *    documentation and/or other materials provided with the distribution.
1453541Sshin *
1553541Sshin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1653541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1753541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1853541Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1953541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2053541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2153541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2253541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2353541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2453541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2553541Sshin * SUCH DAMAGE.
2653541Sshin *
2753541Sshin * Author: Hartmut Brandt <harti@freebsd.org>
28174510Sobrien */
29174510Sobrien#include <sys/cdefs.h>
3053541Sshin__FBSDID("$FreeBSD: releng/10.3/sbin/atm/atmconfig/main.c 270824 2014-08-29 18:26:55Z ngie $");
3153541Sshin
32174510Sobrien#include <sys/types.h>
33174510Sobrien#include <sys/sysctl.h>
34174510Sobrien#include <netdb.h>
3562587Sitojun#include <stdarg.h>
3662587Sitojun#include <ctype.h>
37142215Sglebius#include <limits.h>
38178167Sqingli#include <stdint.h>
3955009Sshin#include <fnmatch.h>
4053541Sshin#include <dirent.h>
4153541Sshin#ifdef WITH_BSNMP
4253541Sshin#include <bsnmp/asn1.h>
43186119Sqingli#include <bsnmp/snmp.h>
44186119Sqingli#include <bsnmp/snmpclient.h>
4553541Sshin#endif
4653541Sshin
4753541Sshin#include "atmconfig.h"
4853541Sshin#include "private.h"
4953541Sshin
5053541Sshin/* verbosity level */
5153541Sshinstatic int verbose;
5253541Sshin
5378064Sume/* notitle option */
5453541Sshinstatic int notitle;
5553541Sshin
5653541Sshin/* need to put heading before next output */
5753541Sshinstatic int need_heading;
58147306Sbrooks
5953541Sshin/*
60178167Sqingli * TOP LEVEL commands
61178167Sqingli */
62178167Sqinglistatic void help_func(int argc, char *argv[]) __dead2;
6353541Sshin
6453541Sshinstatic const struct cmdtab static_main_tab[] = {
6553541Sshin	{ "help",	NULL,		help_func },
66186119Sqingli	{ "options",	NULL,		NULL },
67186119Sqingli	{ "commands",	NULL,		NULL },
6853541Sshin	{ "diag",	diag_tab,	NULL },
69151477Ssuz	{ "natm",	natm_tab,	NULL },
7062587Sitojun	{ NULL,		NULL,		NULL }
7153541Sshin};
72148385Sume
7353541Sshinstatic struct cmdtab *main_tab = NULL;
7462587Sitojunstatic size_t main_tab_size = sizeof(static_main_tab) /
75211193Swill	sizeof(static_main_tab[0]);
76211501Sanchie
7753541Sshinstatic int
7862587Sitojunsubstr(const char *s1, const char *s2)
7953541Sshin{
8062587Sitojun	return (strlen(s1) <= strlen(s2) && strncmp(s1, s2, strlen(s1)) == 0);
81175162Sobrien}
82175162Sobrien
83175162Sobrien/*
84191816Szec * Current help file state
85175162Sobrien */
86175162Sobrienstruct help_file {
87175162Sobrien	int	file_state;	/* 0:looking for main file, 1:found, 2:other */
8853541Sshin	const char *p_start;	/* current path pointer */
89207369Sbz	const char *p_end;	/* end of current path in path */
90207369Sbz	char	*dirname;	/* directory name */
91195727Srwatson	DIR	*dir;		/* open directory */
92195727Srwatson	char	*fname;		/* current filename */
93195699Srwatson	FILE	*fp;		/* open file */
9453541Sshin	char	line[LINE_MAX];	/* current line */
95108470Sschweikh	u_int	fcnt;		/* count of files found */
9653541Sshin};
9753541Sshin
98148987Sumestruct help_pos {
9953541Sshin	off_t	pos;		/* file position */
10053541Sshin	u_int	fcnt;		/* number of file */
101171259Sdelphij	char	*fname;		/* name of file */
10253541Sshin	const char *p_start;	/* current path pointer */
10353541Sshin	const char *p_end;	/* end of current path in path */
10453541Sshin};
10562587Sitojun
10653541Sshinstatic int
10753541Sshinhelp_next_file(struct help_file *hp)
10862587Sitojun{
10953541Sshin	const char *fpat;
11053541Sshin	struct dirent *ent;
111142215Sglebius
11253541Sshin	if (hp->file_state == 3)
11353541Sshin		return (-1);
11453541Sshin
11553541Sshin	if (hp->file_state == 0)
11662587Sitojun		fpat = FILE_HELP;
117165118Sbz	else
11853541Sshin		fpat = FILE_HELP_OTHERS;
11978064Sume
12078064Sume	if (hp->file_state == 0 || hp->file_state == 1) {
12178064Sume		/* start from beginning */
12278064Sume		hp->p_start = PATH_HELP;
12378064Sume		hp->file_state++;
12478064Sume	}
125190964Srwatson
12678064Sume  try_file:
12778064Sume	if (hp->dir != NULL) {
12878064Sume		/* directory open (must be state 2) */
12978064Sume		while ((ent = readdir(hp->dir)) != NULL) {
13078064Sume			if (fnmatch(fpat, ent->d_name, FNM_NOESCAPE) != 0)
131148385Sume				continue;
132148385Sume			if (asprintf(&hp->fname, "%s/%s", hp->dirname,
13378064Sume			    ent->d_name) == -1)
13453541Sshin				err(1, NULL);
13578064Sume			if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
13678064Sume				hp->fcnt++;
137165118Sbz				return (0);
138165118Sbz			}
13978064Sume			free(hp->fname);
14053541Sshin		}
14153541Sshin		/* end of directory */
14253541Sshin		closedir(hp->dir);
143148987Sume		hp->dir = NULL;
144120941Sume		free(hp->dirname);
14595023Ssuz		goto next_path;
146120941Sume	}
147120941Sume
148120941Sume	/* nothing open - advanc to new path element */
14995023Ssuz  try_path:
15053541Sshin	for (hp->p_end = hp->p_start; *hp->p_end != '\0' &&
15178064Sume	    *hp->p_end != ':'; hp->p_end++)
152120941Sume		;
15353541Sshin
15453541Sshin	if (asprintf(&hp->dirname, "%.*s", (int)(hp->p_end - hp->p_start),
155185348Szec	    hp->p_start) == -1)
156183529Scperciva		err(1, NULL);
157183529Scperciva
158183529Scperciva	if (hp->file_state == 1) {
159183529Scperciva		/* just try to open */
160183529Scperciva		if (asprintf(&hp->fname, "%s/%s", hp->dirname, fpat) == -1)
161183529Scperciva			err(1, NULL);
162183529Scperciva		if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
163183529Scperciva			hp->fcnt++;
164183529Scperciva			return (0);
165183529Scperciva		}
166183529Scperciva		free(hp->fname);
167183529Scperciva
168186119Sqingli		goto next_path;
169183529Scperciva	}
170183529Scperciva
171183529Scperciva	/* open directory */
172183529Scperciva	if ((hp->dir = opendir(hp->dirname)) != NULL)
17353541Sshin		goto try_file;
17453541Sshin
17553541Sshin	free(hp->dirname);
17678064Sume
17753541Sshin  next_path:
17853541Sshin	hp->p_start = hp->p_end;
17953541Sshin	if (*hp->p_start == '\0') {
18053541Sshin		/* end of path */
18153541Sshin		if (hp->file_state == 1)
18253541Sshin			errx(1, "help file not found");
18378064Sume		return (-1);
18478064Sume	}
18578064Sume	hp->p_start++;
18678064Sume	goto try_path;
18753541Sshin
18853541Sshin}
18953541Sshin
19095023Ssuz/*
19153541Sshin * Save current file position
19253541Sshin */
193120941Sumestatic void
19453541Sshinhelp_file_tell(struct help_file *hp, struct help_pos *pos)
19578064Sume{
19678064Sume	if (pos->fname != NULL)
19753541Sshin		free(pos->fname);
19853541Sshin	if ((pos->fname = strdup(hp->fname)) == NULL)
19953541Sshin		err(1, NULL);
20053541Sshin	pos->fcnt = hp->fcnt;
20153541Sshin	pos->p_start = hp->p_start;
20253541Sshin	pos->p_end = hp->p_end;
20353541Sshin	if ((pos->pos = ftello(hp->fp)) == -1)
20453541Sshin		err(1, "%s", pos->fname);
20553541Sshin}
20653541Sshin
20753541Sshin/*
20853541Sshin * Go to that position
20953541Sshin *
21053541Sshin * We can go either to the original help file or back in the current file.
21153541Sshin */
21253541Sshinstatic void
21353541Sshinhelp_file_seek(struct help_file *hp, struct help_pos *pos)
21453541Sshin{
21553541Sshin	hp->p_start = pos->p_start;
21653541Sshin	hp->p_end = pos->p_end;
21753541Sshin	hp->fcnt = pos->fcnt;
21853541Sshin
21953541Sshin	if (hp->dir != NULL) {
22053541Sshin		free(hp->dirname);
22153541Sshin		closedir(hp->dir);
222142215Sglebius		hp->dir = NULL;
223211157Swill	}
224151465Ssuz
225142215Sglebius	if (hp->fp != NULL &&strcmp(hp->fname, pos->fname) != 0) {
22653541Sshin		free(hp->fname);
22753541Sshin		fclose(hp->fp);
228151465Ssuz		hp->fp = NULL;
22953541Sshin	}
23053541Sshin	if (hp->fp == NULL) {
231121765Ssam		if ((hp->fname = strdup(pos->fname)) == NULL)
232178167Sqingli			err(1, NULL);
233178167Sqingli		if ((hp->fp = fopen(hp->fname, "r")) == NULL)
234178167Sqingli			err(1, "reopen %s", hp->fname);
23553541Sshin	}
236171260Sdelphij	if (fseeko(hp->fp, pos->pos, SEEK_SET) == -1)
23753541Sshin		err(1, "seek %s", hp->fname);
23853541Sshin
23953541Sshin	if (pos->fcnt == 1)
24053541Sshin		/* go back to state 1 */
241178167Sqingli		hp->file_state = 1;
242178167Sqingli	else
243178167Sqingli		/* lock */
244178167Sqingli		hp->file_state = 3;
245178167Sqingli}
246178167Sqingli
24753541Sshin/*
248178167Sqingli * Rewind to position 0
249121765Ssam */
250121765Ssamstatic void
251121765Ssamhelp_file_rewind(struct help_file *hp)
252187946Sbz{
253121765Ssam
25453541Sshin	if (hp->file_state == 1) {
25562587Sitojun		if (fseeko(hp->fp, (off_t)0, SEEK_SET) == -1)
25653541Sshin			err(1, "rewind help file");
25762587Sitojun		return;
25862587Sitojun	}
25962587Sitojun
26053541Sshin	if (hp->dir != NULL) {
26162587Sitojun		free(hp->dirname);
26262587Sitojun		closedir(hp->dir);
26353541Sshin		hp->dir = NULL;
26453541Sshin	}
265151479Ssuz
26653541Sshin	if (hp->fp != NULL) {
26778064Sume		free(hp->fname);
26853541Sshin		fclose(hp->fp);
26953541Sshin		hp->fp = NULL;
27053541Sshin	}
27162587Sitojun	memset(hp, 0, sizeof(*hp));
27253541Sshin}
27353541Sshin
27453541Sshin/*
27553541Sshin * Get next line from a help file
27653541Sshin */
27762587Sitojunstatic const char *
27853541Sshinhelp_next_line(struct help_file *hp)
27953541Sshin{
280120941Sume	for (;;) {
28153541Sshin		if (hp->fp != NULL) {
282165118Sbz			if (fgets(hp->line, sizeof(hp->line), hp->fp) != NULL)
283120941Sume				return (hp->line);
28478064Sume			if (ferror(hp->fp))
28553541Sshin				err(1, "%s", hp->fname);
28653541Sshin			free(hp->fname);
28753541Sshin
288120941Sume			fclose(hp->fp);
289165118Sbz			hp->fp = NULL;
29062587Sitojun		}
29153541Sshin		if (help_next_file(hp) == -1)
29253541Sshin			return (NULL);
29353541Sshin	}
29453541Sshin
29553541Sshin}
29653541Sshin
29753541Sshin/*
29853541Sshin * This function prints the available 0-level help topics from all
29953541Sshin * other help files by scanning the files. It assumes, that this is called
30053541Sshin * only from the main help file.
30153541Sshin */
30253541Sshinstatic void
30353541Sshinhelp_get_0topics(struct help_file *hp)
30453541Sshin{
30553541Sshin	struct help_pos save;
30653541Sshin	const char *line;
30753541Sshin
308148987Sume	memset(&save, 0, sizeof(save));
30953541Sshin	help_file_tell(hp, &save);
31053541Sshin
31153541Sshin	help_file_rewind(hp);
31253541Sshin	while ((line = help_next_line(hp)) != NULL) {
31353541Sshin		if (line[0] == '^' && line[1] == '^')
31453541Sshin			printf("%s", line + 2);
31553541Sshin	}
31662587Sitojun	help_file_seek(hp, &save);
31753541Sshin}
31853541Sshin
31953541Sshin/*
32053541Sshin * Function to print help. The help argument is in argv[0] here.
32153541Sshin */
32253541Sshinstatic void
32353541Sshinhelp_func(int argc, char *argv[])
32453541Sshin{
32553541Sshin	struct help_file hfile;
32653541Sshin	struct help_pos match, last_match;
32753541Sshin	const char *line;
328148385Sume	char key[100];
329148385Sume	int level;
330148385Sume	int i, has_sub_topics;
331148385Sume
332148385Sume	memset(&hfile, 0, sizeof(hfile));
333148385Sume	memset(&match, 0, sizeof(match));
334120941Sume	memset(&last_match, 0, sizeof(last_match));
335181803Sbz
336120941Sume	if (argc == 0) {
33762587Sitojun		/* only 'help' - show intro */
33853541Sshin		if ((argv[0] = strdup("intro")) == NULL)
33953541Sshin			err(1, NULL);
340120941Sume		argc = 1;
341120941Sume	}
34253541Sshin
34353541Sshin	optind = 0;
344120941Sume	match.pos = -1;
345181803Sbz	last_match.pos = -1;
346120941Sume	for (;;) {
34762587Sitojun		/* read next line */
348194760Srwatson		if ((line = help_next_line(&hfile)) == NULL) {
349194760Srwatson			/* EOF */
35062587Sitojun			level = 999;
35153541Sshin			goto stop;
35253541Sshin		}
35353541Sshin		if (line[0] != '^' || line[1] == '^')
354165118Sbz			continue;
355165118Sbz
356165118Sbz		if (sscanf(line + 1, "%d%99s", &level, key) != 2)
357165118Sbz			errx(1, "error in help file '%s'", line);
358165118Sbz
359165118Sbz		if (level < optind) {
360190964Srwatson  stop:
361194760Srwatson			/* next higher level entry - stop this level */
362194760Srwatson			if (match.pos == -1) {
36362587Sitojun				/* not found */
36453541Sshin				goto not_found;
36553541Sshin			}
36653541Sshin			/* go back to the match */
367108470Sschweikh			help_file_seek(&hfile, &match);
36853541Sshin			last_match = match;
36953541Sshin			memset(&match, 0, sizeof(match));
37053541Sshin			match.pos = -1;
37153541Sshin
37253541Sshin			/* go to next key */
373148987Sume			if (++optind >= argc)
374171259Sdelphij				break;
375171259Sdelphij		}
376171259Sdelphij		if (level == optind) {
37753541Sshin			if (substr(argv[optind], key)) {
37853541Sshin				if (match.pos != -1) {
379186119Sqingli					printf("Ambiguous topic.");
380186119Sqingli					goto list_topics;
38153541Sshin				}
38253541Sshin				help_file_tell(&hfile, &match);
383211501Sanchie			}
38453541Sshin		}
38553541Sshin	}
38653541Sshin
38753541Sshin	/* before breaking above we have seeked back to the matching point */
38862587Sitojun	for (;;) {
38953541Sshin		if ((line = help_next_line(&hfile)) == NULL)
390148385Sume			break;
391120941Sume
39253541Sshin		if (line[0] == '#')
39353541Sshin			continue;
39453541Sshin		if (line[0] == '^') {
39562587Sitojun			if (line[1] == '^')
39662587Sitojun				continue;
39762587Sitojun			break;
39862587Sitojun		}
39962587Sitojun		if (strncmp(line, "$MAIN", 5) == 0) {
40062587Sitojun			help_get_0topics(&hfile);
40162587Sitojun			continue;
40262587Sitojun		}
40353541Sshin		printf("%s", line);
40462587Sitojun	}
40553541Sshin
406111119Simp	exit(0);
40762587Sitojun
408111119Simp  not_found:
40962587Sitojun	printf("Topic not found.");
41062587Sitojun
41162587Sitojun  list_topics:
41262587Sitojun	printf(" Use one of:\natmconfig help");
41362587Sitojun	for (i = 0; i < optind; i++)
41462587Sitojun		printf(" %s", argv[i]);
41562587Sitojun
41678064Sume	printf(" [");
41762587Sitojun
418215559Sbz	/* list all the keys at this level */
419215559Sbz	if (last_match.pos == -1)
42053541Sshin		/* go back to start of help */
42153541Sshin		help_file_rewind(&hfile);
42253541Sshin	else
42353541Sshin		help_file_seek(&hfile, &last_match);
42453541Sshin
42553541Sshin	has_sub_topics = 0;
42653541Sshin	while ((line = help_next_line(&hfile)) != NULL) {
42753541Sshin		if (line[0] == '#' || line[0] != '^' || line[1] == '^')
42853541Sshin			continue;
42995023Ssuz
43053541Sshin		if (sscanf(line + 1, "%d%99s", &level, key) != 2)
43153541Sshin			errx(1, "error in help file '%s'", line);
43253541Sshin
43353541Sshin		if (level < optind)
43462587Sitojun			break;
43562587Sitojun		if (level == optind) {
43653541Sshin			has_sub_topics = 1;
43753541Sshin			printf(" %s", key);
43853541Sshin		}
43953541Sshin	}
44053541Sshin	printf(" ].");
44153541Sshin	if (!has_sub_topics)
44253541Sshin		printf(" No sub-topics found.");
443148385Sume	printf("\n");
44453541Sshin	exit(1);
44553541Sshin}
44653541Sshin
44753541Sshin#ifdef WITH_BSNMP
448148385Sume/*
449148385Sume * Parse a server specification
45053541Sshin *
45153541Sshin * syntax is [trans::][community@][server][:port]
452194760Srwatson */
453194760Srwatsonstatic void
45453541Sshinparse_server(char *name)
45553541Sshin{
45653541Sshin	char *p, *s = name;
45753541Sshin
45853541Sshin	/* look for a double colon */
45953541Sshin	for (p = s; *p != '\0'; p++) {
46053541Sshin		if (*p == '\\' && p[1] != '\0') {
46153541Sshin			p++;
46253541Sshin			continue;
46353541Sshin		}
46453541Sshin		if (*p == ':' && p[1] == ':')
46553541Sshin			break;
46653541Sshin	}
467148385Sume	if (*p != '\0') {
46853541Sshin		if (p > s) {
469216022Sbz			if (p - s == 3 && strncmp(s, "udp", 3) == 0)
47053541Sshin				snmp_client.trans = SNMP_TRANS_UDP;
471216022Sbz			else if (p - s == 6 && strncmp(s, "stream", 6) == 0)
472216022Sbz				snmp_client.trans = SNMP_TRANS_LOC_STREAM;
473216022Sbz			else if (p - s == 5 && strncmp(s, "dgram", 5) == 0)
474216022Sbz				snmp_client.trans = SNMP_TRANS_LOC_DGRAM;
475216022Sbz			else
476216022Sbz				errx(1, "unknown SNMP transport '%.*s'",
477216022Sbz				    (int)(p - s), s);
478216022Sbz		}
479216022Sbz		s = p + 2;
480216022Sbz	}
481216022Sbz
482216022Sbz	/* look for a @ */
483216022Sbz	for (p = s; *p != '\0'; p++) {
484216022Sbz		if (*p == '\\' && p[1] != '\0') {
485216022Sbz			p++;
486216022Sbz			continue;
487216022Sbz		}
488216022Sbz		if (*p == '@')
489148385Sume			break;
490194760Srwatson	}
491194760Srwatson
492216022Sbz	if (*p != '\0') {
493194760Srwatson		if (p - s > SNMP_COMMUNITY_MAXLEN)
494194760Srwatson			err(1, "community string too long");
495148385Sume		strncpy(snmp_client.read_community, s, p - s);
496148385Sume		snmp_client.read_community[p - s] = '\0';
497216022Sbz		strncpy(snmp_client.write_community, s, p - s);
498148385Sume		snmp_client.write_community[p - s] = '\0';
499148385Sume		s = p + 1;
500148385Sume	}
501148385Sume
502148385Sume	/* look for a colon */
503148385Sume	for (p = s; *p != '\0'; p++) {
504194777Sbz		if (*p == '\\' && p[1] != '\0') {
505194777Sbz			p++;
506194777Sbz			continue;
507165118Sbz		}
508148385Sume		if (*p == ':')
509148385Sume			break;
510148385Sume	}
511165118Sbz
512165118Sbz	if (*p == ':') {
513148385Sume		if (p > s) {
51453541Sshin			*p = '\0';
515216022Sbz			snmp_client_set_host(&snmp_client, s);
51653541Sshin			*p = ':';
51753541Sshin		}
51853541Sshin		snmp_client_set_port(&snmp_client, p + 1);
51953541Sshin	} else if (p > s)
52053541Sshin		snmp_client_set_host(&snmp_client, s);
521148385Sume}
522148385Sume#endif
523148385Sume
52453541Sshinint
525216022Sbzmain(int argc, char *argv[])
52653541Sshin{
52753541Sshin	int opt, i;
52853541Sshin	const struct cmdtab *match, *cc, *tab;
52953541Sshin
53053541Sshin#ifdef WITH_BSNMP
53153541Sshin	snmp_client_init(&snmp_client);
532121315Sume	snmp_client.trans = SNMP_TRANS_LOC_STREAM;
53353541Sshin	snmp_client_set_host(&snmp_client, PATH_ILMI_SOCK);
53453541Sshin#endif
53553541Sshin
53653541Sshin#ifdef WITH_BSNMP
53753541Sshin#define	OPTSTR	"htvs:"
53853541Sshin#else
53953541Sshin#define OPTSTR	"htv"
54053541Sshin#endif
54153541Sshin
54253541Sshin	while ((opt = getopt(argc, argv, OPTSTR)) != -1)
54353541Sshin		switch (opt) {
54453541Sshin
54553541Sshin		  case 'h':
54653541Sshin			help_func(0, argv);
54753541Sshin
54853541Sshin#ifdef WITH_BSNMP
54953541Sshin		  case 's':
55053541Sshin			parse_server(optarg);
551120941Sume			break;
55253541Sshin#endif
55353541Sshin
55453541Sshin		  case 'v':
55553541Sshin			verbose++;
55653541Sshin			break;
55753541Sshin
55853541Sshin		  case 't':
55953541Sshin			notitle = 1;
56053541Sshin			break;
56153541Sshin		}
56253541Sshin
563120941Sume	if (argv[optind] == NULL)
564120941Sume		help_func(0, argv);
56553541Sshin
566211501Sanchie	argc -= optind;
567211501Sanchie	argv += optind;
568211501Sanchie
569211501Sanchie	if ((main_tab = malloc(sizeof(static_main_tab))) == NULL)
570211501Sanchie		err(1, NULL);
571211501Sanchie	memcpy(main_tab, static_main_tab, sizeof(static_main_tab));
572211501Sanchie
573211501Sanchie#ifdef WITH_BSNMP
574211501Sanchie	/* XXX while this is compiled in */
575151536Ssuz	device_register();
576148385Sume#endif
577148385Sume
578190964Srwatson	cc = main_tab;
579148385Sume	i = 0;
580148385Sume	for (;;) {
581148385Sume		/*
58253541Sshin		 * Scan the table for a match
583148385Sume		 */
584148385Sume		tab = cc;
585148385Sume		match = NULL;
586148385Sume		while (cc->string != NULL) {
587148385Sume			if (substr(argv[i], cc->string)) {
588148385Sume				if (match != NULL) {
589148385Sume					printf("Ambiguous option '%s'",
590148385Sume					    argv[i]);
59153541Sshin					cc = tab;
59253541Sshin					goto subopts;
59353541Sshin				}
59453541Sshin				match = cc;
59553541Sshin			}
59653541Sshin			cc++;
597148987Sume		}
59862587Sitojun		if ((cc = match) == NULL) {
59962587Sitojun			printf("Unknown option '%s'", argv[i]);
60062587Sitojun			cc = tab;
60162587Sitojun			goto subopts;
60253541Sshin		}
60353541Sshin
604171259Sdelphij		/*
60553541Sshin		 * Have a match. If there is no subtable, there must
60653541Sshin		 * be either a handler or the command is only a help entry.
60753541Sshin		 */
60862587Sitojun		if (cc->sub == NULL) {
60953541Sshin			if (cc->func != NULL)
61062587Sitojun				break;
61162587Sitojun			printf("Unknown option '%s'", argv[i]);
61262587Sitojun			cc = tab;
61362587Sitojun			goto subopts;
61462587Sitojun		}
61553541Sshin
61653541Sshin		/*
617186468Skmacy		 * Look at the next argument. If it doesn't exist or it
61853541Sshin		 * looks like a switch, terminate the scan here.
619186119Sqingli		 */
62053541Sshin		if (argv[i + 1] == NULL || argv[i + 1][0] == '-') {
621186119Sqingli			if (cc->func != NULL)
622211501Sanchie				break;
623186119Sqingli			printf("Need sub-option for '%s'", argv[i]);
624165118Sbz			cc = cc->sub;
62553541Sshin			goto subopts;
62653541Sshin		}
62778064Sume
62878064Sume		cc = cc->sub;
629165118Sbz		i++;
630165118Sbz	}
63178064Sume
63262587Sitojun	argc -= i + 1;
63362587Sitojun	argv += i + 1;
63462587Sitojun
63562587Sitojun	(*cc->func)(argc, argv);
63662587Sitojun
63762587Sitojun	return (0);
63862587Sitojun
63962587Sitojun  subopts:
640190964Srwatson	printf(". Select one of:\n");
64153541Sshin	while (cc->string != NULL) {
64253541Sshin		if (cc->func != NULL || cc->sub != NULL)
64362587Sitojun			printf("%s ", cc->string);
644148385Sume		cc++;
64562587Sitojun	}
64662587Sitojun	printf("\n");
64762587Sitojun
64862587Sitojun	return (1);
64953541Sshin}
650148385Sume
651148385Sumevoid
652150202Ssuzverb(const char *fmt, ...)
65353541Sshin{
65453541Sshin	va_list ap;
65578064Sume
65653541Sshin	if (verbose) {
657165118Sbz		va_start(ap, fmt);
65878064Sume		vfprintf(stderr, fmt, ap);
65953541Sshin		fprintf(stderr, "\n");
66053541Sshin		va_end(ap);
66153541Sshin	}
66278064Sume}
66378064Sume
66478064Sumevoid
66553541Sshinheading(const char *fmt, ...)
66653541Sshin{
66753541Sshin	va_list ap;
66853541Sshin
66953541Sshin	if (need_heading) {
67078064Sume		need_heading = 0;
67178064Sume		if (!notitle) {
67278064Sume			va_start(ap, fmt);
67362587Sitojun			fprintf(stdout, fmt, ap);
67453541Sshin			va_end(ap);
67553541Sshin		}
67653541Sshin	}
67753541Sshin}
67853541Sshin
67953541Sshinvoid
68053541Sshinheading_init(void)
68153541Sshin{
68253541Sshin	need_heading = 1;
68353541Sshin}
68453541Sshin
68553541Sshin/*
68653541Sshin * stringify an enumerated value
68753541Sshin */
68853541Sshinconst char *
68953541Sshinpenum(int32_t value, const struct penum *strtab, char *buf)
69053541Sshin{
69153541Sshin	while (strtab->str != NULL) {
69253541Sshin		if (strtab->value == value) {
69353541Sshin			strcpy(buf, strtab->str);
694194760Srwatson			return (buf);
69553541Sshin		}
69662587Sitojun		strtab++;
69753541Sshin	}
69853541Sshin	warnx("illegal value for enumerated variable '%d'", value);
69995023Ssuz	strcpy(buf, "?");
70053541Sshin	return (buf);
701194760Srwatson}
70253541Sshin
70353541Sshin/*
704165118Sbz * And the other way 'round
70562587Sitojun */
70653541Sshinint
70753541Sshinpparse(int32_t *val, const struct penum *tab, const char *str)
70853541Sshin{
709120941Sume
710165118Sbz	while (tab->str != NULL) {
711120941Sume		if (strcmp(tab->str, str) == 0) {
71278064Sume			*val = tab->value;
71353541Sshin			return (0);
71453541Sshin		}
71553541Sshin		tab++;
716120941Sume	}
717120941Sume	return (-1);
71853541Sshin}
719186119Sqingli
720186119Sqingli/*
721186119Sqingli * Parse command line options
722186119Sqingli */
72362587Sitojunint
724186119Sqingliparse_options(int *pargc, char ***pargv, const struct option *opts)
72553541Sshin{
72653541Sshin	const struct option *o, *m;
72753541Sshin	char *arg;
72853541Sshin	u_long ularg, ularg1;
72953541Sshin	long larg;
73053541Sshin	char *end;
731186119Sqingli
73262587Sitojun	if (*pargc == 0)
733186119Sqingli		return (-1);
73453541Sshin	arg = (*pargv)[0];
73553541Sshin	if (arg[0] != '-' || arg[1] == '\0')
73653541Sshin		return (-1);
73753541Sshin	if (arg[1] == '-' && arg[2] == '\0') {
738186119Sqingli		(*pargv)++;
739186119Sqingli		(*pargc)--;
74053541Sshin		return (-1);
74153541Sshin	}
74262587Sitojun
743151539Ssuz	m = NULL;
744186119Sqingli	for (o = opts; o->optstr != NULL; o++) {
745186119Sqingli		if (strlen(arg + 1) <= strlen(o->optstr) &&
746120941Sume		    strncmp(arg + 1, o->optstr, strlen(arg + 1)) == 0) {
74778064Sume			if (m != NULL)
74853541Sshin				errx(1, "ambiguous option '%s'", arg);
749186119Sqingli			m = o;
75078064Sume		}
75178064Sume	}
75278064Sume	if (m == NULL)
75378064Sume		errx(1, "unknown option '%s'", arg);
75478064Sume
75578064Sume	(*pargv)++;
75678064Sume	(*pargc)--;
757186468Skmacy
75878064Sume	if (m->opttype == OPT_NONE)
75953541Sshin		return (m - opts);
76053541Sshin
76153541Sshin	if (m->opttype == OPT_SIMPLE) {
76253541Sshin		*(int *)m->optarg = 1;
76353541Sshin		return (m - opts);
76453541Sshin	}
765151465Ssuz
76653541Sshin	if (*pargc == 0)
76753541Sshin		errx(1, "option requires argument '%s'", arg);
768186119Sqingli	optarg = *(*pargv)++;
769186119Sqingli	(*pargc)--;
77053541Sshin
77153541Sshin	switch (m->opttype) {
77253541Sshin
77353541Sshin	  case OPT_UINT:
77453541Sshin		ularg = strtoul(optarg, &end, 0);
77553541Sshin		if (*end != '\0')
77653541Sshin			errx(1, "bad unsigned integer argument for '%s'", arg);
77753541Sshin		if (ularg > UINT_MAX)
77853541Sshin			errx(1, "argument to large for option '%s'", arg);
77953541Sshin		*(u_int *)m->optarg = (u_int)ularg;
78053541Sshin		break;
78153541Sshin
78253541Sshin	  case OPT_INT:
78353541Sshin		larg = strtol(optarg, &end, 0);
78453541Sshin		if (*end != '\0')
78553541Sshin			errx(1, "bad integer argument for '%s'", arg);
78653541Sshin		if (larg > INT_MAX || larg < INT_MIN)
78753541Sshin			errx(1, "argument out of range for option '%s'", arg);
78853541Sshin		*(int *)m->optarg = (int)larg;
78953541Sshin		break;
79053541Sshin
79153541Sshin	  case OPT_UINT32:
79253541Sshin		ularg = strtoul(optarg, &end, 0);
79353541Sshin		if (*end != '\0')
79453541Sshin			errx(1, "bad unsigned integer argument for '%s'", arg);
79553541Sshin		if (ularg > UINT32_MAX)
796151539Ssuz			errx(1, "argument to large for option '%s'", arg);
79753541Sshin		*(uint32_t *)m->optarg = (uint32_t)ularg;
79853541Sshin		break;
79953541Sshin
80053541Sshin	  case OPT_INT32:
80178064Sume		larg = strtol(optarg, &end, 0);
80253541Sshin		if (*end != '\0')
803186119Sqingli			errx(1, "bad integer argument for '%s'", arg);
80478064Sume		if (larg > INT32_MAX || larg < INT32_MIN)
80562587Sitojun			errx(1, "argument out of range for option '%s'", arg);
80653541Sshin		*(int32_t *)m->optarg = (int32_t)larg;
807151465Ssuz		break;
808151465Ssuz
80953541Sshin	  case OPT_UINT64:
81053541Sshin		*(uint64_t *)m->optarg = strtoull(optarg, &end, 0);
81153541Sshin		if (*end != '\0')
812151465Ssuz			errx(1, "bad unsigned integer argument for '%s'", arg);
813186119Sqingli		break;
814186119Sqingli
81553541Sshin	  case OPT_INT64:
81653541Sshin		*(int64_t *)m->optarg = strtoll(optarg, &end, 0);
81753541Sshin		if (*end != '\0')
81853541Sshin			errx(1, "bad integer argument for '%s'", arg);
81953541Sshin		break;
82053541Sshin
82153541Sshin	  case OPT_FLAG:
82253541Sshin		if (strcasecmp(optarg, "enable") == 0 ||
82353541Sshin		    strcasecmp(optarg, "yes") == 0 ||
82462587Sitojun		    strcasecmp(optarg, "true") == 0 ||
825151539Ssuz		    strcasecmp(optarg, "on") == 0 ||
826186119Sqingli		    strcmp(optarg, "1") == 0)
827151539Ssuz			*(int *)m->optarg = 1;
82853541Sshin		else if (strcasecmp(optarg, "disable") == 0 ||
82953541Sshin		    strcasecmp(optarg, "no") == 0 ||
830151465Ssuz		    strcasecmp(optarg, "false") == 0 ||
83153541Sshin		    strcasecmp(optarg, "off") == 0 ||
832186119Sqingli		    strcmp(optarg, "0") == 0)
833181803Sbz			*(int *)m->optarg = 0;
83478064Sume		else
83553541Sshin			errx(1, "bad boolean argument to '%s'", arg);
83653541Sshin		break;
83753541Sshin
83853541Sshin	  case OPT_VCI:
83953541Sshin		ularg = strtoul(optarg, &end, 0);
84053541Sshin		if (*end == '.') {
84153541Sshin			ularg1 = strtoul(end + 1, &end, 0);
84253541Sshin		} else {
84353541Sshin			ularg1 = ularg;
84453541Sshin			ularg = 0;
84553541Sshin		}
84653541Sshin		if (*end != '\0')
847186119Sqingli			errx(1, "bad VCI value for option '%s'", arg);
84895023Ssuz		if (ularg > 0xff)
84995023Ssuz			errx(1, "VPI value too large for option '%s'", arg);
85095023Ssuz		if (ularg1 > 0xffff)
85195023Ssuz			errx(1, "VCI value too large for option '%s'", arg);
85295023Ssuz		((u_int *)m->optarg)[0] = ularg;
853120941Sume		((u_int *)m->optarg)[1] = ularg1;
85495023Ssuz		break;
855186119Sqingli
85653541Sshin	  case OPT_STRING:
85753541Sshin		if (m->optarg != NULL)
858181803Sbz			*(const char **)m->optarg = optarg;
85953541Sshin		break;
86053541Sshin
86153541Sshin	  default:
86253541Sshin		errx(1, "(internal) bad option type %u for '%s'",
86353541Sshin		    m->opttype, arg);
86453541Sshin	}
86553541Sshin	return (m - opts);
866128422Sluigi}
86753541Sshin
86853541Sshin/*
86953541Sshin * for compiled-in modules
87053541Sshin */
871186119Sqinglivoid
872186119Sqingliregister_module(const struct amodule *mod)
873186119Sqingli{
874186119Sqingli	main_tab_size++;
875186119Sqingli	if ((main_tab = realloc(main_tab, main_tab_size * sizeof(main_tab[0])))
876186119Sqingli	    == NULL)
877151539Ssuz		err(1, NULL);
878151539Ssuz	main_tab[main_tab_size - 2] = *mod->cmd;
879169273Ssuz	memset(&main_tab[main_tab_size - 1], 0, sizeof(main_tab[0]));
880186119Sqingli}
881186119Sqingli