xref: /titanic_51/usr/src/contrib/ast/src/lib/libast/port/astconf.c (revision b4dd7d09880f14016feece03929a224eca1cf39a)
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 
29 static 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 
93 static char		null[1];
94 static char		root[2] = "/";
95 
96 typedef 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 
109 typedef 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 
119 static 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 
278 typedef 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 
303 static State_t	state = { "getconf", "_AST_FEATURES", "CONFORMANCE = standard", "POSIXLY_CORRECT", dynamic, -1 };
304 
305 static char*	feature(Feature_t*, const char*, const char*, const char*, unsigned int, Error_f);
306 
307 /*
308  * return fmtbuf() copy of s
309  */
310 
311 static char*
312 buffer(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 
324 static char*
325 synthesize(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 
501 static void
502 initialize(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))
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 
620 static char*
621 format(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 
811 static char*
812 feature(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 
864 static int
865 lookup(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 
977 static char*
978 fmtlower(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 
1001 static char*
1002 print(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 
1373 static Sfio_t*
1374 nativeconf(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 
1422 char*
1423 astgetconf(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 
1552 char*
1553 astconf(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 
1563 Ast_confdisc_f
1564 astconfdisc(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 
1579 void
1580 astconflist(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