Home | History | Annotate | Download | only in port
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1985-2009 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 (at) research.att.com>                  *
     18 *                  David Korn <dgk (at) research.att.com>                   *
     19 *                   Phong Vo <kpv (at) 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*
    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*
    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
    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*
    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