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