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