xref: /openbsd-src/lib/libcrypto/conf/conf_mod.c (revision 2a59c28f1eda4417570b8d10bbffcb8558777f77)
1*2a59c28fStb /* $OpenBSD: conf_mod.c,v 1.40 2024/10/10 06:51:22 tb Exp $ */
2e6841c1dSdjm /* Written by Stephen Henson (steve@openssl.org) for the OpenSSL
3da347917Sbeck  * project 2001.
4da347917Sbeck  */
5da347917Sbeck /* ====================================================================
6da347917Sbeck  * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
7da347917Sbeck  *
8da347917Sbeck  * Redistribution and use in source and binary forms, with or without
9da347917Sbeck  * modification, are permitted provided that the following conditions
10da347917Sbeck  * are met:
11da347917Sbeck  *
12da347917Sbeck  * 1. Redistributions of source code must retain the above copyright
13da347917Sbeck  *    notice, this list of conditions and the following disclaimer.
14da347917Sbeck  *
15da347917Sbeck  * 2. Redistributions in binary form must reproduce the above copyright
16da347917Sbeck  *    notice, this list of conditions and the following disclaimer in
17da347917Sbeck  *    the documentation and/or other materials provided with the
18da347917Sbeck  *    distribution.
19da347917Sbeck  *
20da347917Sbeck  * 3. All advertising materials mentioning features or use of this
21da347917Sbeck  *    software must display the following acknowledgment:
22da347917Sbeck  *    "This product includes software developed by the OpenSSL Project
23da347917Sbeck  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24da347917Sbeck  *
25da347917Sbeck  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26da347917Sbeck  *    endorse or promote products derived from this software without
27da347917Sbeck  *    prior written permission. For written permission, please contact
28da347917Sbeck  *    licensing@OpenSSL.org.
29da347917Sbeck  *
30da347917Sbeck  * 5. Products derived from this software may not be called "OpenSSL"
31da347917Sbeck  *    nor may "OpenSSL" appear in their names without prior written
32da347917Sbeck  *    permission of the OpenSSL Project.
33da347917Sbeck  *
34da347917Sbeck  * 6. Redistributions of any form whatsoever must retain the following
35da347917Sbeck  *    acknowledgment:
36da347917Sbeck  *    "This product includes software developed by the OpenSSL Project
37da347917Sbeck  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38da347917Sbeck  *
39da347917Sbeck  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40da347917Sbeck  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41da347917Sbeck  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42da347917Sbeck  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43da347917Sbeck  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44da347917Sbeck  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45da347917Sbeck  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46da347917Sbeck  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47da347917Sbeck  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48da347917Sbeck  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49da347917Sbeck  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50da347917Sbeck  * OF THE POSSIBILITY OF SUCH DAMAGE.
51da347917Sbeck  * ====================================================================
52da347917Sbeck  *
53da347917Sbeck  * This product includes cryptographic software written by Eric Young
54da347917Sbeck  * (eay@cryptsoft.com).  This product includes software written by Tim
55da347917Sbeck  * Hudson (tjh@cryptsoft.com).
56da347917Sbeck  *
57da347917Sbeck  */
58da347917Sbeck 
59da347917Sbeck #include <ctype.h>
60a8913c44Sjsing #include <stdio.h>
61a8913c44Sjsing #include <string.h>
62a8913c44Sjsing #include <unistd.h>
63a8913c44Sjsing 
64da347917Sbeck #include <openssl/conf.h>
65b6ab114eSjsing #include <openssl/crypto.h>
66b6ab114eSjsing #include <openssl/err.h>
67da347917Sbeck #include <openssl/x509.h>
68da347917Sbeck 
69e0656bc1Stb /* This structure contains data about supported modules. */
7044486fcbSjsing struct conf_module_st {
71da347917Sbeck 	/* Name of the module */
72da347917Sbeck 	char *name;
73da347917Sbeck 	/* Init function */
74da347917Sbeck 	conf_init_func *init;
75da347917Sbeck 	/* Finish function */
76da347917Sbeck 	conf_finish_func *finish;
77da347917Sbeck 	/* Number of successfully initialized modules */
78da347917Sbeck 	int links;
79da347917Sbeck };
80da347917Sbeck 
81da347917Sbeck 
82da347917Sbeck /* This structure contains information about modules that have been
83da347917Sbeck  * successfully initialized. There may be more than one entry for a
84da347917Sbeck  * given module.
85da347917Sbeck  */
86da347917Sbeck 
8744486fcbSjsing struct conf_imodule_st {
88482e134aStb 	CONF_MODULE *mod;
89da347917Sbeck 	char *value;
90da347917Sbeck };
91da347917Sbeck 
92da347917Sbeck static STACK_OF(CONF_MODULE) *supported_modules = NULL;
93da347917Sbeck static STACK_OF(CONF_IMODULE) *initialized_modules = NULL;
94da347917Sbeck 
95511ac327Stb static void module_free(CONF_MODULE *mod);
961d022c02Stb static void imodule_free(CONF_IMODULE *imod);
97da347917Sbeck static void module_finish(CONF_IMODULE *imod);
98da347917Sbeck static int module_run(const CONF *cnf, char *name, char *value,
99da347917Sbeck     unsigned long flags);
1003cdf45a4Stb static int module_add(const char *name, conf_init_func *ifunc,
101e0656bc1Stb     conf_finish_func *ffunc);
102da347917Sbeck static CONF_MODULE *module_find(char *name);
1035c82b5ecStb static int module_init(CONF_MODULE *mod, char *name, char *value,
104da347917Sbeck     const CONF *cnf);
105da347917Sbeck 
106da347917Sbeck /* Main function: load modules from a CONF structure */
107da347917Sbeck 
10844486fcbSjsing int
10944486fcbSjsing CONF_modules_load(const CONF *cnf, const char *appname, unsigned long flags)
110da347917Sbeck {
111da347917Sbeck 	STACK_OF(CONF_VALUE) *values;
112da347917Sbeck 	CONF_VALUE *vl;
1134fcf65c5Sdjm 	char *vsection = NULL;
114da347917Sbeck 
115da347917Sbeck 	int ret, i;
116da347917Sbeck 
117da347917Sbeck 	if (!cnf)
118da347917Sbeck 		return 1;
119da347917Sbeck 
1204fcf65c5Sdjm 	if (appname)
121da347917Sbeck 		vsection = NCONF_get_string(cnf, NULL, appname);
122da347917Sbeck 
1234fcf65c5Sdjm 	if (!appname || (!vsection && (flags & CONF_MFLAGS_DEFAULT_SECTION)))
1244fcf65c5Sdjm 		vsection = NCONF_get_string(cnf, NULL, "openssl_conf");
1254fcf65c5Sdjm 
12644486fcbSjsing 	if (!vsection) {
127da347917Sbeck 		ERR_clear_error();
128da347917Sbeck 		return 1;
129da347917Sbeck 	}
130da347917Sbeck 
131da347917Sbeck 	values = NCONF_get_section(cnf, vsection);
132da347917Sbeck 
133da347917Sbeck 	if (!values)
134da347917Sbeck 		return 0;
135da347917Sbeck 
13644486fcbSjsing 	for (i = 0; i < sk_CONF_VALUE_num(values); i++) {
137da347917Sbeck 		vl = sk_CONF_VALUE_value(values, i);
138da347917Sbeck 		ret = module_run(cnf, vl->name, vl->value, flags);
139da347917Sbeck 		if (ret <= 0)
140da347917Sbeck 			if (!(flags & CONF_MFLAGS_IGNORE_ERRORS))
141da347917Sbeck 				return ret;
142da347917Sbeck 	}
143da347917Sbeck 
144da347917Sbeck 	return 1;
145da347917Sbeck }
146bbdd77aaSbeck LCRYPTO_ALIAS(CONF_modules_load);
147da347917Sbeck 
14844486fcbSjsing int
14944486fcbSjsing CONF_modules_load_file(const char *filename, const char *appname,
150da347917Sbeck     unsigned long flags)
151da347917Sbeck {
152da347917Sbeck 	char *file = NULL;
153da347917Sbeck 	CONF *conf = NULL;
154da347917Sbeck 	int ret = 0;
155da347917Sbeck 	conf = NCONF_new(NULL);
156da347917Sbeck 	if (!conf)
157da347917Sbeck 		goto err;
158da347917Sbeck 
15944486fcbSjsing 	if (filename == NULL) {
160da347917Sbeck 		file = CONF_get1_default_config_file();
161da347917Sbeck 		if (!file)
162da347917Sbeck 			goto err;
16344486fcbSjsing 	} else
164da347917Sbeck 		file = (char *)filename;
165da347917Sbeck 
16644486fcbSjsing 	if (NCONF_load(conf, file, NULL) <= 0) {
167da347917Sbeck 		if ((flags & CONF_MFLAGS_IGNORE_MISSING_FILE) &&
16844486fcbSjsing 		    (ERR_GET_REASON(ERR_peek_last_error()) ==
16944486fcbSjsing 		    CONF_R_NO_SUCH_FILE)) {
170da347917Sbeck 			ERR_clear_error();
171da347917Sbeck 			ret = 1;
172da347917Sbeck 		}
173da347917Sbeck 		goto err;
174da347917Sbeck 	}
175da347917Sbeck 
176da347917Sbeck 	ret = CONF_modules_load(conf, appname, flags);
177da347917Sbeck 
178da347917Sbeck err:
179da347917Sbeck 	if (filename == NULL)
1806f3a6cb1Sbeck 		free(file);
181da347917Sbeck 	NCONF_free(conf);
182da347917Sbeck 
183da347917Sbeck 	return ret;
184da347917Sbeck }
185bbdd77aaSbeck LCRYPTO_ALIAS(CONF_modules_load_file);
186da347917Sbeck 
18744486fcbSjsing static int
18844486fcbSjsing module_run(const CONF *cnf, char *name, char *value, unsigned long flags)
189da347917Sbeck {
190511ac327Stb 	CONF_MODULE *mod;
191da347917Sbeck 	int ret;
192da347917Sbeck 
193511ac327Stb 	if ((mod = module_find(name)) == NULL) {
19444486fcbSjsing 		if (!(flags & CONF_MFLAGS_SILENT)) {
1955067ae9fSbeck 			CONFerror(CONF_R_UNKNOWN_MODULE_NAME);
1960f637b92Sbeck 			ERR_asprintf_error_data("module=%s", name);
197da347917Sbeck 		}
198da347917Sbeck 		return -1;
199da347917Sbeck 	}
200da347917Sbeck 
201511ac327Stb 	ret = module_init(mod, name, value, cnf);
202da347917Sbeck 
20344486fcbSjsing 	if (ret <= 0) {
20444486fcbSjsing 		if (!(flags & CONF_MFLAGS_SILENT)) {
2055067ae9fSbeck 			CONFerror(CONF_R_MODULE_INITIALIZATION_ERROR);
2060f637b92Sbeck 			ERR_asprintf_error_data
2070f637b92Sbeck 			    ("module=%s, value=%s, retcode=%-8d",
2080f637b92Sbeck 			    name, value, ret);
209da347917Sbeck 		}
210da347917Sbeck 	}
211da347917Sbeck 
212da347917Sbeck 	return ret;
213da347917Sbeck }
214da347917Sbeck 
2153cdf45a4Stb static int
216e0656bc1Stb module_add(const char *name, conf_init_func *ifunc, conf_finish_func *ffunc)
217da347917Sbeck {
218fee7a564Stb 	CONF_MODULE *mod = NULL;
2193cdf45a4Stb 	int ret = 0;
22044486fcbSjsing 
22169442892Sbeck 	if (name == NULL)
2223cdf45a4Stb 		goto err;
2233cdf45a4Stb 
224da347917Sbeck 	if (supported_modules == NULL)
225da347917Sbeck 		supported_modules = sk_CONF_MODULE_new_null();
226da347917Sbeck 	if (supported_modules == NULL)
2273cdf45a4Stb 		goto err;
228da347917Sbeck 
229fee7a564Stb 	if ((mod = calloc(1, sizeof(*mod))) == NULL)
2303cdf45a4Stb 		goto err;
231fee7a564Stb 	if ((mod->name = strdup(name)) == NULL)
2323cdf45a4Stb 		goto err;
233fee7a564Stb 	mod->init = ifunc;
234fee7a564Stb 	mod->finish = ffunc;
235da347917Sbeck 
236fee7a564Stb 	if (!sk_CONF_MODULE_push(supported_modules, mod))
2373cdf45a4Stb 		goto err;
238fee7a564Stb 	mod = NULL;
239da347917Sbeck 
2403cdf45a4Stb 	ret = 1;
2413cdf45a4Stb 
2423cdf45a4Stb  err:
243fee7a564Stb 	module_free(mod);
2443cdf45a4Stb 
2453cdf45a4Stb 	return ret;
246da347917Sbeck }
247da347917Sbeck 
248da347917Sbeck /* Find a module from the list. We allow module names of the
249da347917Sbeck  * form modname.XXXX to just search for modname to allow the
250da347917Sbeck  * same module to be initialized more than once.
251da347917Sbeck  */
252da347917Sbeck 
25344486fcbSjsing static CONF_MODULE *
25444486fcbSjsing module_find(char *name)
255da347917Sbeck {
256fee7a564Stb 	CONF_MODULE *mod;
257da347917Sbeck 	int i, nchar;
258da347917Sbeck 	char *p;
25944486fcbSjsing 
260da347917Sbeck 	p = strrchr(name, '.');
261da347917Sbeck 
262da347917Sbeck 	if (p)
263da347917Sbeck 		nchar = p - name;
264da347917Sbeck 	else
265da347917Sbeck 		nchar = strlen(name);
266da347917Sbeck 
26744486fcbSjsing 	for (i = 0; i < sk_CONF_MODULE_num(supported_modules); i++) {
268fee7a564Stb 		mod = sk_CONF_MODULE_value(supported_modules, i);
269fee7a564Stb 		if (!strncmp(mod->name, name, nchar))
270fee7a564Stb 			return mod;
271da347917Sbeck 	}
272da347917Sbeck 
273da347917Sbeck 	return NULL;
274da347917Sbeck }
275da347917Sbeck 
276da347917Sbeck /* initialize a module */
27744486fcbSjsing static int
2785c82b5ecStb module_init(CONF_MODULE *mod, char *name, char *value, const CONF *cnf)
279da347917Sbeck {
280da347917Sbeck 	CONF_IMODULE *imod = NULL;
28196025fd0Stb 	int need_finish = 0;
28296025fd0Stb 	int ret = -1;
283da347917Sbeck 
28496025fd0Stb 	if (name == NULL || value == NULL)
28596025fd0Stb 		goto err;
28696025fd0Stb 
28796025fd0Stb 	if ((imod = calloc(1, sizeof(*imod))) == NULL)
288da347917Sbeck 		goto err;
289da347917Sbeck 
2905c82b5ecStb 	imod->mod = mod;
291da347917Sbeck 
29296025fd0Stb 	if ((imod->value = strdup(value)) == NULL)
29396025fd0Stb 		goto err;
294da347917Sbeck 
29596025fd0Stb 	if (mod->init != NULL) {
29696025fd0Stb 		need_finish = 1;
29796025fd0Stb 		if (mod->init(imod, cnf) <= 0)
298da347917Sbeck 			goto err;
299da347917Sbeck 	}
300da347917Sbeck 
30196025fd0Stb 	if (initialized_modules == NULL)
302da347917Sbeck 		initialized_modules = sk_CONF_IMODULE_new_null();
30396025fd0Stb 	if (initialized_modules == NULL)
304da347917Sbeck 		goto err;
305da347917Sbeck 
30696025fd0Stb 	if (!sk_CONF_IMODULE_push(initialized_modules, imod))
307da347917Sbeck 		goto err;
30896025fd0Stb 	imod = NULL;
30996025fd0Stb 	need_finish = 0;
310da347917Sbeck 
3115c82b5ecStb 	mod->links++;
312da347917Sbeck 
31396025fd0Stb 	ret = 1;
314da347917Sbeck 
315da347917Sbeck  err:
31696025fd0Stb 	if (need_finish && mod->finish != NULL)
3175c82b5ecStb 		mod->finish(imod);
318da347917Sbeck 
31996025fd0Stb 	imodule_free(imod);
320da347917Sbeck 
32196025fd0Stb 	return ret;
322da347917Sbeck }
323da347917Sbeck 
324da347917Sbeck /* Unload any dynamic modules that have a link count of zero:
325da347917Sbeck  * i.e. have no active initialized modules. If 'all' is set
326da347917Sbeck  * then all modules are unloaded including static ones.
327da347917Sbeck  */
328da347917Sbeck 
32944486fcbSjsing void
33044486fcbSjsing CONF_modules_unload(int all)
331da347917Sbeck {
332da347917Sbeck 	int i;
333511ac327Stb 	CONF_MODULE *mod;
33444486fcbSjsing 
335da347917Sbeck 	CONF_modules_finish();
33644486fcbSjsing 
337da347917Sbeck 	/* unload modules in reverse order */
33844486fcbSjsing 	for (i = sk_CONF_MODULE_num(supported_modules) - 1; i >= 0; i--) {
339511ac327Stb 		mod = sk_CONF_MODULE_value(supported_modules, i);
340e0656bc1Stb 		if (!all)
341da347917Sbeck 			continue;
342da347917Sbeck 		/* Since we're working in reverse this is OK */
3434fcf65c5Sdjm 		(void)sk_CONF_MODULE_delete(supported_modules, i);
344511ac327Stb 		module_free(mod);
345da347917Sbeck 	}
34644486fcbSjsing 	if (sk_CONF_MODULE_num(supported_modules) == 0) {
347da347917Sbeck 		sk_CONF_MODULE_free(supported_modules);
348da347917Sbeck 		supported_modules = NULL;
349da347917Sbeck 	}
350da347917Sbeck }
351bbdd77aaSbeck LCRYPTO_ALIAS(CONF_modules_unload);
352da347917Sbeck 
353da347917Sbeck /* unload a single module */
35444486fcbSjsing static void
355511ac327Stb module_free(CONF_MODULE *mod)
356da347917Sbeck {
357511ac327Stb 	if (mod == NULL)
358f06d1561Stb 		return;
359f06d1561Stb 
360511ac327Stb 	free(mod->name);
361511ac327Stb 	free(mod);
362da347917Sbeck }
363da347917Sbeck 
3641d022c02Stb static void
3651d022c02Stb imodule_free(CONF_IMODULE *imod)
3661d022c02Stb {
3671d022c02Stb 	if (imod == NULL)
3681d022c02Stb 		return;
3691d022c02Stb 
3701d022c02Stb 	free(imod->value);
3711d022c02Stb 	free(imod);
3721d022c02Stb }
3731d022c02Stb 
374da347917Sbeck /* finish and free up all modules instances */
375da347917Sbeck 
37644486fcbSjsing void
37744486fcbSjsing CONF_modules_finish(void)
378da347917Sbeck {
379da347917Sbeck 	CONF_IMODULE *imod;
38044486fcbSjsing 
38144486fcbSjsing 	while (sk_CONF_IMODULE_num(initialized_modules) > 0) {
382da347917Sbeck 		imod = sk_CONF_IMODULE_pop(initialized_modules);
383da347917Sbeck 		module_finish(imod);
384da347917Sbeck 	}
385da347917Sbeck 	sk_CONF_IMODULE_free(initialized_modules);
386da347917Sbeck 	initialized_modules = NULL;
387da347917Sbeck }
388bbdd77aaSbeck LCRYPTO_ALIAS(CONF_modules_finish);
389da347917Sbeck 
390da347917Sbeck /* finish a module instance */
391da347917Sbeck 
39244486fcbSjsing static void
39344486fcbSjsing module_finish(CONF_IMODULE *imod)
394da347917Sbeck {
395482e134aStb 	if (imod->mod->finish)
396482e134aStb 		imod->mod->finish(imod);
397482e134aStb 	imod->mod->links--;
3981d022c02Stb 
3991d022c02Stb 	imodule_free(imod);
400da347917Sbeck }
401da347917Sbeck 
402da347917Sbeck /* Add a static module to OpenSSL */
403da347917Sbeck 
40444486fcbSjsing int
405e0656bc1Stb CONF_module_add(const char *name, conf_init_func *ifunc, conf_finish_func *ffunc)
406da347917Sbeck {
4073cdf45a4Stb 	return module_add(name, ifunc, ffunc);
408da347917Sbeck }
409da347917Sbeck 
41044486fcbSjsing void
41144486fcbSjsing CONF_modules_free(void)
412da347917Sbeck {
413da347917Sbeck 	CONF_modules_finish();
414da347917Sbeck 	CONF_modules_unload(1);
415da347917Sbeck }
416bbdd77aaSbeck LCRYPTO_ALIAS(CONF_modules_free);
417da347917Sbeck 
41844486fcbSjsing const char *
4191d159ddeStb CONF_imodule_get_value(const CONF_IMODULE *imod)
420da347917Sbeck {
4211d159ddeStb 	return imod->value;
422da347917Sbeck }
423da347917Sbeck 
4241297a291Sderaadt char *
4251297a291Sderaadt CONF_get1_default_config_file(void)
426da347917Sbeck {
42775eb8854Sderaadt 	char *file = NULL;
428da347917Sbeck 
42944486fcbSjsing 	if (asprintf(&file, "%s/openssl.cnf",
43044486fcbSjsing 	    X509_get_default_cert_area()) == -1)
431e1962626Sderaadt 		return (NULL);
432da347917Sbeck 	return file;
433da347917Sbeck }
434bbdd77aaSbeck LCRYPTO_ALIAS(CONF_get1_default_config_file);
435da347917Sbeck 
436da347917Sbeck /* This function takes a list separated by 'sep' and calls the
437da347917Sbeck  * callback function giving the start and length of each member
438da347917Sbeck  * optionally stripping leading and trailing whitespace. This can
439da347917Sbeck  * be used to parse comma separated lists for example.
440da347917Sbeck  */
441da347917Sbeck 
44244486fcbSjsing int
44344486fcbSjsing CONF_parse_list(const char *list_, int sep, int nospc,
444da347917Sbeck     int (*list_cb)(const char *elem, int len, void *usr), void *arg)
445da347917Sbeck {
446da347917Sbeck 	int ret;
447da347917Sbeck 	const char *lstart, *tmpend, *p;
448da347917Sbeck 
44944486fcbSjsing 	if (list_ == NULL) {
4505067ae9fSbeck 		CONFerror(CONF_R_LIST_CANNOT_BE_NULL);
4510a5d6edeSdjm 		return 0;
4520a5d6edeSdjm 	}
4530a5d6edeSdjm 
4540a5d6edeSdjm 	lstart = list_;
45544486fcbSjsing 	for (;;) {
45644486fcbSjsing 		if (nospc) {
45750c17820Sdjm 			while (*lstart && isspace((unsigned char)*lstart))
458da347917Sbeck 				lstart++;
459da347917Sbeck 		}
460da347917Sbeck 		p = strchr(lstart, sep);
461da347917Sbeck 		if (p == lstart || !*lstart)
462da347917Sbeck 			ret = list_cb(NULL, 0, arg);
46344486fcbSjsing 		else {
464da347917Sbeck 			if (p)
465da347917Sbeck 				tmpend = p - 1;
466da347917Sbeck 			else
467da347917Sbeck 				tmpend = lstart + strlen(lstart) - 1;
46844486fcbSjsing 			if (nospc) {
469da347917Sbeck 				while (isspace((unsigned char)*tmpend))
470da347917Sbeck 					tmpend--;
471da347917Sbeck 			}
472da347917Sbeck 			ret = list_cb(lstart, tmpend - lstart + 1, arg);
473da347917Sbeck 		}
474da347917Sbeck 		if (ret <= 0)
475da347917Sbeck 			return ret;
476da347917Sbeck 		if (p == NULL)
477da347917Sbeck 			return 1;
478da347917Sbeck 		lstart = p + 1;
479da347917Sbeck 	}
480da347917Sbeck }
481