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