187866Ssheldonh/*
287866Ssheldonh * Copyright (c) 2000, Boris Popov
387866Ssheldonh * All rights reserved.
487866Ssheldonh *
587866Ssheldonh * Redistribution and use in source and binary forms, with or without
687866Ssheldonh * modification, are permitted provided that the following conditions
787866Ssheldonh * are met:
887866Ssheldonh * 1. Redistributions of source code must retain the above copyright
987866Ssheldonh *    notice, this list of conditions and the following disclaimer.
1087866Ssheldonh * 2. Redistributions in binary form must reproduce the above copyright
1187866Ssheldonh *    notice, this list of conditions and the following disclaimer in the
1287866Ssheldonh *    documentation and/or other materials provided with the distribution.
1387866Ssheldonh * 3. All advertising materials mentioning features or use of this software
1487866Ssheldonh *    must display the following acknowledgement:
1587866Ssheldonh *    This product includes software developed by Boris Popov.
1687866Ssheldonh * 4. Neither the name of the author nor the names of any co-contributors
1787866Ssheldonh *    may be used to endorse or promote products derived from this software
1887866Ssheldonh *    without specific prior written permission.
1987866Ssheldonh *
2087866Ssheldonh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2187866Ssheldonh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2287866Ssheldonh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2387866Ssheldonh * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2487866Ssheldonh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2587866Ssheldonh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2687866Ssheldonh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2787866Ssheldonh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2887866Ssheldonh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2987866Ssheldonh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3087866Ssheldonh * SUCH DAMAGE.
3187866Ssheldonh *
3287866Ssheldonh * $Id: rcfile.c,v 1.5 2001/04/16 12:46:46 bp Exp $
3387866Ssheldonh */
34136700Sobrien
35136700Sobrien#include <sys/cdefs.h>
36136700Sobrien__FBSDID("$FreeBSD: stable/11/contrib/smbfs/lib/smb/rcfile.c 356599 2020-01-10 12:20:25Z pluknet $");
37136700Sobrien
3887866Ssheldonh#include <sys/types.h>
3987866Ssheldonh#include <sys/queue.h>
4087866Ssheldonh#include <ctype.h>
4187866Ssheldonh#include <errno.h>
42356599Spluknet#define _WITH_DPRINTF
4387866Ssheldonh#include <stdio.h>
4487866Ssheldonh#include <string.h>
4587866Ssheldonh#include <stdlib.h>
4687866Ssheldonh#include <pwd.h>
4787866Ssheldonh#include <unistd.h>
4887866Ssheldonh#include <err.h>
4987866Ssheldonh
5087866Ssheldonh#include <cflib.h>
5187866Ssheldonh#include "rcfile_priv.h"
5287866Ssheldonh
5387866SsheldonhSLIST_HEAD(rcfile_head, rcfile);
5487866Ssheldonhstatic struct rcfile_head pf_head = {NULL};
5587866Ssheldonh
5687866Ssheldonhstatic struct rcfile* rc_cachelookup(const char *filename);
5787866Ssheldonhstatic struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname);
5887866Ssheldonhstatic struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname);
5987866Ssheldonhstatic int rc_freesect(struct rcfile *rcp, struct rcsection *rsp);
6087866Ssheldonhstatic struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname);
6187866Ssheldonhstatic struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value);
6287866Ssheldonhstatic void rc_key_free(struct rckey *p);
6387866Ssheldonhstatic void rc_parse(struct rcfile *rcp);
6487866Ssheldonh
6587866Ssheldonh
6687866Ssheldonh/*
6787866Ssheldonh * open rcfile and load its content, if already open - return previous handle
6887866Ssheldonh */
6987866Ssheldonhint
7087866Ssheldonhrc_open(const char *filename, const char *mode, struct rcfile **rcfile)
7187866Ssheldonh{
7287866Ssheldonh	struct rcfile *rcp;
7387866Ssheldonh	FILE *f;
7487866Ssheldonh
7587866Ssheldonh	rcp = rc_cachelookup(filename);
7687866Ssheldonh	if (rcp) {
7787866Ssheldonh		*rcfile = rcp;
7887866Ssheldonh		return 0;
7987866Ssheldonh	}
8087866Ssheldonh	f = fopen(filename, mode);
8187866Ssheldonh	if (f == NULL)
8287866Ssheldonh		return errno;
8387866Ssheldonh	rcp = malloc(sizeof(struct rcfile));
8487866Ssheldonh	if (rcp == NULL) {
8587866Ssheldonh		fclose(f);
8687866Ssheldonh		return ENOMEM;
8787866Ssheldonh	}
8887866Ssheldonh	bzero(rcp, sizeof(struct rcfile));
8987866Ssheldonh	rcp->rf_name = strdup(filename);
9087866Ssheldonh	rcp->rf_f = f;
9187866Ssheldonh	SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
9287866Ssheldonh	rc_parse(rcp);
9387866Ssheldonh	*rcfile = rcp;
9487866Ssheldonh	return 0;
9587866Ssheldonh}
9687866Ssheldonh
9787866Ssheldonhint
9887866Ssheldonhrc_merge(const char *filename, struct rcfile **rcfile)
9987866Ssheldonh{
10087866Ssheldonh	struct rcfile *rcp = *rcfile;
10187866Ssheldonh	FILE *f, *t;
10287866Ssheldonh
10387866Ssheldonh	if (rcp == NULL) {
10487866Ssheldonh		return rc_open(filename, "r", rcfile);
10587866Ssheldonh	}
10687866Ssheldonh	f = fopen (filename, "r");
10787866Ssheldonh	if (f == NULL)
10887866Ssheldonh		return errno;
10987866Ssheldonh	t = rcp->rf_f;
11087866Ssheldonh	rcp->rf_f = f;
11187866Ssheldonh	rc_parse(rcp);
11287866Ssheldonh	rcp->rf_f = t;
11387866Ssheldonh	fclose(f);
11487866Ssheldonh	return 0;
11587866Ssheldonh}
11687866Ssheldonh
11787866Ssheldonhint
11887866Ssheldonhrc_close(struct rcfile *rcp)
11987866Ssheldonh{
12087866Ssheldonh	struct rcsection *p, *n;
12187866Ssheldonh
12287866Ssheldonh	fclose(rcp->rf_f);
12387866Ssheldonh	for(p = SLIST_FIRST(&rcp->rf_sect); p;) {
12487866Ssheldonh		n = p;
12587866Ssheldonh		p = SLIST_NEXT(p,rs_next);
12687866Ssheldonh		rc_freesect(rcp, n);
12787866Ssheldonh	}
12887866Ssheldonh	free(rcp->rf_name);
12987866Ssheldonh	SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
13087866Ssheldonh	free(rcp);
13187866Ssheldonh	return 0;
13287866Ssheldonh}
13387866Ssheldonh
13487866Ssheldonhstatic struct rcfile*
13587866Ssheldonhrc_cachelookup(const char *filename)
13687866Ssheldonh{
13787866Ssheldonh	struct rcfile *p;
13887866Ssheldonh
13987866Ssheldonh	SLIST_FOREACH(p, &pf_head, rf_next)
14087866Ssheldonh		if (strcmp (filename, p->rf_name) == 0)
14187866Ssheldonh			return p;
14287866Ssheldonh	return 0;
14387866Ssheldonh}
14487866Ssheldonh
14587866Ssheldonhstatic struct rcsection *
14687866Ssheldonhrc_findsect(struct rcfile *rcp, const char *sectname)
14787866Ssheldonh{
14887866Ssheldonh	struct rcsection *p;
14987866Ssheldonh
15087866Ssheldonh	SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
15187866Ssheldonh		if (strcmp(p->rs_name, sectname)==0)
15287866Ssheldonh			return p;
15387866Ssheldonh	return NULL;
15487866Ssheldonh}
15587866Ssheldonh
15687866Ssheldonhstatic struct rcsection *
15787866Ssheldonhrc_addsect(struct rcfile *rcp, const char *sectname)
15887866Ssheldonh{
15987866Ssheldonh	struct rcsection *p;
160356566Sbapt	const char* sectletter = sectname;
16187866Ssheldonh
16287866Ssheldonh	p = rc_findsect(rcp, sectname);
16387866Ssheldonh	if (p) return p;
16487866Ssheldonh	p = malloc(sizeof(*p));
16587866Ssheldonh	if (!p) return NULL;
166356566Sbapt	for(sectletter = sectname; *sectletter; sectletter++) {
167356566Sbapt		if (islower(*sectletter)) {
168356566Sbapt			if (strcmp(sectname, "default"))
169356566Sbapt				dprintf(STDERR_FILENO, "warning: section name [%s] contains lower-case letters\n", sectname);
170356566Sbapt			break;
171356566Sbapt		}
172356566Sbapt	}
17387866Ssheldonh	p->rs_name = strdup(sectname);
17487866Ssheldonh	SLIST_INIT(&p->rs_keys);
17587866Ssheldonh	SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
17687866Ssheldonh	return p;
17787866Ssheldonh}
17887866Ssheldonh
17987866Ssheldonhstatic int
18087866Ssheldonhrc_freesect(struct rcfile *rcp, struct rcsection *rsp)
18187866Ssheldonh{
18287866Ssheldonh	struct rckey *p,*n;
18387866Ssheldonh
18487866Ssheldonh	SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next);
18587866Ssheldonh	for(p = SLIST_FIRST(&rsp->rs_keys);p;) {
18687866Ssheldonh		n = p;
18787866Ssheldonh		p = SLIST_NEXT(p,rk_next);
18887866Ssheldonh		rc_key_free(n);
18987866Ssheldonh	}
19087866Ssheldonh	free(rsp->rs_name);
19187866Ssheldonh	free(rsp);
19287866Ssheldonh	return 0;
19387866Ssheldonh}
19487866Ssheldonh
19587866Ssheldonhstatic struct rckey *
19687866Ssheldonhrc_sect_findkey(struct rcsection *rsp, const char *keyname)
19787866Ssheldonh{
19887866Ssheldonh	struct rckey *p;
19987866Ssheldonh
20087866Ssheldonh	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
20187866Ssheldonh		if (strcmp(p->rk_name, keyname)==0)
20287866Ssheldonh			return p;
20387866Ssheldonh	return NULL;
20487866Ssheldonh}
20587866Ssheldonh
20687866Ssheldonhstatic struct rckey *
20787866Ssheldonhrc_sect_addkey(struct rcsection *rsp, const char *name, const char *value)
20887866Ssheldonh{
20987866Ssheldonh	struct rckey *p;
21087866Ssheldonh
21187866Ssheldonh	p = rc_sect_findkey(rsp, name);
21287866Ssheldonh	if (p) {
21387866Ssheldonh		free(p->rk_value);
21487866Ssheldonh	} else {
21587866Ssheldonh		p = malloc(sizeof(*p));
21687866Ssheldonh		if (!p) return NULL;
21787866Ssheldonh		SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
21887866Ssheldonh		p->rk_name = strdup(name);
21987866Ssheldonh	}
22087866Ssheldonh	p->rk_value = value ? strdup(value) : strdup("");
22187866Ssheldonh	return p;
22287866Ssheldonh}
22387866Ssheldonh
22487866Ssheldonh#if 0
22587866Ssheldonhvoid
22687866Ssheldonhrc_sect_delkey(struct rcsection *rsp, struct rckey *p)
22787866Ssheldonh{
22887866Ssheldonh
22987866Ssheldonh	SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next);
23087866Ssheldonh	rc_key_free(p);
23187866Ssheldonh	return;
23287866Ssheldonh}
23387866Ssheldonh#endif
23487866Ssheldonh
23587866Ssheldonhstatic void
23687866Ssheldonhrc_key_free(struct rckey *p)
23787866Ssheldonh{
23887866Ssheldonh	free(p->rk_value);
23987866Ssheldonh	free(p->rk_name);
24087866Ssheldonh	free(p);
24187866Ssheldonh}
24287866Ssheldonh
24387866Ssheldonhenum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
24487866Ssheldonh
24587866Ssheldonhstatic void
24687866Ssheldonhrc_parse(struct rcfile *rcp)
24787866Ssheldonh{
24887866Ssheldonh	FILE *f = rcp->rf_f;
24987866Ssheldonh	int state = stNewLine, c;
25087866Ssheldonh	struct rcsection *rsp = NULL;
25187866Ssheldonh	struct rckey *rkp = NULL;
25287866Ssheldonh	char buf[2048];
25387866Ssheldonh	char *next = buf, *last = &buf[sizeof(buf)-1];
25487866Ssheldonh
25587866Ssheldonh	while ((c = getc (f)) != EOF) {
25687866Ssheldonh		if (c == '\r')
25787866Ssheldonh			continue;
25887866Ssheldonh		if (state == stNewLine) {
25987866Ssheldonh			next = buf;
26087866Ssheldonh			if (isspace(c))
26187866Ssheldonh				continue;	/* skip leading junk */
26287866Ssheldonh			if (c == '[') {
26387866Ssheldonh				state = stHeader;
26487866Ssheldonh				rsp = NULL;
26587866Ssheldonh				continue;
26687866Ssheldonh			}
26787866Ssheldonh			if (c == '#' || c == ';') {
26887866Ssheldonh				state = stSkipToEOL;
26987866Ssheldonh			} else {		/* something meaningfull */
27087866Ssheldonh				state = stGetKey;
27187866Ssheldonh			}
27287866Ssheldonh		}
27387866Ssheldonh		if (state == stSkipToEOL || next == last) {/* ignore long lines */
27487866Ssheldonh			if (c == '\n'){
27587866Ssheldonh				state = stNewLine;
27687866Ssheldonh				next = buf;
27787866Ssheldonh			}
27887866Ssheldonh			continue;
27987866Ssheldonh		}
28087866Ssheldonh		if (state == stHeader) {
28187866Ssheldonh			if (c == ']') {
28287866Ssheldonh				*next = 0;
28387866Ssheldonh				next = buf;
28487866Ssheldonh				rsp = rc_addsect(rcp, buf);
28587866Ssheldonh				state = stSkipToEOL;
28687866Ssheldonh			} else
28787866Ssheldonh				*next++ = c;
28887866Ssheldonh			continue;
28987866Ssheldonh		}
29087866Ssheldonh		if (state == stGetKey) {
29187866Ssheldonh			if (c == ' ' || c == '\t')/* side effect: 'key name='*/
29287866Ssheldonh				continue;	  /* become 'keyname=' 	     */
29387866Ssheldonh			if (c == '\n') {	/* silently ignore ... */
29487866Ssheldonh				state = stNewLine;
29587866Ssheldonh				continue;
29687866Ssheldonh			}
29787866Ssheldonh			if (c != '=') {
29887866Ssheldonh				*next++ = c;
29987866Ssheldonh				continue;
30087866Ssheldonh			}
30187866Ssheldonh			*next = 0;
30287866Ssheldonh			if (rsp == NULL) {
30387866Ssheldonh				fprintf(stderr, "Key '%s' defined before section\n", buf);
30487866Ssheldonh				state = stSkipToEOL;
30587866Ssheldonh				continue;
30687866Ssheldonh			}
30787866Ssheldonh			rkp = rc_sect_addkey(rsp, buf, NULL);
30887866Ssheldonh			next = buf;
30987866Ssheldonh			state = stGetValue;
31087866Ssheldonh			continue;
31187866Ssheldonh		}
31287866Ssheldonh		/* only stGetValue left */
31387866Ssheldonh		if (state != stGetValue) {
31487866Ssheldonh			fprintf(stderr, "Well, I can't parse file '%s'\n",rcp->rf_name);
31587866Ssheldonh			state = stSkipToEOL;
31687866Ssheldonh		}
31787866Ssheldonh		if (c != '\n') {
31887866Ssheldonh			*next++ = c;
31987866Ssheldonh			continue;
32087866Ssheldonh		}
32187866Ssheldonh		*next = 0;
32287866Ssheldonh		rkp->rk_value = strdup(buf);
32387866Ssheldonh		state = stNewLine;
32487866Ssheldonh		rkp = NULL;
32587866Ssheldonh	} 	/* while */
32687866Ssheldonh	if (c == EOF && state == stGetValue) {
32787866Ssheldonh		*next = 0;
32887866Ssheldonh		rkp->rk_value = strdup(buf);
32987866Ssheldonh	}
33087866Ssheldonh	return;
33187866Ssheldonh}
33287866Ssheldonh
33387866Ssheldonhint
33487866Ssheldonhrc_getstringptr(struct rcfile *rcp, const char *section, const char *key,
33587866Ssheldonh	char **dest)
33687866Ssheldonh{
33787866Ssheldonh	struct rcsection *rsp;
33887866Ssheldonh	struct rckey *rkp;
33987866Ssheldonh
34087866Ssheldonh	*dest = NULL;
34187866Ssheldonh	rsp = rc_findsect(rcp, section);
34287866Ssheldonh	if (!rsp) return ENOENT;
34387866Ssheldonh	rkp = rc_sect_findkey(rsp,key);
34487866Ssheldonh	if (!rkp) return ENOENT;
34587866Ssheldonh	*dest = rkp->rk_value;
34687866Ssheldonh	return 0;
34787866Ssheldonh}
34887866Ssheldonh
34987866Ssheldonhint
35087866Ssheldonhrc_getstring(struct rcfile *rcp, const char *section, const char *key,
35187866Ssheldonh	size_t maxlen, char *dest)
35287866Ssheldonh{
35387866Ssheldonh	char *value;
35487866Ssheldonh	int error;
35587866Ssheldonh
35687866Ssheldonh	error = rc_getstringptr(rcp, section, key, &value);
35787866Ssheldonh	if (error)
35887866Ssheldonh		return error;
35987866Ssheldonh	if (strlen(value) >= maxlen) {
360136700Sobrien		warnx("line too long for key '%s' in section '%s', max = %zd\n", key, section, maxlen);
36187866Ssheldonh		return EINVAL;
36287866Ssheldonh	}
36387866Ssheldonh	strcpy(dest, value);
36487866Ssheldonh	return 0;
36587866Ssheldonh}
36687866Ssheldonh
36787866Ssheldonhint
36887866Ssheldonhrc_getint(struct rcfile *rcp, const char *section, const char *key, int *value)
36987866Ssheldonh{
37087866Ssheldonh	struct rcsection *rsp;
37187866Ssheldonh	struct rckey *rkp;
37287866Ssheldonh
37387866Ssheldonh	rsp = rc_findsect(rcp, section);
37487866Ssheldonh	if (!rsp)
37587866Ssheldonh		return ENOENT;
37687866Ssheldonh	rkp = rc_sect_findkey(rsp, key);
37787866Ssheldonh	if (!rkp)
37887866Ssheldonh		return ENOENT;
37987866Ssheldonh	errno = 0;
38087866Ssheldonh	*value = strtol(rkp->rk_value, NULL, 0);
38187866Ssheldonh	if (errno) {
38287866Ssheldonh		warnx("invalid int value '%s' for key '%s' in section '%s'\n", rkp->rk_value, key, section);
38387866Ssheldonh		return errno;
38487866Ssheldonh	}
38587866Ssheldonh	return 0;
38687866Ssheldonh}
38787866Ssheldonh
38887866Ssheldonh/*
38987866Ssheldonh * 1,yes,true
39087866Ssheldonh * 0,no,false
39187866Ssheldonh */
39287866Ssheldonhint
39387866Ssheldonhrc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value)
39487866Ssheldonh{
39587866Ssheldonh	struct rcsection *rsp;
39687866Ssheldonh	struct rckey *rkp;
39787866Ssheldonh	char *p;
39887866Ssheldonh
39987866Ssheldonh	rsp = rc_findsect(rcp, section);
40087866Ssheldonh	if (!rsp) return ENOENT;
40187866Ssheldonh	rkp = rc_sect_findkey(rsp,key);
40287866Ssheldonh	if (!rkp) return ENOENT;
40387866Ssheldonh	p = rkp->rk_value;
40487866Ssheldonh	while (*p && isspace(*p)) p++;
40587866Ssheldonh	if (*p == '0' || strcasecmp(p,"no") == 0 || strcasecmp(p,"false") == 0) {
40687866Ssheldonh		*value = 0;
40787866Ssheldonh		return 0;
40887866Ssheldonh	}
40987866Ssheldonh	if (*p == '1' || strcasecmp(p,"yes") == 0 || strcasecmp(p,"true") == 0) {
41087866Ssheldonh		*value = 1;
41187866Ssheldonh		return 0;
41287866Ssheldonh	}
41387866Ssheldonh	fprintf(stderr, "invalid boolean value '%s' for key '%s' in section '%s' \n",p, key, section);
41487866Ssheldonh	return EINVAL;
41587866Ssheldonh}
41687866Ssheldonh
41787866Ssheldonh/*
41887866Ssheldonh * Unified command line/rc file parser
41987866Ssheldonh */
42087866Ssheldonhint
42187866Ssheldonhopt_args_parse(struct rcfile *rcp, struct opt_args *ap, const char *sect,
42287866Ssheldonh	opt_callback_t *callback)
42387866Ssheldonh{
42487866Ssheldonh	int len, error;
42587866Ssheldonh
42687866Ssheldonh	for (; ap->opt; ap++) {
42787866Ssheldonh		switch (ap->type) {
42887866Ssheldonh		    case OPTARG_STR:
42987866Ssheldonh			if (rc_getstringptr(rcp, sect, ap->name, &ap->str) != 0)
43087866Ssheldonh				break;
43187866Ssheldonh			len = strlen(ap->str);
43287866Ssheldonh			if (len > ap->ival) {
43387866Ssheldonh				warnx("rc: argument for option '%c' (%s) too long\n", ap->opt, ap->name);
43487866Ssheldonh				return EINVAL;
43587866Ssheldonh			}
43687866Ssheldonh			callback(ap);
43787866Ssheldonh			break;
43887866Ssheldonh		    case OPTARG_BOOL:
43987866Ssheldonh			error = rc_getbool(rcp, sect, ap->name, &ap->ival);
44087866Ssheldonh			if (error == ENOENT)
44187866Ssheldonh				break;
44287866Ssheldonh			if (error)
44387866Ssheldonh				return EINVAL;
44487866Ssheldonh			callback(ap);
44587866Ssheldonh			break;
44687866Ssheldonh		    case OPTARG_INT:
44787866Ssheldonh			if (rc_getint(rcp, sect, ap->name, &ap->ival) != 0)
44887866Ssheldonh				break;
44987866Ssheldonh			if (((ap->flag & OPTFL_HAVEMIN) && ap->ival < ap->min) ||
45087866Ssheldonh			    ((ap->flag & OPTFL_HAVEMAX) && ap->ival > ap->max)) {
45187866Ssheldonh				warnx("rc: argument for option '%c' (%s) should be in [%d-%d] range\n",
45287866Ssheldonh				    ap->opt, ap->name, ap->min, ap->max);
45387866Ssheldonh				return EINVAL;
45487866Ssheldonh			}
45587866Ssheldonh			callback(ap);
45687866Ssheldonh			break;
45787866Ssheldonh		    default:
45887866Ssheldonh			break;
45987866Ssheldonh		}
46087866Ssheldonh	}
46187866Ssheldonh	return 0;
46287866Ssheldonh}
46387866Ssheldonh
46487866Ssheldonhint
46587866Ssheldonhopt_args_parseopt(struct opt_args *ap, int opt, char *arg,
46687866Ssheldonh	opt_callback_t *callback)
46787866Ssheldonh{
46887866Ssheldonh	int len;
46987866Ssheldonh
47087866Ssheldonh	for (; ap->opt; ap++) {
47187866Ssheldonh		if (ap->opt != opt)
47287866Ssheldonh			continue;
47387866Ssheldonh		switch (ap->type) {
47487866Ssheldonh		    case OPTARG_STR:
47587866Ssheldonh			ap->str = arg;
47687866Ssheldonh			if (arg) {
47787866Ssheldonh				len = strlen(ap->str);
47887866Ssheldonh				if (len > ap->ival) {
47987866Ssheldonh					warnx("opt: Argument for option '%c' (%s) too long\n", ap->opt, ap->name);
48087866Ssheldonh					return EINVAL;
48187866Ssheldonh				}
48287866Ssheldonh				callback(ap);
48387866Ssheldonh			}
48487866Ssheldonh			break;
48587866Ssheldonh		    case OPTARG_BOOL:
48687866Ssheldonh			ap->ival = 0;
48787866Ssheldonh			callback(ap);
48887866Ssheldonh			break;
48987866Ssheldonh		    case OPTARG_INT:
49087866Ssheldonh			errno = 0;
49187866Ssheldonh			ap->ival = strtol(arg, NULL, 0);
49287866Ssheldonh			if (errno) {
49387866Ssheldonh				warnx("opt: Invalid integer value for option '%c' (%s).\n",ap->opt,ap->name);
49487866Ssheldonh				return EINVAL;
49587866Ssheldonh			}
49687866Ssheldonh			if (((ap->flag & OPTFL_HAVEMIN) &&
49787866Ssheldonh			     (ap->ival < ap->min)) ||
49887866Ssheldonh			    ((ap->flag & OPTFL_HAVEMAX) &&
49987866Ssheldonh			     (ap->ival > ap->max))) {
50087866Ssheldonh				warnx("opt: Argument for option '%c' (%s) should be in [%d-%d] range\n",ap->opt,ap->name,ap->min,ap->max);
50187866Ssheldonh				return EINVAL;
50287866Ssheldonh			}
50387866Ssheldonh			callback(ap);
50487866Ssheldonh			break;
50587866Ssheldonh		    default:
50687866Ssheldonh			break;
50787866Ssheldonh		}
50887866Ssheldonh		break;
50987866Ssheldonh	}
51087866Ssheldonh	return 0;
51187866Ssheldonh}
51287866Ssheldonh
513