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