1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#pragma prototyped
23
24/*
25 * string interface to confstr(),pathconf(),sysconf(),sysinfo()
26 * extended to allow some features to be set per-process
27 */
28
29static const char id[] = "\n@(#)$Id: getconf (AT&T Research) 2009-07-02 $\0\n";
30
31#include "univlib.h"
32
33#include <ast.h>
34#include <error.h>
35#include <fs3d.h>
36#include <ctype.h>
37#include <regex.h>
38#include <proc.h>
39
40#include "conftab.h"
41#include "FEATURE/libpath"
42
43#ifndef DEBUG_astconf
44#define DEBUG_astconf		0
45#endif
46
47#ifndef _pth_getconf
48#undef	ASTCONF_system
49#define ASTCONF_system		0
50#endif
51
52#if _sys_systeminfo
53# if !_lib_sysinfo
54#   if _lib_systeminfo
55#     define _lib_sysinfo	1
56#     define sysinfo(a,b,c)	systeminfo(a,b,c)
57#   else
58#     if _lib_syscall && _sys_syscall
59#       include <sys/syscall.h>
60#       if defined(SYS_systeminfo)
61#         define _lib_sysinfo	1
62#         define sysinfo(a,b,c)	syscall(SYS_systeminfo,a,b,c)
63#       endif
64#     endif
65#   endif
66# endif
67#else
68# undef	_lib_sysinfo
69#endif
70
71#define CONF_ERROR	(CONF_USER<<0)
72#define CONF_READONLY	(CONF_USER<<1)
73#define CONF_ALLOC	(CONF_USER<<2)
74#define CONF_GLOBAL	(CONF_USER<<3)
75
76#define DEFAULT(o)	((state.std||!dynamic[o].ast)?dynamic[o].std:dynamic[o].ast)
77#define INITIALIZE()	do{if(!state.data)synthesize(NiL,NiL,NiL);}while(0)
78#define STANDARD(v)	(streq(v,"standard")||streq(v,"strict")||streq(v,"posix")||streq(v,"xopen"))
79
80#define MAXVAL		256
81
82#if MAXVAL <= UNIV_SIZE
83#undef	MAXVAL
84#define	MAXVAL		(UNIV_SIZE+1)
85#endif
86
87#ifndef _UNIV_DEFAULT
88#define _UNIV_DEFAULT	"att"
89#endif
90
91static char	null[1];
92static char	root[2] = "/";
93
94typedef struct Feature_s
95{
96	struct Feature_s*next;
97	const char*	name;
98	char*		value;
99	char*		std;
100	char*		ast;
101	short		length;
102	short		standard;
103	unsigned int	flags;
104	short		op;
105} Feature_t;
106
107typedef struct
108{
109	Conf_t*		conf;
110	const char*	name;
111	unsigned int	flags;
112	short		call;
113	short		standard;
114	short		section;
115} Lookup_t;
116
117static Feature_t	dynamic[] =
118{
119#define OP_conformance	0
120	{
121		&dynamic[OP_conformance+1],
122		"CONFORMANCE",
123		"ast",
124		"standard",
125		"ast",
126		11,
127		CONF_AST,
128		0,
129		OP_conformance
130	},
131#define OP_fs_3d	1
132	{
133		&dynamic[OP_fs_3d+1],
134		"FS_3D",
135		&null[0],
136		"0",
137		0,
138		5,
139		CONF_AST,
140		0,
141		OP_fs_3d
142	},
143#define OP_getconf	2
144	{
145		&dynamic[OP_getconf+1],
146		"GETCONF",
147#ifdef _pth_getconf
148		_pth_getconf,
149#else
150		&null[0],
151#endif
152		0,
153		0,
154		7,
155		CONF_AST,
156		CONF_READONLY,
157		OP_getconf
158	},
159#define OP_hosttype	3
160	{
161		&dynamic[OP_hosttype+1],
162		"HOSTTYPE",
163		HOSTTYPE,
164		0,
165		0,
166		8,
167		CONF_AST,
168		CONF_READONLY,
169		OP_hosttype
170	},
171#define OP_libpath	4
172	{
173		&dynamic[OP_libpath+1],
174		"LIBPATH",
175#ifdef CONF_LIBPATH
176		CONF_LIBPATH,
177#else
178		&null[0],
179#endif
180		0,
181		0,
182		7,
183		CONF_AST,
184		0,
185		OP_libpath
186	},
187#define OP_libprefix	5
188	{
189		&dynamic[OP_libprefix+1],
190		"LIBPREFIX",
191#ifdef CONF_LIBPREFIX
192		CONF_LIBPREFIX,
193#else
194		"lib",
195#endif
196		0,
197		0,
198		9,
199		CONF_AST,
200		0,
201		OP_libprefix
202	},
203#define OP_libsuffix	6
204	{
205		&dynamic[OP_libsuffix+1],
206		"LIBSUFFIX",
207#ifdef CONF_LIBSUFFIX
208		CONF_LIBSUFFIX,
209#else
210		".so",
211#endif
212		0,
213		0,
214		9,
215		CONF_AST,
216		0,
217		OP_libsuffix
218	},
219#define OP_path_attributes	7
220	{
221		&dynamic[OP_path_attributes+1],
222		"PATH_ATTRIBUTES",
223#if _WINIX
224		"c",
225#else
226		&null[0],
227#endif
228		&null[0],
229		0,
230		15,
231		CONF_AST,
232		CONF_READONLY,
233		OP_path_attributes
234	},
235#define OP_path_resolve	8
236	{
237		&dynamic[OP_path_resolve+1],
238		"PATH_RESOLVE",
239		&null[0],
240		"physical",
241		"metaphysical",
242		12,
243		CONF_AST,
244		0,
245		OP_path_resolve
246	},
247#define OP_universe	9
248	{
249		0,
250		"UNIVERSE",
251		&null[0],
252		"att",
253		0,
254		8,
255		CONF_AST,
256		0,
257		OP_universe
258	},
259	{
260		0
261	}
262};
263
264typedef struct
265{
266
267	const char*	id;
268	const char*	name;
269	Feature_t*	features;
270
271	int		std;
272
273	/* default initialization from here down */
274
275	int		prefix;
276	int		synthesizing;
277
278	char*		data;
279	char*		last;
280
281	Feature_t*	recent;
282
283	Ast_confdisc_f	notify;
284
285} State_t;
286
287static State_t	state = { "getconf", "_AST_FEATURES", dynamic, -1 };
288
289static char*	feature(const char*, const char*, const char*, unsigned int, Error_f);
290
291/*
292 * return fmtbuf() copy of s
293 */
294
295static char*
296buffer(char* s)
297{
298	return strcpy(fmtbuf(strlen(s) + 1), s);
299}
300
301/*
302 * synthesize state for fp
303 * fp==0 initializes from getenv(state.name)
304 * value==0 just does lookup
305 * otherwise state is set to value
306 */
307
308static char*
309synthesize(register Feature_t* fp, const char* path, const char* value)
310{
311	register char*		s;
312	register char*		d;
313	register char*		v;
314	register char*		p;
315	register int		n;
316
317#if DEBUG_astconf
318	if (fp)
319		error(-2, "astconf synthesize name=%s path=%s value=%s fp=%p%s", fp->name, path, value, fp, state.synthesizing ? " SYNTHESIZING" : "");
320#endif
321	if (state.synthesizing)
322		return null;
323	if (!state.data)
324	{
325		char*		se;
326		char*		de;
327		char*		ve;
328
329		state.prefix = strlen(state.name) + 1;
330		n = state.prefix + 3 * MAXVAL;
331		if (s = getenv(state.name))
332			n += strlen(s) + 1;
333		n = roundof(n, 32);
334		if (!(state.data = newof(0, char, n, 0)))
335			return 0;
336		state.last = state.data + n - 1;
337		strcpy(state.data, state.name);
338		state.data += state.prefix - 1;
339		*state.data++ = '=';
340		if (s)
341			strcpy(state.data, s);
342		ve = state.data;
343		state.synthesizing = 1;
344		for (;;)
345		{
346			for (s = ve; isspace(*s); s++);
347			for (d = s; *d && !isspace(*d); d++);
348			for (se = d; isspace(*d); d++);
349			for (v = d; *v && !isspace(*v); v++);
350			for (de = v; isspace(*v); v++);
351			if (!*v)
352				break;
353			for (ve = v; *ve && !isspace(*ve); ve++);
354			if (*ve)
355				*ve = 0;
356			else
357				ve = 0;
358			*de = 0;
359			*se = 0;
360			feature(s, d, v, 0, 0);
361			*se = ' ';
362			*de = ' ';
363			if (!ve)
364				break;
365			*ve++ = ' ';
366		}
367		state.synthesizing = 0;
368	}
369	if (!fp)
370		return state.data;
371	if (!state.last)
372	{
373		if (!value)
374			return 0;
375		n = strlen(value);
376		goto ok;
377	}
378	s = (char*)fp->name;
379	n = fp->length;
380	d = state.data;
381	for (;;)
382	{
383		while (isspace(*d))
384			d++;
385		if (!*d)
386			break;
387		if (strneq(d, s, n) && isspace(d[n]))
388		{
389			if (!value)
390			{
391				for (d += n + 1; *d && !isspace(*d); d++);
392				for (; isspace(*d); d++);
393				for (s = d; *s && !isspace(*s); s++);
394				n = s - d;
395				value = (const char*)d;
396				goto ok;
397			}
398			for (s = p = d + n + 1; *s && !isspace(*s); s++);
399			for (; isspace(*s); s++);
400			for (v = s; *s && !isspace(*s); s++);
401			n = s - v;
402			if ((!path || *path == *p && strlen(path) == (v - p - 1) && !memcmp(path, p, v - p - 1)) && strneq(v, value, n))
403				goto ok;
404			for (; isspace(*s); s++);
405			if (*s)
406				for (; *d = *s++; d++);
407			else if (d != state.data)
408				d--;
409			break;
410		}
411		for (; *d && !isspace(*d); d++);
412		for (; isspace(*d); d++);
413		for (; *d && !isspace(*d); d++);
414		for (; isspace(*d); d++);
415		for (; *d && !isspace(*d); d++);
416	}
417	if (!value)
418	{
419		if (!fp->op)
420		{
421			if (fp->flags & CONF_ALLOC)
422				fp->value[0] = 0;
423			else
424				fp->value = null;
425		}
426		return 0;
427	}
428	if (!value[0])
429		value = "0";
430	if (!path || !path[0] || path[0] == '/' && !path[1])
431		path = "-";
432	n += strlen(path) + strlen(value) + 3;
433	if (d + n >= state.last)
434	{
435		int	c;
436		int	i;
437
438		i = d - state.data;
439		state.data -= state.prefix;
440		c = n + state.last - state.data + 3 * MAXVAL;
441		c = roundof(c, 32);
442		if (!(state.data = newof(state.data, char, c, 0)))
443			return 0;
444		state.last = state.data + c - 1;
445		state.data += state.prefix;
446		d = state.data + i;
447	}
448	if (d != state.data)
449		*d++ = ' ';
450	for (s = (char*)fp->name; *d = *s++; d++);
451	*d++ = ' ';
452	for (s = (char*)path; *d = *s++; d++);
453	*d++ = ' ';
454	for (s = (char*)value; *d = *s++; d++);
455#if DEBUG_astconf
456	error(-3, "astconf synthesize %s", state.data - state.prefix);
457#endif
458	setenviron(state.data - state.prefix);
459	if (state.notify)
460		(*state.notify)(NiL, NiL, state.data - state.prefix);
461	n = s - (char*)value - 1;
462 ok:
463	if (!(fp->flags & CONF_ALLOC))
464		fp->value = 0;
465	if (n == 1 && (*value == '0' || *value == '-'))
466		n = 0;
467	if (!(fp->value = newof(fp->value, char, n, 1)))
468		fp->value = null;
469	else
470	{
471		fp->flags |= CONF_ALLOC;
472		memcpy(fp->value, value, n);
473		fp->value[n] = 0;
474	}
475	return fp->value;
476}
477
478/*
479 * initialize the value for fp
480 * if command!=0 then it is checked for on $PATH
481 * synthesize(fp,path,succeed) called on success
482 * otherwise synthesize(fp,path,fail) called
483 */
484
485static void
486initialize(register Feature_t* fp, const char* path, const char* command, const char* succeed, const char* fail)
487{
488	register char*	p;
489	register int	ok = 1;
490
491#if DEBUG_astconf
492	error(-2, "astconf initialize name=%s path=%s command=%s succeed=%s fail=%s fp=%p%s", fp->name, path, command, succeed, fail, fp, state.synthesizing ? " SYNTHESIZING" : "");
493#endif
494	switch (fp->op)
495	{
496	case OP_conformance:
497		ok = getenv("POSIXLY_CORRECT") != 0;
498		break;
499	case OP_hosttype:
500		ok = 1;
501		break;
502	case OP_path_attributes:
503		ok = 1;
504		break;
505	case OP_path_resolve:
506		ok = fs3d(FS3D_TEST);
507		break;
508	case OP_universe:
509		ok = streq(_UNIV_DEFAULT, DEFAULT(OP_universe));
510		/*FALLTHROUGH...*/
511	default:
512		if (p = getenv("PATH"))
513		{
514			register int	r = 1;
515			register char*	d = p;
516			Sfio_t*		tmp;
517
518#if DEBUG_astconf
519			error(-2, "astconf initialize name=%s ok=%d PATH=%s", fp->name, ok, p);
520#endif
521			if (tmp = sfstropen())
522			{
523				for (;;)
524				{
525					switch (*p++)
526					{
527					case 0:
528						break;
529					case ':':
530						if (command && (fp->op != OP_universe || !ok))
531						{
532							if (r = p - d - 1)
533							{
534								sfwrite(tmp, d, r);
535								sfputc(tmp, '/');
536								sfputr(tmp, command, 0);
537								if ((d = sfstruse(tmp)) && !eaccess(d, X_OK))
538								{
539									ok = 1;
540									if (fp->op != OP_universe)
541										break;
542								}
543							}
544							d = p;
545						}
546						r = 1;
547						continue;
548					case '/':
549						if (r)
550						{
551							r = 0;
552							if (fp->op == OP_universe)
553							{
554								if (p[0] == 'u' && p[1] == 's' && p[2] == 'r' && p[3] == '/')
555									for (p += 4; *p == '/'; p++);
556								if (p[0] == 'b' && p[1] == 'i' && p[2] == 'n')
557								{
558									for (p += 3; *p == '/'; p++);
559									if (!*p || *p == ':')
560										break;
561								}
562							}
563						}
564						if (fp->op == OP_universe)
565						{
566							if (strneq(p, "xpg", 3) || strneq(p, "5bin", 4))
567							{
568								ok = 1;
569								break;
570							}
571							if (strneq(p, "bsd", 3) || strneq(p, "ucb", 3))
572							{
573								ok = 0;
574								break;
575							}
576						}
577						continue;
578					default:
579						r = 0;
580						continue;
581					}
582					break;
583				}
584				sfclose(tmp);
585			}
586			else
587				ok = 1;
588		}
589		break;
590	}
591#if DEBUG_astconf
592	error(-1, "AHA#%d state.std=%d %s [%s] std=%s ast=%s value=%s ok=%d", __LINE__,  state.std, fp->name, ok ? succeed : fail, fp->std, fp->ast, fp->value, ok);
593#endif
594	synthesize(fp, path, ok ? succeed : fail);
595}
596
597/*
598 * format synthesized value
599 */
600
601static char*
602format(register Feature_t* fp, const char* path, const char* value, unsigned int flags, Error_f conferror)
603{
604	register Feature_t*	sp;
605	register int		n;
606
607#if DEBUG_astconf
608	error(-2, "astconf format name=%s path=%s value=%s flags=%04x fp=%p%s", fp->name, path, value, flags, fp, state.synthesizing ? " SYNTHESIZING" : "");
609#endif
610	if (value)
611		fp->flags &= ~CONF_GLOBAL;
612	else if (fp->flags & CONF_GLOBAL)
613		return fp->value;
614	switch (fp->op)
615	{
616
617	case OP_conformance:
618		if (value && STANDARD(value))
619			value = fp->std;
620		n = state.std = streq(fp->value, fp->std);
621#if DEBUG_astconf
622		error(-1, "AHA#%d state.std=%d %s [%s] std=%s ast=%s value=%s", __LINE__,  state.std, fp->name, value, fp->std, fp->ast, fp->value);
623#endif
624		if (!synthesize(fp, path, value))
625			initialize(fp, path, NiL, fp->std, fp->value);
626#if DEBUG_astconf
627		error(-1, "AHA#%d state.std=%d %s [%s] std=%s ast=%s value=%s", __LINE__,  state.std, fp->name, value, fp->std, fp->ast, fp->value);
628#endif
629		if (!n && STANDARD(fp->value))
630		{
631			state.std = 1;
632			for (sp = state.features; sp; sp = sp->next)
633				if (sp->std && sp->op && sp->op != OP_conformance)
634					astconf(sp->name, path, sp->std);
635		}
636#if DEBUG_astconf
637		error(-1, "AHA#%d state.std=%d %s [%s] std=%s ast=%s value=%s", __LINE__,  state.std, fp->name, value, fp->std, fp->ast, fp->value);
638#endif
639		break;
640
641	case OP_fs_3d:
642		fp->value = fs3d(value ? value[0] ? FS3D_ON : FS3D_OFF : FS3D_TEST) ? "1" : null;
643		break;
644
645	case OP_hosttype:
646		break;
647
648	case OP_path_attributes:
649#ifdef _PC_PATH_ATTRIBUTES
650		{
651			register char*	s;
652			register char*	e;
653			intmax_t	v;
654
655			/*
656			 * _PC_PATH_ATTRIBUTES is a bitmap for 'a' to 'z'
657			 */
658
659			if ((v = pathconf(path, _PC_PATH_ATTRIBUTES)) == -1L)
660				return 0;
661			s = fp->value;
662			e = s + sizeof(fp->value) - 1;
663			for (n = 'a'; n <= 'z'; n++)
664				if (v & (1 << (n - 'a')))
665				{
666					*s++ = n;
667					if (s >= e)
668						break;
669				}
670			*s = 0;
671		}
672#endif
673		break;
674
675	case OP_path_resolve:
676		if (!synthesize(fp, path, value))
677			initialize(fp, path, NiL, "logical", DEFAULT(OP_path_resolve));
678		break;
679
680	case OP_universe:
681#if _lib_universe
682		if (getuniverse(fp->value) < 0)
683			strcpy(fp->value, DEFAULT(OP_universe));
684		if (value)
685			setuniverse(value);
686#else
687#ifdef UNIV_MAX
688		n = 0;
689		if (value)
690		{
691			while (n < univ_max && !streq(value, univ_name[n])
692				n++;
693			if (n >= univ_max)
694			{
695				if (conferror)
696					(*conferror)(&state, &state, 2, "%s: %s: universe value too large", fp->name, value);
697				return 0;
698			}
699		}
700#ifdef ATT_UNIV
701		n = setuniverse(n + 1);
702		if (!value && n > 0)
703			setuniverse(n);
704#else
705		n = universe(value ? n + 1 : U_GET);
706#endif
707		if (n <= 0 || n >= univ_max)
708			n = 1;
709		strcpy(fp->value, univ_name[n - 1]);
710#else
711		if (value && streq(path, "="))
712		{
713			if (state.synthesizing)
714			{
715				if (!(fp->flags & CONF_ALLOC))
716					fp->value = 0;
717				n = strlen(value);
718				if (!(fp->value = newof(fp->value, char, n, 1)))
719					fp->value = null;
720				else
721				{
722					fp->flags |= CONF_ALLOC;
723					memcpy(fp->value, value, n);
724					fp->value[n] = 0;
725				}
726			}
727			else
728				synthesize(fp, path, value);
729		}
730		else
731			initialize(fp, path, "echo", DEFAULT(OP_universe), "ucb");
732#endif
733#endif
734		break;
735
736	default:
737		synthesize(fp, path, value);
738		break;
739
740	}
741	if (streq(path, "="))
742		fp->flags |= CONF_GLOBAL;
743	return fp->value;
744}
745
746/*
747 * value==0 get feature name
748 * value!=0 set feature name
749 * 0 returned if error or not defined; otherwise previous value
750 */
751
752static char*
753feature(const char* name, const char* path, const char* value, unsigned int flags, Error_f conferror)
754{
755	register Feature_t*	fp;
756	register int		n;
757
758	if (value && (streq(value, "-") || streq(value, "0")))
759		value = null;
760	for (fp = state.features; fp && !streq(fp->name, name); fp = fp->next);
761#if DEBUG_astconf
762	error(-2, "astconf feature name=%s path=%s value=%s flags=%04x fp=%p%s", name, path, value, flags, fp, state.synthesizing ? " SYNTHESIZING" : "");
763#endif
764	if (!fp)
765	{
766		if (!value)
767			return 0;
768		if (state.notify && !(*state.notify)(name, path, value))
769			return 0;
770		n = strlen(name);
771		if (!(fp = newof(0, Feature_t, 1, n + 1)))
772		{
773			if (conferror)
774				(*conferror)(&state, &state, 2, "%s: out of space", name);
775			return 0;
776		}
777		fp->op = -1;
778		fp->name = (const char*)fp + sizeof(Feature_t);
779		strcpy((char*)fp->name, name);
780		fp->length = n;
781		fp->std = &null[0];
782		fp->next = state.features;
783		state.features = fp;
784	}
785	else if (value)
786	{
787		if (fp->flags & CONF_READONLY)
788		{
789			if (conferror)
790				(*conferror)(&state, &state, 2, "%s: cannot set readonly symbol", fp->name);
791			return 0;
792		}
793		if (state.notify && !streq(fp->value, value) && !(*state.notify)(name, path, value))
794			return 0;
795	}
796	else
797		state.recent = fp;
798	return format(fp, path, value, flags, conferror);
799}
800
801/*
802 * binary search for name in conf[]
803 */
804
805static int
806lookup(register Lookup_t* look, const char* name, unsigned int flags)
807{
808	register Conf_t*	mid = (Conf_t*)conf;
809	register Conf_t*	lo = mid;
810	register Conf_t*	hi = mid + conf_elements;
811	register int		v;
812	register int		c;
813	char*			e;
814	const Prefix_t*		p;
815
816	static Conf_t		num;
817
818	look->flags = 0;
819	look->call = -1;
820	look->standard = (flags & ASTCONF_AST) ? CONF_AST : -1;
821	look->section = -1;
822	while (*name == '_')
823		name++;
824 again:
825	for (p = prefix; p < &prefix[prefix_elements]; p++)
826		if (strneq(name, p->name, p->length) && ((c = name[p->length] == '_' || name[p->length] == '(' || name[p->length] == '#') || (v = isdigit(name[p->length]) && name[p->length + 1] == '_')))
827		{
828			if (p->call < 0)
829			{
830				if (look->standard >= 0)
831					break;
832				look->standard = p->standard;
833			}
834			else
835			{
836				if (look->call >= 0)
837					break;
838				look->call = p->call;
839			}
840			if (name[p->length] == '(' || name[p->length] == '#')
841			{
842				look->conf = &num;
843				strncpy((char*)num.name, name, sizeof(num.name));
844				num.call = p->call;
845				num.flags = *name == 'C' ? CONF_STRING : 0;
846				num.op = (short)strtol(name + p->length + 1, &e, 10);
847				if (name[p->length] == '(' && *e == ')')
848					e++;
849				if (*e)
850					break;
851				return 1;
852			}
853			name += p->length + c;
854			if (look->section < 0 && !c && v)
855			{
856				look->section = name[0] - '0';
857				name += 2;
858			}
859			goto again;
860		}
861#if HUH_2006_02_10
862	if (look->section < 0)
863		look->section = 1;
864#endif
865	look->name = name;
866#if DEBUG_astconf
867	error(-2, "astconf normal name=%s standard=%d section=%d call=%d flags=%04x elements=%d", look->name, look->standard, look->section, look->call, flags, conf_elements);
868#endif
869	c = *((unsigned char*)name);
870	while (lo <= hi)
871	{
872		mid = lo + (hi - lo) / 2;
873#if DEBUG_astconf
874		error(-3, "astconf lookup name=%s mid=%s", name, mid->name);
875#endif
876		if (!(v = c - *((unsigned char*)mid->name)) && !(v = strcmp(name, mid->name)))
877		{
878			hi = mid;
879			lo = (Conf_t*)conf;
880			do
881			{
882				if ((look->standard < 0 || look->standard == mid->standard) &&
883				    (look->section < 0 || look->section == mid->section) &&
884				    (look->call < 0 || look->call == mid->call))
885					goto found;
886			} while (mid-- > lo && streq(mid->name, look->name));
887			mid = hi;
888			hi = lo + conf_elements - 1;
889			while (++mid < hi && streq(mid->name, look->name))
890			{
891				if ((look->standard < 0 || look->standard == mid->standard) &&
892				    (look->section < 0 || look->section == mid->section) &&
893				    (look->call < 0 || look->call == mid->call))
894					goto found;
895			}
896			break;
897		}
898		else if (v > 0)
899			lo = mid + 1;
900		else
901			hi = mid - 1;
902	}
903	return 0;
904 found:
905	if (look->call < 0 && look->standard >= 0 && (look->section <= 1 || (mid->flags & CONF_MINMAX)))
906		look->flags |= CONF_MINMAX;
907	look->conf = mid;
908#if DEBUG_astconf
909	error(-2, "astconf lookup name=%s standard=%d:%d section=%d:%d call=%d:%d", look->name, look->standard, mid->standard, look->section, mid->section, look->call, mid->call);
910#endif
911	return 1;
912}
913
914/*
915 * return a tolower'd copy of s
916 */
917
918static char*
919fmtlower(register const char* s)
920{
921	register int	c;
922	register char*	t;
923	char*		b;
924
925	b = t = fmtbuf(strlen(s) + 1);
926	while (c = *s++)
927	{
928		if (isupper(c))
929			c = tolower(c);
930		*t++ = c;
931	}
932	*t = 0;
933	return b;
934}
935
936/*
937 * print value line for p
938 * if !name then value prefixed by "p->name="
939 * if (flags & CONF_MINMAX) then default minmax value used
940 */
941
942static char*
943print(Sfio_t* sp, register Lookup_t* look, const char* name, const char* path, int listflags, Error_f conferror)
944{
945	register Conf_t*	p = look->conf;
946	register unsigned int	flags = look->flags;
947	char*			call;
948	char*			f;
949	const char*		s;
950	int			i;
951	int			n;
952	int			olderrno;
953	int			drop;
954	int			defined;
955	intmax_t		v;
956	char			buf[PATH_MAX];
957	char			flg[16];
958
959	if (!name && !(p->flags & CONF_STRING) && (p->flags & (CONF_FEATURE|CONF_LIMIT|CONF_MINMAX)) && (p->flags & (CONF_LIMIT|CONF_PREFIXED)) != CONF_LIMIT)
960		flags |= CONF_PREFIXED;
961	olderrno = errno;
962	errno = 0;
963#if DEBUG_astconf
964	error(-1, "astconf name=%s:%s:%s standard=%d section=%d call=%s op=%d flags=|%s%s%s%s%s:|%s%s%s%s%s%s%s%s%s%s"
965		, name, look->name, p->name, p->standard, p->section, prefix[p->call + CONF_call].name, p->op
966		, (flags & CONF_FEATURE) ? "FEATURE|" : ""
967		, (flags & CONF_LIMIT) ? "LIMIT|" : ""
968		, (flags & CONF_MINMAX) ? "MINMAX|" : ""
969		, (flags & CONF_PREFIXED) ? "PREFIXED|" : ""
970		, (flags & CONF_STRING) ? "STRING|" : ""
971		, (p->flags & CONF_DEFER_CALL) ? "DEFER_CALL|" : ""
972		, (p->flags & CONF_DEFER_MM) ? "DEFER_MM|" : ""
973		, (p->flags & CONF_FEATURE) ? "FEATURE|" : ""
974		, (p->flags & CONF_LIMIT_DEF) ? "LIMIT_DEF|" : (p->flags & CONF_LIMIT) ? "LIMIT|" : ""
975		, (p->flags & CONF_MINMAX_DEF) ? "MINMAX_DEF|" : (p->flags & CONF_MINMAX) ? "MINMAX|" : ""
976		, (p->flags & CONF_NOUNDERSCORE) ? "NOUNDERSCORE|" : ""
977		, (p->flags & CONF_PREFIXED) ? "PREFIXED|" : ""
978		, (p->flags & CONF_PREFIX_ONLY) ? "PREFIX_ONLY|" : ""
979		, (p->flags & CONF_STANDARD) ? "STANDARD|" : ""
980		, (p->flags & CONF_STRING) ? "STRING|" : ""
981		, (p->flags & CONF_UNDERSCORE) ? "UNDERSCORE|" : ""
982		);
983#endif
984	flags |= CONF_LIMIT_DEF|CONF_MINMAX_DEF;
985	if (conferror && name)
986	{
987		if ((p->flags & CONF_PREFIX_ONLY) && look->standard < 0)
988			goto bad;
989		if (!(flags & CONF_MINMAX) || !(p->flags & CONF_MINMAX))
990		{
991			switch (p->call)
992			{
993			case CONF_pathconf:
994				if (path == root)
995				{
996					(*conferror)(&state, &state, 2, "%s: path expected", name);
997					goto bad;
998				}
999				break;
1000			default:
1001				if (path != root)
1002				{
1003					(*conferror)(&state, &state, 2, "%s: path not expected", name);
1004					goto bad;
1005				}
1006				break;
1007			}
1008#ifdef _pth_getconf
1009			if (p->flags & CONF_DEFER_CALL)
1010				goto bad;
1011#endif
1012		}
1013		else
1014		{
1015			if (path != root)
1016			{
1017				(*conferror)(&state, &state, 2, "%s: path not expected", name);
1018				goto bad;
1019			}
1020#ifdef _pth_getconf
1021			if ((p->flags & CONF_DEFER_MM) || !(p->flags & CONF_MINMAX_DEF))
1022				goto bad;
1023#endif
1024		}
1025		if (look->standard >= 0 && (name[0] != '_' && ((p->flags & CONF_UNDERSCORE) || look->section <= 1) || name[0] == '_' && (p->flags & CONF_NOUNDERSCORE)) || look->standard < 0 && name[0] == '_')
1026			goto bad;
1027	}
1028	s = 0;
1029	defined = 1;
1030	switch (i = (p->op < 0 || (flags & CONF_MINMAX) && (p->flags & CONF_MINMAX_DEF)) ? 0 : p->call)
1031	{
1032	case CONF_confstr:
1033		call = "confstr";
1034#if _lib_confstr
1035		if (!(v = confstr(p->op, buf, sizeof(buf))))
1036		{
1037			defined = 0;
1038			v = -1;
1039			errno = EINVAL;
1040		}
1041		else if (v > 0)
1042		{
1043			buf[sizeof(buf) - 1] = 0;
1044			s = (const char*)buf;
1045		}
1046		else
1047			defined = 0;
1048		break;
1049#else
1050		goto predef;
1051#endif
1052	case CONF_pathconf:
1053		call = "pathconf";
1054#if _lib_pathconf
1055		if ((v = pathconf(path, p->op)) < 0)
1056			defined = 0;
1057		break;
1058#else
1059		goto predef;
1060#endif
1061	case CONF_sysconf:
1062		call = "sysconf";
1063#if _lib_sysconf
1064		if ((v = sysconf(p->op)) < 0)
1065			defined = 0;
1066		break;
1067#else
1068		goto predef;
1069#endif
1070	case CONF_sysinfo:
1071		call = "sysinfo";
1072#if _lib_sysinfo
1073		if ((v = sysinfo(p->op, buf, sizeof(buf))) >= 0)
1074		{
1075			buf[sizeof(buf) - 1] = 0;
1076			s = (const char*)buf;
1077		}
1078		else
1079			defined = 0;
1080		break;
1081#else
1082		goto predef;
1083#endif
1084	default:
1085		call = "synthesis";
1086		errno = EINVAL;
1087		v = -1;
1088		defined = 0;
1089		break;
1090	case 0:
1091		call = 0;
1092		if (p->standard == CONF_AST)
1093		{
1094			if (streq(p->name, "RELEASE") && (i = open("/proc/version", O_RDONLY)) >= 0)
1095			{
1096				n = read(i, buf, sizeof(buf) - 1);
1097				close(i);
1098				if (n > 0 && buf[n - 1] == '\n')
1099					n--;
1100				if (n > 0 && buf[n - 1] == '\r')
1101					n--;
1102				buf[n] = 0;
1103				if (buf[0])
1104				{
1105					v = 0;
1106					s = buf;
1107					break;
1108				}
1109			}
1110		}
1111		if (p->flags & CONF_MINMAX_DEF)
1112		{
1113			if (!((p->flags & CONF_LIMIT_DEF)))
1114				flags |= CONF_MINMAX;
1115			listflags &= ~ASTCONF_system;
1116		}
1117	predef:
1118		if (look->standard == CONF_AST)
1119		{
1120			if (streq(p->name, "VERSION"))
1121			{
1122				v = ast.version;
1123				break;
1124			}
1125		}
1126		if (flags & CONF_MINMAX)
1127		{
1128			if ((p->flags & CONF_MINMAX_DEF) && (!(listflags & ASTCONF_system) || !(p->flags & CONF_DEFER_MM)))
1129			{
1130				v = p->minmax.number;
1131				s = p->minmax.string;
1132				break;
1133			}
1134		}
1135		else if ((p->flags & CONF_LIMIT_DEF) && (!(listflags & ASTCONF_system) || !(p->flags & CONF_DEFER_CALL)))
1136		{
1137			v = p->limit.number;
1138			s = p->limit.string;
1139			break;
1140		}
1141		flags &= ~(CONF_LIMIT_DEF|CONF_MINMAX_DEF);
1142		v = -1;
1143		errno = EINVAL;
1144		defined = 0;
1145		break;
1146	}
1147	if (!defined)
1148	{
1149		if (!errno)
1150		{
1151			if ((p->flags & CONF_FEATURE) || !(p->flags & (CONF_LIMIT|CONF_MINMAX)))
1152				flags &= ~(CONF_LIMIT_DEF|CONF_MINMAX_DEF);
1153		}
1154		else if (flags & CONF_PREFIXED)
1155			flags &= ~(CONF_LIMIT_DEF|CONF_MINMAX_DEF);
1156		else if (errno != EINVAL || !i)
1157		{
1158			if (!sp)
1159			{
1160				if (conferror)
1161				{
1162					if (call)
1163						(*conferror)(&state, &state, ERROR_SYSTEM|2, "%s: %s error", p->name, call);
1164					else if (!(listflags & ASTCONF_system))
1165						(*conferror)(&state, &state, 2, "%s: unknown name", p->name);
1166				}
1167				goto bad;
1168			}
1169			else
1170			{
1171				flags &= ~(CONF_LIMIT_DEF|CONF_MINMAX_DEF);
1172				flags |= CONF_ERROR;
1173			}
1174		}
1175	}
1176	errno = olderrno;
1177	if ((listflags & ASTCONF_defined) && !(flags & (CONF_LIMIT_DEF|CONF_MINMAX_DEF)))
1178		goto bad;
1179	if ((drop = !sp) && !(sp = sfstropen()))
1180		goto bad;
1181	if (listflags & ASTCONF_table)
1182	{
1183		f = flg;
1184		if (p->flags & CONF_DEFER_CALL)
1185			*f++ = 'C';
1186		if (p->flags & CONF_DEFER_MM)
1187			*f++ = 'D';
1188		if (p->flags & CONF_FEATURE)
1189			*f++ = 'F';
1190		if (p->flags & CONF_LIMIT)
1191			*f++ = 'L';
1192		if (p->flags & CONF_MINMAX)
1193			*f++ = 'M';
1194		if (p->flags & CONF_NOSECTION)
1195			*f++ = 'N';
1196		if (p->flags & CONF_PREFIXED)
1197			*f++ = 'P';
1198		if (p->flags & CONF_STANDARD)
1199			*f++ = 'S';
1200		if (p->flags & CONF_UNDERSCORE)
1201			*f++ = 'U';
1202		if (p->flags & CONF_NOUNDERSCORE)
1203			*f++ = 'V';
1204		if (p->flags & CONF_PREFIX_ONLY)
1205			*f++ = 'W';
1206		if (f == flg)
1207			*f++ = 'X';
1208		*f = 0;
1209		sfprintf(sp, "%*s %*s %d %2s %4d %6s ", sizeof(p->name), p->name, sizeof(prefix[p->standard].name), prefix[p->standard].name, p->section, prefix[p->call + CONF_call].name, p->op, flg);
1210		if (p->flags & CONF_LIMIT_DEF)
1211		{
1212			if (p->limit.string)
1213				sfprintf(sp, "L[%s] ", (listflags & ASTCONF_quote) ? fmtquote(p->limit.string, "\"", "\"", strlen(p->limit.string), FMT_SHELL) : p->limit.string);
1214			else
1215				sfprintf(sp, "L[%I*d] ", sizeof(p->limit.number), p->limit.number);
1216		}
1217		if (p->flags & CONF_MINMAX_DEF)
1218		{
1219			if (p->minmax.string)
1220				sfprintf(sp, "M[%s] ", (listflags & ASTCONF_quote) ? fmtquote(p->minmax.string, "\"", "\"", strlen(p->minmax.string), FMT_SHELL) : p->minmax.string);
1221			else
1222				sfprintf(sp, "M[%I*d] ", sizeof(p->minmax.number), p->minmax.number);
1223		}
1224		if (flags & CONF_ERROR)
1225			sfprintf(sp, "error");
1226		else if (defined)
1227		{
1228			if (s)
1229				sfprintf(sp, "%s", (listflags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL) : s);
1230			else if (v != -1)
1231				sfprintf(sp, "%I*d", sizeof(v), v);
1232			else
1233				sfprintf(sp, "%I*u", sizeof(v), v);
1234		}
1235		sfprintf(sp, "\n");
1236	}
1237	else
1238	{
1239		if (!(flags & CONF_PREFIXED) || (listflags & ASTCONF_base))
1240		{
1241			if (!name)
1242			{
1243				if ((p->flags & (CONF_PREFIXED|CONF_STRING)) == (CONF_PREFIXED|CONF_STRING) && (!(listflags & ASTCONF_base) || p->standard != CONF_POSIX))
1244				{
1245					if ((p->flags & CONF_UNDERSCORE) && !(listflags & ASTCONF_base))
1246						sfprintf(sp, "_");
1247					sfprintf(sp, "%s", (listflags & ASTCONF_lower) ? fmtlower(prefix[p->standard].name) : prefix[p->standard].name);
1248					if (p->section > 1)
1249						sfprintf(sp, "%d", p->section);
1250					sfprintf(sp, "_");
1251				}
1252				sfprintf(sp, "%s=", (listflags & ASTCONF_lower) ? fmtlower(p->name) : p->name);
1253			}
1254			if (flags & CONF_ERROR)
1255				sfprintf(sp, "error");
1256			else if (defined)
1257			{
1258				if (s)
1259					sfprintf(sp, "%s", (listflags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL) : s);
1260				else if (v != -1)
1261					sfprintf(sp, "%I*d", sizeof(v), v);
1262				else
1263					sfprintf(sp, "%I*u", sizeof(v), v);
1264			}
1265			else
1266				sfprintf(sp, "undefined");
1267			if (!name)
1268				sfprintf(sp, "\n");
1269		}
1270		if (!name && !(listflags & ASTCONF_base) && !(p->flags & CONF_STRING) && (p->flags & (CONF_FEATURE|CONF_MINMAX)))
1271		{
1272			if (p->flags & CONF_UNDERSCORE)
1273				sfprintf(sp, "_");
1274			sfprintf(sp, "%s", (listflags & ASTCONF_lower) ? fmtlower(prefix[p->standard].name) : prefix[p->standard].name);
1275			if (p->section > 1)
1276				sfprintf(sp, "%d", p->section);
1277			sfprintf(sp, "_%s=", (listflags & ASTCONF_lower) ? fmtlower(p->name) : p->name);
1278			if (v != -1)
1279				sfprintf(sp, "%I*d", sizeof(v), v);
1280			else if (defined)
1281				sfprintf(sp, "%I*u", sizeof(v), v);
1282			else
1283				sfprintf(sp, "undefined");
1284			sfprintf(sp, "\n");
1285		}
1286	}
1287	if (drop)
1288	{
1289		if (call = sfstruse(sp))
1290			call = buffer(call);
1291		else
1292			call = "[ out of space ]";
1293		sfclose(sp);
1294		return call;
1295	}
1296 bad:
1297	return (listflags & ASTCONF_error) ? (char*)0 : null;
1298}
1299
1300/*
1301 * return read stream to native getconf utility
1302 */
1303
1304static Sfio_t*
1305nativeconf(Proc_t** pp, const char* operand)
1306{
1307#ifdef _pth_getconf
1308	Sfio_t*		sp;
1309	char*		cmd[3];
1310	long		ops[2];
1311
1312#if DEBUG_astconf
1313	error(-2, "astconf defer %s %s", _pth_getconf, operand);
1314#endif
1315	cmd[0] = (char*)state.id;
1316	cmd[1] = (char*)operand;
1317	cmd[2] = 0;
1318	ops[0] = PROC_FD_DUP(open("/dev/null",O_WRONLY,0), 2, PROC_FD_CHILD);
1319	ops[1] = 0;
1320	if (*pp = procopen(_pth_getconf, cmd, environ, ops, PROC_READ))
1321	{
1322		if (sp = sfnew(NiL, NiL, SF_UNBOUND, (*pp)->rfd, SF_READ))
1323		{
1324			sfdisc(sp, SF_POPDISC);
1325			return sp;
1326		}
1327		procclose(*pp);
1328	}
1329#endif
1330	return 0;
1331}
1332
1333/*
1334 * value==0 gets value for name
1335 * value!=0 sets value for name and returns previous value
1336 * path==0 implies path=="/"
1337 *
1338 * settable return values are in permanent store
1339 * non-settable return values copied to a tmp fmtbuf() buffer
1340 *
1341 *	if (streq(astgetconf("PATH_RESOLVE", NiL, NiL, 0, 0), "logical"))
1342 *		our_way();
1343 *
1344 *	universe = astgetconf("UNIVERSE", NiL, "att", 0, 0);
1345 *	astgetconf("UNIVERSE", NiL, universe, 0, 0);
1346 *
1347 * if (flags&ASTCONF_error)!=0 then error return value is 0
1348 * otherwise 0 not returned
1349 */
1350
1351#define ALT	16
1352
1353char*
1354astgetconf(const char* name, const char* path, const char* value, int flags, Error_f conferror)
1355{
1356	register char*	s;
1357	int		n;
1358	Lookup_t	look;
1359	Sfio_t*		tmp;
1360
1361#if __OBSOLETE__ < 20080101
1362	if (pointerof(flags) == (void*)errorf)
1363	{
1364		conferror = errorf;
1365		flags = ASTCONF_error;
1366	}
1367	else if (conferror && conferror != errorf)
1368		conferror = 0;
1369#endif
1370	if (!name)
1371	{
1372		if (path)
1373			return null;
1374		if (!(name = value))
1375		{
1376			if (state.data)
1377			{
1378				Ast_confdisc_f	notify;
1379
1380#if _HUH20000515 /* doesn't work for shell builtins */
1381				free(state.data - state.prefix);
1382#endif
1383				state.data = 0;
1384				notify = state.notify;
1385				state.notify = 0;
1386				INITIALIZE();
1387				state.notify = notify;
1388			}
1389			return null;
1390		}
1391		value = 0;
1392	}
1393	INITIALIZE();
1394	if (!path)
1395		path = root;
1396	if (state.recent && streq(name, state.recent->name) && (s = format(state.recent, path, value, flags, conferror)))
1397		return s;
1398	if (lookup(&look, name, flags))
1399	{
1400		if (value)
1401		{
1402		ro:
1403			errno = EINVAL;
1404			if (conferror)
1405				(*conferror)(&state, &state, 2, "%s: cannot set value", name);
1406			return (flags & ASTCONF_error) ? (char*)0 : null;
1407		}
1408		return print(NiL, &look, name, path, flags, conferror);
1409	}
1410	if ((n = strlen(name)) > 3 && n < (ALT + 3))
1411	{
1412		if (streq(name + n - 3, "DEV"))
1413		{
1414			if (tmp = sfstropen())
1415			{
1416				sfprintf(tmp, "/dev/");
1417				for (s = (char*)name; s < (char*)name + n - 3; s++)
1418					sfputc(tmp, isupper(*s) ? tolower(*s) : *s);
1419				if ((s = sfstruse(tmp)) && !access(s, F_OK))
1420				{
1421					if (value)
1422						goto ro;
1423					s = buffer(s);
1424					sfclose(tmp);
1425					return s;
1426				}
1427				sfclose(tmp);
1428			}
1429		}
1430		else if (streq(name + n - 3, "DIR"))
1431		{
1432			Lookup_t		altlook;
1433			char			altname[ALT];
1434
1435			static const char*	dirs[] = { "/usr/lib", "/usr", null };
1436
1437			strcpy(altname, name);
1438			altname[n - 3] = 0;
1439			if (lookup(&altlook, altname, flags))
1440			{
1441				if (value)
1442				{
1443					errno = EINVAL;
1444					if (conferror)
1445						(*conferror)(&state, &state, 2, "%s: cannot set value", altname);
1446					return (flags & ASTCONF_error) ? (char*)0 : null;
1447				}
1448				return print(NiL, &altlook, altname, path, flags, conferror);
1449			}
1450			for (s = altname; *s; s++)
1451				if (isupper(*s))
1452					*s = tolower(*s);
1453			if (tmp = sfstropen())
1454			{
1455				for (n = 0; n < elementsof(dirs); n++)
1456				{
1457					sfprintf(tmp, "%s/%s/.", dirs[n], altname);
1458					if ((s = sfstruse(tmp)) && !access(s, F_OK))
1459					{
1460						if (value)
1461							goto ro;
1462						s = buffer(s);
1463						sfclose(tmp);
1464						return s;
1465					}
1466				}
1467				sfclose(tmp);
1468			}
1469		}
1470	}
1471	if ((look.standard < 0 || look.standard == CONF_AST) && look.call <= 0 && look.section <= 1 && (s = feature(look.name, path, value, flags, conferror)))
1472		return s;
1473	errno = EINVAL;
1474	if (conferror && !(flags & ASTCONF_system))
1475		(*conferror)(&state, &state, 2, "%s: unknown name", name);
1476	return (flags & ASTCONF_error) ? (char*)0 : null;
1477}
1478
1479/*
1480 * astconf() never returns 0
1481 */
1482
1483char*
1484astconf(const char* name, const char* path, const char* value)
1485{
1486	return astgetconf(name, path, value, 0, 0);
1487}
1488
1489/*
1490 * set discipline function to be called when features change
1491 * old discipline function returned
1492 */
1493
1494Ast_confdisc_f
1495astconfdisc(Ast_confdisc_f new_notify)
1496{
1497	Ast_confdisc_f	old_notify;
1498
1499	INITIALIZE();
1500	old_notify = state.notify;
1501	state.notify = new_notify;
1502	return old_notify;
1503}
1504
1505/*
1506 * list all name=value entries on sp
1507 * path==0 implies path=="/"
1508 */
1509
1510void
1511astconflist(Sfio_t* sp, const char* path, int flags, const char* pattern)
1512{
1513	char*		s;
1514	char*		f;
1515	char*		call;
1516	Feature_t*	fp;
1517	Lookup_t	look;
1518	regex_t		re;
1519	regdisc_t	redisc;
1520	int		olderrno;
1521	char		flg[8];
1522#ifdef _pth_getconf_a
1523	Proc_t*		proc;
1524	Sfio_t*		pp;
1525#endif
1526
1527	INITIALIZE();
1528	if (!path)
1529		path = root;
1530	else if (access(path, F_OK))
1531	{
1532		errorf(&state, &state, 2, "%s: not found", path);
1533		return;
1534	}
1535	olderrno = errno;
1536	look.flags = 0;
1537	if (!(flags & (ASTCONF_read|ASTCONF_write|ASTCONF_parse)))
1538		flags |= ASTCONF_read|ASTCONF_write;
1539	else if (flags & ASTCONF_parse)
1540		flags |= ASTCONF_write;
1541	if (!(flags & (ASTCONF_matchcall|ASTCONF_matchname|ASTCONF_matchstandard)))
1542		pattern = 0;
1543	if (pattern)
1544	{
1545		memset(&redisc, 0, sizeof(redisc));
1546		redisc.re_version = REG_VERSION;
1547		redisc.re_errorf = (regerror_t)errorf;
1548		re.re_disc = &redisc;
1549		if (regcomp(&re, pattern, REG_DISCIPLINE|REG_EXTENDED|REG_LENIENT|REG_NULL))
1550			return;
1551	}
1552	if (flags & ASTCONF_read)
1553	{
1554		for (look.conf = (Conf_t*)conf; look.conf < (Conf_t*)&conf[conf_elements]; look.conf++)
1555		{
1556			if (pattern)
1557			{
1558				if (flags & ASTCONF_matchcall)
1559				{
1560					if (regexec(&re, prefix[look.conf->call + CONF_call].name, 0, NiL, 0))
1561						continue;
1562				}
1563				else if (flags & ASTCONF_matchname)
1564				{
1565					if (regexec(&re, look.conf->name, 0, NiL, 0))
1566						continue;
1567				}
1568				else if (flags & ASTCONF_matchstandard)
1569				{
1570					if (regexec(&re, prefix[look.conf->standard].name, 0, NiL, 0))
1571						continue;
1572				}
1573			}
1574			print(sp, &look, NiL, path, flags, errorf);
1575		}
1576#ifdef _pth_getconf_a
1577		if (pp = nativeconf(&proc, _pth_getconf_a))
1578		{
1579			call = "GC";
1580			while (f = sfgetr(pp, '\n', 1))
1581			{
1582				for (s = f; *s && *s != '=' && *s != ':' && !isspace(*s); s++);
1583				if (*s)
1584					for (*s++ = 0; isspace(*s); s++);
1585				if (!lookup(&look, f, flags))
1586				{
1587					if (flags & ASTCONF_table)
1588					{
1589						if (look.standard < 0)
1590							look.standard = 0;
1591						if (look.section < 1)
1592							look.section = 1;
1593						sfprintf(sp, "%*s %*s %d %2s %4d %5s %s\n", sizeof(conf[0].name), f, sizeof(prefix[look.standard].name), prefix[look.standard].name, look.section, call, 0, "N", s);
1594					}
1595					else if (flags & ASTCONF_parse)
1596						sfprintf(sp, "%s %s - %s\n", state.id, f, s);
1597					else
1598						sfprintf(sp, "%s=%s\n", f, (flags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL) : s);
1599				}
1600			}
1601			sfclose(pp);
1602			procclose(proc);
1603		}
1604#endif
1605	}
1606	if (flags & ASTCONF_write)
1607	{
1608		call = "AC";
1609		for (fp = state.features; fp; fp = fp->next)
1610		{
1611			if (pattern)
1612			{
1613				if (flags & ASTCONF_matchcall)
1614				{
1615					if (regexec(&re, call, 0, NiL, 0))
1616						continue;
1617				}
1618				else if (flags & ASTCONF_matchname)
1619				{
1620					if (regexec(&re, fp->name, 0, NiL, 0))
1621						continue;
1622				}
1623				else if (flags & ASTCONF_matchstandard)
1624				{
1625					if (regexec(&re, prefix[fp->standard].name, 0, NiL, 0))
1626						continue;
1627				}
1628			}
1629			if (!(s = feature(fp->name, path, NiL, 0, 0)) || !*s)
1630				s = "0";
1631			if (flags & ASTCONF_table)
1632			{
1633				f = flg;
1634				if (fp->flags & CONF_ALLOC)
1635					*f++ = 'A';
1636				if (fp->flags & CONF_READONLY)
1637					*f++ = 'R';
1638				if (f == flg)
1639					*f++ = 'X';
1640				*f = 0;
1641				sfprintf(sp, "%*s %*s %d %2s %4d %5s %s\n", sizeof(conf[0].name), fp->name, sizeof(prefix[fp->standard].name), prefix[fp->standard].name, 1, call, 0, flg, s);
1642			}
1643			else if (flags & ASTCONF_parse)
1644				sfprintf(sp, "%s %s - %s\n", state.id, (flags & ASTCONF_lower) ? fmtlower(fp->name) : fp->name, fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL));
1645			else
1646				sfprintf(sp, "%s=%s\n", (flags & ASTCONF_lower) ? fmtlower(fp->name) : fp->name, (flags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL) : s);
1647		}
1648	}
1649	if (pattern)
1650		regfree(&re);
1651	errno = olderrno;
1652}
1653