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