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: releng/11.0/contrib/smbfs/lib/smb/rcfile.c 136700 2004-10-19 17:44:31Z obrien $");
37136700Sobrien
3887866Ssheldonh#include <sys/types.h>
3987866Ssheldonh#include <sys/queue.h>
4087866Ssheldonh#include <ctype.h>
4187866Ssheldonh#include <errno.h>
4287866Ssheldonh#include <stdio.h>
4387866Ssheldonh#include <string.h>
4487866Ssheldonh#include <stdlib.h>
4587866Ssheldonh#include <pwd.h>
4687866Ssheldonh#include <unistd.h>
4787866Ssheldonh#include <err.h>
4887866Ssheldonh
4987866Ssheldonh#include <cflib.h>
5087866Ssheldonh#include "rcfile_priv.h"
5187866Ssheldonh
5287866SsheldonhSLIST_HEAD(rcfile_head, rcfile);
5387866Ssheldonhstatic struct rcfile_head pf_head = {NULL};
5487866Ssheldonh
5587866Ssheldonhstatic struct rcfile* rc_cachelookup(const char *filename);
5687866Ssheldonhstatic struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname);
5787866Ssheldonhstatic struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname);
5887866Ssheldonhstatic int rc_freesect(struct rcfile *rcp, struct rcsection *rsp);
5987866Ssheldonhstatic struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname);
6087866Ssheldonhstatic struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value);
6187866Ssheldonhstatic void rc_key_free(struct rckey *p);
6287866Ssheldonhstatic void rc_parse(struct rcfile *rcp);
6387866Ssheldonh
6487866Ssheldonh
6587866Ssheldonh/*
6687866Ssheldonh * open rcfile and load its content, if already open - return previous handle
6787866Ssheldonh */
6887866Ssheldonhint
6987866Ssheldonhrc_open(const char *filename, const char *mode, struct rcfile **rcfile)
7087866Ssheldonh{
7187866Ssheldonh	struct rcfile *rcp;
7287866Ssheldonh	FILE *f;
7387866Ssheldonh
7487866Ssheldonh	rcp = rc_cachelookup(filename);
7587866Ssheldonh	if (rcp) {
7687866Ssheldonh		*rcfile = rcp;
7787866Ssheldonh		return 0;
7887866Ssheldonh	}
7987866Ssheldonh	f = fopen(filename, mode);
8087866Ssheldonh	if (f == NULL)
8187866Ssheldonh		return errno;
8287866Ssheldonh	rcp = malloc(sizeof(struct rcfile));
8387866Ssheldonh	if (rcp == NULL) {
8487866Ssheldonh		fclose(f);
8587866Ssheldonh		return ENOMEM;
8687866Ssheldonh	}
8787866Ssheldonh	bzero(rcp, sizeof(struct rcfile));
8887866Ssheldonh	rcp->rf_name = strdup(filename);
8987866Ssheldonh	rcp->rf_f = f;
9087866Ssheldonh	SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
9187866Ssheldonh	rc_parse(rcp);
9287866Ssheldonh	*rcfile = rcp;
9387866Ssheldonh	return 0;
9487866Ssheldonh}
9587866Ssheldonh
9687866Ssheldonhint
9787866Ssheldonhrc_merge(const char *filename, struct rcfile **rcfile)
9887866Ssheldonh{
9987866Ssheldonh	struct rcfile *rcp = *rcfile;
10087866Ssheldonh	FILE *f, *t;
10187866Ssheldonh
10287866Ssheldonh	if (rcp == NULL) {
10387866Ssheldonh		return rc_open(filename, "r", rcfile);
10487866Ssheldonh	}
10587866Ssheldonh	f = fopen (filename, "r");
10687866Ssheldonh	if (f == NULL)
10787866Ssheldonh		return errno;
10887866Ssheldonh	t = rcp->rf_f;
10987866Ssheldonh	rcp->rf_f = f;
11087866Ssheldonh	rc_parse(rcp);
11187866Ssheldonh	rcp->rf_f = t;
11287866Ssheldonh	fclose(f);
11387866Ssheldonh	return 0;
11487866Ssheldonh}
11587866Ssheldonh
11687866Ssheldonhint
11787866Ssheldonhrc_close(struct rcfile *rcp)
11887866Ssheldonh{
11987866Ssheldonh	struct rcsection *p, *n;
12087866Ssheldonh
12187866Ssheldonh	fclose(rcp->rf_f);
12287866Ssheldonh	for(p = SLIST_FIRST(&rcp->rf_sect); p;) {
12387866Ssheldonh		n = p;
12487866Ssheldonh		p = SLIST_NEXT(p,rs_next);
12587866Ssheldonh		rc_freesect(rcp, n);
12687866Ssheldonh	}
12787866Ssheldonh	free(rcp->rf_name);
12887866Ssheldonh	SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
12987866Ssheldonh	free(rcp);
13087866Ssheldonh	return 0;
13187866Ssheldonh}
13287866Ssheldonh
13387866Ssheldonhstatic struct rcfile*
13487866Ssheldonhrc_cachelookup(const char *filename)
13587866Ssheldonh{
13687866Ssheldonh	struct rcfile *p;
13787866Ssheldonh
13887866Ssheldonh	SLIST_FOREACH(p, &pf_head, rf_next)
13987866Ssheldonh		if (strcmp (filename, p->rf_name) == 0)
14087866Ssheldonh			return p;
14187866Ssheldonh	return 0;
14287866Ssheldonh}
14387866Ssheldonh
14487866Ssheldonhstatic struct rcsection *
14587866Ssheldonhrc_findsect(struct rcfile *rcp, const char *sectname)
14687866Ssheldonh{
14787866Ssheldonh	struct rcsection *p;
14887866Ssheldonh
14987866Ssheldonh	SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
15087866Ssheldonh		if (strcmp(p->rs_name, sectname)==0)
15187866Ssheldonh			return p;
15287866Ssheldonh	return NULL;
15387866Ssheldonh}
15487866Ssheldonh
15587866Ssheldonhstatic struct rcsection *
15687866Ssheldonhrc_addsect(struct rcfile *rcp, const char *sectname)
15787866Ssheldonh{
15887866Ssheldonh	struct rcsection *p;
15987866Ssheldonh
16087866Ssheldonh	p = rc_findsect(rcp, sectname);
16187866Ssheldonh	if (p) return p;
16287866Ssheldonh	p = malloc(sizeof(*p));
16387866Ssheldonh	if (!p) return NULL;
16487866Ssheldonh	p->rs_name = strdup(sectname);
16587866Ssheldonh	SLIST_INIT(&p->rs_keys);
16687866Ssheldonh	SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
16787866Ssheldonh	return p;
16887866Ssheldonh}
16987866Ssheldonh
17087866Ssheldonhstatic int
17187866Ssheldonhrc_freesect(struct rcfile *rcp, struct rcsection *rsp)
17287866Ssheldonh{
17387866Ssheldonh	struct rckey *p,*n;
17487866Ssheldonh
17587866Ssheldonh	SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next);
17687866Ssheldonh	for(p = SLIST_FIRST(&rsp->rs_keys);p;) {
17787866Ssheldonh		n = p;
17887866Ssheldonh		p = SLIST_NEXT(p,rk_next);
17987866Ssheldonh		rc_key_free(n);
18087866Ssheldonh	}
18187866Ssheldonh	free(rsp->rs_name);
18287866Ssheldonh	free(rsp);
18387866Ssheldonh	return 0;
18487866Ssheldonh}
18587866Ssheldonh
18687866Ssheldonhstatic struct rckey *
18787866Ssheldonhrc_sect_findkey(struct rcsection *rsp, const char *keyname)
18887866Ssheldonh{
18987866Ssheldonh	struct rckey *p;
19087866Ssheldonh
19187866Ssheldonh	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
19287866Ssheldonh		if (strcmp(p->rk_name, keyname)==0)
19387866Ssheldonh			return p;
19487866Ssheldonh	return NULL;
19587866Ssheldonh}
19687866Ssheldonh
19787866Ssheldonhstatic struct rckey *
19887866Ssheldonhrc_sect_addkey(struct rcsection *rsp, const char *name, const char *value)
19987866Ssheldonh{
20087866Ssheldonh	struct rckey *p;
20187866Ssheldonh
20287866Ssheldonh	p = rc_sect_findkey(rsp, name);
20387866Ssheldonh	if (p) {
20487866Ssheldonh		free(p->rk_value);
20587866Ssheldonh	} else {
20687866Ssheldonh		p = malloc(sizeof(*p));
20787866Ssheldonh		if (!p) return NULL;
20887866Ssheldonh		SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
20987866Ssheldonh		p->rk_name = strdup(name);
21087866Ssheldonh	}
21187866Ssheldonh	p->rk_value = value ? strdup(value) : strdup("");
21287866Ssheldonh	return p;
21387866Ssheldonh}
21487866Ssheldonh
21587866Ssheldonh#if 0
21687866Ssheldonhvoid
21787866Ssheldonhrc_sect_delkey(struct rcsection *rsp, struct rckey *p)
21887866Ssheldonh{
21987866Ssheldonh
22087866Ssheldonh	SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next);
22187866Ssheldonh	rc_key_free(p);
22287866Ssheldonh	return;
22387866Ssheldonh}
22487866Ssheldonh#endif
22587866Ssheldonh
22687866Ssheldonhstatic void
22787866Ssheldonhrc_key_free(struct rckey *p)
22887866Ssheldonh{
22987866Ssheldonh	free(p->rk_value);
23087866Ssheldonh	free(p->rk_name);
23187866Ssheldonh	free(p);
23287866Ssheldonh}
23387866Ssheldonh
23487866Ssheldonhenum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
23587866Ssheldonh
23687866Ssheldonhstatic void
23787866Ssheldonhrc_parse(struct rcfile *rcp)
23887866Ssheldonh{
23987866Ssheldonh	FILE *f = rcp->rf_f;
24087866Ssheldonh	int state = stNewLine, c;
24187866Ssheldonh	struct rcsection *rsp = NULL;
24287866Ssheldonh	struct rckey *rkp = NULL;
24387866Ssheldonh	char buf[2048];
24487866Ssheldonh	char *next = buf, *last = &buf[sizeof(buf)-1];
24587866Ssheldonh
24687866Ssheldonh	while ((c = getc (f)) != EOF) {
24787866Ssheldonh		if (c == '\r')
24887866Ssheldonh			continue;
24987866Ssheldonh		if (state == stNewLine) {
25087866Ssheldonh			next = buf;
25187866Ssheldonh			if (isspace(c))
25287866Ssheldonh				continue;	/* skip leading junk */
25387866Ssheldonh			if (c == '[') {
25487866Ssheldonh				state = stHeader;
25587866Ssheldonh				rsp = NULL;
25687866Ssheldonh				continue;
25787866Ssheldonh			}
25887866Ssheldonh			if (c == '#' || c == ';') {
25987866Ssheldonh				state = stSkipToEOL;
26087866Ssheldonh			} else {		/* something meaningfull */
26187866Ssheldonh				state = stGetKey;
26287866Ssheldonh			}
26387866Ssheldonh		}
26487866Ssheldonh		if (state == stSkipToEOL || next == last) {/* ignore long lines */
26587866Ssheldonh			if (c == '\n'){
26687866Ssheldonh				state = stNewLine;
26787866Ssheldonh				next = buf;
26887866Ssheldonh			}
26987866Ssheldonh			continue;
27087866Ssheldonh		}
27187866Ssheldonh		if (state == stHeader) {
27287866Ssheldonh			if (c == ']') {
27387866Ssheldonh				*next = 0;
27487866Ssheldonh				next = buf;
27587866Ssheldonh				rsp = rc_addsect(rcp, buf);
27687866Ssheldonh				state = stSkipToEOL;
27787866Ssheldonh			} else
27887866Ssheldonh				*next++ = c;
27987866Ssheldonh			continue;
28087866Ssheldonh		}
28187866Ssheldonh		if (state == stGetKey) {
28287866Ssheldonh			if (c == ' ' || c == '\t')/* side effect: 'key name='*/
28387866Ssheldonh				continue;	  /* become 'keyname=' 	     */
28487866Ssheldonh			if (c == '\n') {	/* silently ignore ... */
28587866Ssheldonh				state = stNewLine;
28687866Ssheldonh				continue;
28787866Ssheldonh			}
28887866Ssheldonh			if (c != '=') {
28987866Ssheldonh				*next++ = c;
29087866Ssheldonh				continue;
29187866Ssheldonh			}
29287866Ssheldonh			*next = 0;
29387866Ssheldonh			if (rsp == NULL) {
29487866Ssheldonh				fprintf(stderr, "Key '%s' defined before section\n", buf);
29587866Ssheldonh				state = stSkipToEOL;
29687866Ssheldonh				continue;
29787866Ssheldonh			}
29887866Ssheldonh			rkp = rc_sect_addkey(rsp, buf, NULL);
29987866Ssheldonh			next = buf;
30087866Ssheldonh			state = stGetValue;
30187866Ssheldonh			continue;
30287866Ssheldonh		}
30387866Ssheldonh		/* only stGetValue left */
30487866Ssheldonh		if (state != stGetValue) {
30587866Ssheldonh			fprintf(stderr, "Well, I can't parse file '%s'\n",rcp->rf_name);
30687866Ssheldonh			state = stSkipToEOL;
30787866Ssheldonh		}
30887866Ssheldonh		if (c != '\n') {
30987866Ssheldonh			*next++ = c;
31087866Ssheldonh			continue;
31187866Ssheldonh		}
31287866Ssheldonh		*next = 0;
31387866Ssheldonh		rkp->rk_value = strdup(buf);
31487866Ssheldonh		state = stNewLine;
31587866Ssheldonh		rkp = NULL;
31687866Ssheldonh	} 	/* while */
31787866Ssheldonh	if (c == EOF && state == stGetValue) {
31887866Ssheldonh		*next = 0;
31987866Ssheldonh		rkp->rk_value = strdup(buf);
32087866Ssheldonh	}
32187866Ssheldonh	return;
32287866Ssheldonh}
32387866Ssheldonh
32487866Ssheldonhint
32587866Ssheldonhrc_getstringptr(struct rcfile *rcp, const char *section, const char *key,
32687866Ssheldonh	char **dest)
32787866Ssheldonh{
32887866Ssheldonh	struct rcsection *rsp;
32987866Ssheldonh	struct rckey *rkp;
33087866Ssheldonh
33187866Ssheldonh	*dest = NULL;
33287866Ssheldonh	rsp = rc_findsect(rcp, section);
33387866Ssheldonh	if (!rsp) return ENOENT;
33487866Ssheldonh	rkp = rc_sect_findkey(rsp,key);
33587866Ssheldonh	if (!rkp) return ENOENT;
33687866Ssheldonh	*dest = rkp->rk_value;
33787866Ssheldonh	return 0;
33887866Ssheldonh}
33987866Ssheldonh
34087866Ssheldonhint
34187866Ssheldonhrc_getstring(struct rcfile *rcp, const char *section, const char *key,
34287866Ssheldonh	size_t maxlen, char *dest)
34387866Ssheldonh{
34487866Ssheldonh	char *value;
34587866Ssheldonh	int error;
34687866Ssheldonh
34787866Ssheldonh	error = rc_getstringptr(rcp, section, key, &value);
34887866Ssheldonh	if (error)
34987866Ssheldonh		return error;
35087866Ssheldonh	if (strlen(value) >= maxlen) {
351136700Sobrien		warnx("line too long for key '%s' in section '%s', max = %zd\n", key, section, maxlen);
35287866Ssheldonh		return EINVAL;
35387866Ssheldonh	}
35487866Ssheldonh	strcpy(dest, value);
35587866Ssheldonh	return 0;
35687866Ssheldonh}
35787866Ssheldonh
35887866Ssheldonhint
35987866Ssheldonhrc_getint(struct rcfile *rcp, const char *section, const char *key, int *value)
36087866Ssheldonh{
36187866Ssheldonh	struct rcsection *rsp;
36287866Ssheldonh	struct rckey *rkp;
36387866Ssheldonh
36487866Ssheldonh	rsp = rc_findsect(rcp, section);
36587866Ssheldonh	if (!rsp)
36687866Ssheldonh		return ENOENT;
36787866Ssheldonh	rkp = rc_sect_findkey(rsp, key);
36887866Ssheldonh	if (!rkp)
36987866Ssheldonh		return ENOENT;
37087866Ssheldonh	errno = 0;
37187866Ssheldonh	*value = strtol(rkp->rk_value, NULL, 0);
37287866Ssheldonh	if (errno) {
37387866Ssheldonh		warnx("invalid int value '%s' for key '%s' in section '%s'\n", rkp->rk_value, key, section);
37487866Ssheldonh		return errno;
37587866Ssheldonh	}
37687866Ssheldonh	return 0;
37787866Ssheldonh}
37887866Ssheldonh
37987866Ssheldonh/*
38087866Ssheldonh * 1,yes,true
38187866Ssheldonh * 0,no,false
38287866Ssheldonh */
38387866Ssheldonhint
38487866Ssheldonhrc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value)
38587866Ssheldonh{
38687866Ssheldonh	struct rcsection *rsp;
38787866Ssheldonh	struct rckey *rkp;
38887866Ssheldonh	char *p;
38987866Ssheldonh
39087866Ssheldonh	rsp = rc_findsect(rcp, section);
39187866Ssheldonh	if (!rsp) return ENOENT;
39287866Ssheldonh	rkp = rc_sect_findkey(rsp,key);
39387866Ssheldonh	if (!rkp) return ENOENT;
39487866Ssheldonh	p = rkp->rk_value;
39587866Ssheldonh	while (*p && isspace(*p)) p++;
39687866Ssheldonh	if (*p == '0' || strcasecmp(p,"no") == 0 || strcasecmp(p,"false") == 0) {
39787866Ssheldonh		*value = 0;
39887866Ssheldonh		return 0;
39987866Ssheldonh	}
40087866Ssheldonh	if (*p == '1' || strcasecmp(p,"yes") == 0 || strcasecmp(p,"true") == 0) {
40187866Ssheldonh		*value = 1;
40287866Ssheldonh		return 0;
40387866Ssheldonh	}
40487866Ssheldonh	fprintf(stderr, "invalid boolean value '%s' for key '%s' in section '%s' \n",p, key, section);
40587866Ssheldonh	return EINVAL;
40687866Ssheldonh}
40787866Ssheldonh
40887866Ssheldonh/*
40987866Ssheldonh * Unified command line/rc file parser
41087866Ssheldonh */
41187866Ssheldonhint
41287866Ssheldonhopt_args_parse(struct rcfile *rcp, struct opt_args *ap, const char *sect,
41387866Ssheldonh	opt_callback_t *callback)
41487866Ssheldonh{
41587866Ssheldonh	int len, error;
41687866Ssheldonh
41787866Ssheldonh	for (; ap->opt; ap++) {
41887866Ssheldonh		switch (ap->type) {
41987866Ssheldonh		    case OPTARG_STR:
42087866Ssheldonh			if (rc_getstringptr(rcp, sect, ap->name, &ap->str) != 0)
42187866Ssheldonh				break;
42287866Ssheldonh			len = strlen(ap->str);
42387866Ssheldonh			if (len > ap->ival) {
42487866Ssheldonh				warnx("rc: argument for option '%c' (%s) too long\n", ap->opt, ap->name);
42587866Ssheldonh				return EINVAL;
42687866Ssheldonh			}
42787866Ssheldonh			callback(ap);
42887866Ssheldonh			break;
42987866Ssheldonh		    case OPTARG_BOOL:
43087866Ssheldonh			error = rc_getbool(rcp, sect, ap->name, &ap->ival);
43187866Ssheldonh			if (error == ENOENT)
43287866Ssheldonh				break;
43387866Ssheldonh			if (error)
43487866Ssheldonh				return EINVAL;
43587866Ssheldonh			callback(ap);
43687866Ssheldonh			break;
43787866Ssheldonh		    case OPTARG_INT:
43887866Ssheldonh			if (rc_getint(rcp, sect, ap->name, &ap->ival) != 0)
43987866Ssheldonh				break;
44087866Ssheldonh			if (((ap->flag & OPTFL_HAVEMIN) && ap->ival < ap->min) ||
44187866Ssheldonh			    ((ap->flag & OPTFL_HAVEMAX) && ap->ival > ap->max)) {
44287866Ssheldonh				warnx("rc: argument for option '%c' (%s) should be in [%d-%d] range\n",
44387866Ssheldonh				    ap->opt, ap->name, ap->min, ap->max);
44487866Ssheldonh				return EINVAL;
44587866Ssheldonh			}
44687866Ssheldonh			callback(ap);
44787866Ssheldonh			break;
44887866Ssheldonh		    default:
44987866Ssheldonh			break;
45087866Ssheldonh		}
45187866Ssheldonh	}
45287866Ssheldonh	return 0;
45387866Ssheldonh}
45487866Ssheldonh
45587866Ssheldonhint
45687866Ssheldonhopt_args_parseopt(struct opt_args *ap, int opt, char *arg,
45787866Ssheldonh	opt_callback_t *callback)
45887866Ssheldonh{
45987866Ssheldonh	int len;
46087866Ssheldonh
46187866Ssheldonh	for (; ap->opt; ap++) {
46287866Ssheldonh		if (ap->opt != opt)
46387866Ssheldonh			continue;
46487866Ssheldonh		switch (ap->type) {
46587866Ssheldonh		    case OPTARG_STR:
46687866Ssheldonh			ap->str = arg;
46787866Ssheldonh			if (arg) {
46887866Ssheldonh				len = strlen(ap->str);
46987866Ssheldonh				if (len > ap->ival) {
47087866Ssheldonh					warnx("opt: Argument for option '%c' (%s) too long\n", ap->opt, ap->name);
47187866Ssheldonh					return EINVAL;
47287866Ssheldonh				}
47387866Ssheldonh				callback(ap);
47487866Ssheldonh			}
47587866Ssheldonh			break;
47687866Ssheldonh		    case OPTARG_BOOL:
47787866Ssheldonh			ap->ival = 0;
47887866Ssheldonh			callback(ap);
47987866Ssheldonh			break;
48087866Ssheldonh		    case OPTARG_INT:
48187866Ssheldonh			errno = 0;
48287866Ssheldonh			ap->ival = strtol(arg, NULL, 0);
48387866Ssheldonh			if (errno) {
48487866Ssheldonh				warnx("opt: Invalid integer value for option '%c' (%s).\n",ap->opt,ap->name);
48587866Ssheldonh				return EINVAL;
48687866Ssheldonh			}
48787866Ssheldonh			if (((ap->flag & OPTFL_HAVEMIN) &&
48887866Ssheldonh			     (ap->ival < ap->min)) ||
48987866Ssheldonh			    ((ap->flag & OPTFL_HAVEMAX) &&
49087866Ssheldonh			     (ap->ival > ap->max))) {
49187866Ssheldonh				warnx("opt: Argument for option '%c' (%s) should be in [%d-%d] range\n",ap->opt,ap->name,ap->min,ap->max);
49287866Ssheldonh				return EINVAL;
49387866Ssheldonh			}
49487866Ssheldonh			callback(ap);
49587866Ssheldonh			break;
49687866Ssheldonh		    default:
49787866Ssheldonh			break;
49887866Ssheldonh		}
49987866Ssheldonh		break;
50087866Ssheldonh	}
50187866Ssheldonh	return 0;
50287866Ssheldonh}
50387866Ssheldonh
504