mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
This commit applies various accumulated bugfixes: - Applied some fixes for compiler warnings based off of the following pull requests (with whitespace changes excluded to avoid inflating the size of the diff): https://github.com/att/ast/pull/281 https://github.com/att/ast/pull/283 https://github.com/att/ast/pull/303 https://github.com/att/ast/pull/304 - clone_type(): Two separate variables in this function share the same name. A bugfix from ksh93v- 2013-05-24 was backported to avoid conflict issues. - Backported a minor error message improvement from ksh2020 for when the user attempts to run too many coprocesses. - Backported a ksh2020 bugfix for a file descriptor leak:58bc8b56- Backported ksh2020 bugfixes for unused variable and pointless assignment lint warnings:47650fe0df209c0d5e417b00- Applied a few minor improvements to libast from graphviz:e7c03541969a7cde- dtmemory(): Mark unused parameters with NOT_USED(). Based on:6ac3ad99- Applied a few documentation/comment tweaks for the NEWS file, printf -v and spawnveg. - Added a missing regression test for using the rm builtin's -f option without additional arguments (this was fixed in ksh93u+ 2012-02-14).
1713 lines
39 KiB
C
1713 lines
39 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1985-2012 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2022 Contributors to ksh 93u+m *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* Glenn Fowler <gsf@research.att.com> *
|
|
* David Korn <dgk@research.att.com> *
|
|
* Phong Vo <kpv@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
|
|
/*
|
|
* string interface to confstr(),pathconf(),sysconf(),sysinfo()
|
|
* extended to allow some features to be set per-process
|
|
*/
|
|
|
|
static const char id[] = "\n@(#)$Id: getconf (AT&T Research) 2012-05-01 $\0\n";
|
|
|
|
#include "univlib.h"
|
|
|
|
#include <ast.h>
|
|
#include <error.h>
|
|
#include <ctype.h>
|
|
#include <regex.h>
|
|
#include <proc.h>
|
|
#include <ls.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#include "conftab.h"
|
|
#include "FEATURE/libpath"
|
|
|
|
#ifndef DEBUG_astconf
|
|
#define DEBUG_astconf 0
|
|
#endif
|
|
|
|
#ifndef _pth_getconf
|
|
#undef ASTCONF_system
|
|
#define ASTCONF_system 0
|
|
#endif
|
|
|
|
#if _sys_systeminfo
|
|
# if !_lib_sysinfo
|
|
# if _lib_systeminfo
|
|
# define _lib_sysinfo 1
|
|
# define sysinfo(a,b,c) systeminfo(a,b,c)
|
|
# else
|
|
# if _lib_syscall && _sys_syscall
|
|
# include <sys/syscall.h>
|
|
# if defined(SYS_systeminfo)
|
|
# define _lib_sysinfo 1
|
|
# define sysinfo(a,b,c) syscall(SYS_systeminfo,a,b,c)
|
|
# endif
|
|
# endif
|
|
# endif
|
|
# endif
|
|
#else
|
|
# undef _lib_sysinfo
|
|
#endif
|
|
|
|
#define CONF_ERROR (CONF_USER<<0)
|
|
#define CONF_READONLY (CONF_USER<<1)
|
|
#define CONF_ALLOC (CONF_USER<<2)
|
|
#define CONF_GLOBAL (CONF_USER<<3)
|
|
|
|
#define DEFAULT(o) ((state.std||!dynamic[o].ast)?dynamic[o].std:dynamic[o].ast)
|
|
#define INITIALIZE() do{if(!state.data)synthesize(NiL,NiL,NiL);}while(0)
|
|
#define STANDARD(v) (streq(v,"standard")||streq(v,"strict")||streq(v,"posix")||streq(v,"xopen"))
|
|
|
|
#define MAXVAL 256
|
|
|
|
#if MAXVAL <= UNIV_SIZE
|
|
#undef MAXVAL
|
|
#define MAXVAL (UNIV_SIZE+1)
|
|
#endif
|
|
|
|
#ifndef _UNIV_DEFAULT
|
|
#define _UNIV_DEFAULT "att"
|
|
#endif
|
|
|
|
static char null[1];
|
|
static char root[2] = "/";
|
|
|
|
typedef struct Feature_s
|
|
{
|
|
struct Feature_s*next;
|
|
const char* name;
|
|
char* value;
|
|
char* std;
|
|
char* ast;
|
|
short length;
|
|
short standard;
|
|
unsigned int flags;
|
|
short op;
|
|
} Feature_t;
|
|
|
|
typedef struct Lookup_s
|
|
{
|
|
Conf_t* conf;
|
|
const char* name;
|
|
unsigned int flags;
|
|
short call;
|
|
short standard;
|
|
short section;
|
|
} Lookup_t;
|
|
|
|
static Feature_t dynamic[] =
|
|
{
|
|
#define OP_architecture 0
|
|
{
|
|
&dynamic[OP_architecture+1],
|
|
"ARCHITECTURE",
|
|
&null[0],
|
|
0,
|
|
0,
|
|
12,
|
|
CONF_AST,
|
|
0,
|
|
OP_architecture
|
|
},
|
|
#define OP_conformance 1
|
|
{
|
|
&dynamic[OP_conformance+1],
|
|
"CONFORMANCE",
|
|
"ast",
|
|
"standard",
|
|
"ast",
|
|
11,
|
|
CONF_AST,
|
|
0,
|
|
OP_conformance
|
|
},
|
|
#define OP_getconf 2
|
|
{
|
|
&dynamic[OP_getconf+1],
|
|
"GETCONF",
|
|
#ifdef _pth_getconf
|
|
_pth_getconf,
|
|
#else
|
|
&null[0],
|
|
#endif
|
|
0,
|
|
0,
|
|
7,
|
|
CONF_AST,
|
|
CONF_READONLY,
|
|
OP_getconf
|
|
},
|
|
#define OP_hosttype 3
|
|
{
|
|
&dynamic[OP_hosttype+1],
|
|
"HOSTTYPE",
|
|
HOSTTYPE,
|
|
0,
|
|
0,
|
|
8,
|
|
CONF_AST,
|
|
CONF_READONLY,
|
|
OP_hosttype
|
|
},
|
|
#define OP_libpath 4
|
|
{
|
|
&dynamic[OP_libpath+1],
|
|
"LIBPATH",
|
|
#ifdef CONF_LIBPATH
|
|
CONF_LIBPATH,
|
|
#else
|
|
&null[0],
|
|
#endif
|
|
0,
|
|
0,
|
|
7,
|
|
CONF_AST,
|
|
0,
|
|
OP_libpath
|
|
},
|
|
#define OP_libprefix 5
|
|
{
|
|
&dynamic[OP_libprefix+1],
|
|
"LIBPREFIX",
|
|
#ifdef CONF_LIBPREFIX
|
|
CONF_LIBPREFIX,
|
|
#else
|
|
"lib",
|
|
#endif
|
|
0,
|
|
0,
|
|
9,
|
|
CONF_AST,
|
|
0,
|
|
OP_libprefix
|
|
},
|
|
#define OP_libsuffix 6
|
|
{
|
|
&dynamic[OP_libsuffix+1],
|
|
"LIBSUFFIX",
|
|
#ifdef CONF_LIBSUFFIX
|
|
CONF_LIBSUFFIX,
|
|
#else
|
|
".so",
|
|
#endif
|
|
0,
|
|
0,
|
|
9,
|
|
CONF_AST,
|
|
0,
|
|
OP_libsuffix
|
|
},
|
|
#define OP_path_attributes 7
|
|
{
|
|
&dynamic[OP_path_attributes+1],
|
|
"PATH_ATTRIBUTES",
|
|
&null[0],
|
|
&null[0],
|
|
0,
|
|
15,
|
|
CONF_AST,
|
|
CONF_READONLY,
|
|
OP_path_attributes
|
|
},
|
|
#define OP_path_resolve 8
|
|
{
|
|
&dynamic[OP_path_resolve+1],
|
|
"PATH_RESOLVE",
|
|
&null[0],
|
|
"physical",
|
|
"metaphysical",
|
|
12,
|
|
CONF_AST,
|
|
0,
|
|
OP_path_resolve
|
|
},
|
|
#define OP_universe 9
|
|
{
|
|
0,
|
|
"UNIVERSE",
|
|
&null[0],
|
|
"att",
|
|
0,
|
|
8,
|
|
CONF_AST,
|
|
0,
|
|
OP_universe
|
|
},
|
|
{
|
|
0
|
|
}
|
|
};
|
|
|
|
typedef struct State_s
|
|
{
|
|
|
|
const char* id;
|
|
const char* name;
|
|
const char* standard;
|
|
const char* strict;
|
|
Feature_t* features;
|
|
|
|
int std;
|
|
|
|
/* default initialization from here down */
|
|
|
|
int prefix;
|
|
int synthesizing;
|
|
|
|
char* data;
|
|
char* last;
|
|
|
|
Feature_t* recent;
|
|
|
|
Ast_confdisc_f notify;
|
|
|
|
} State_t;
|
|
|
|
static State_t state = { "getconf", "_AST_FEATURES", "CONFORMANCE = standard", "POSIXLY_CORRECT", dynamic, -1 };
|
|
|
|
static char* feature(Feature_t*, const char*, const char*, const char*, unsigned int, Error_f);
|
|
|
|
/*
|
|
* return fmtbuf() copy of s
|
|
*/
|
|
|
|
static char*
|
|
buffer(char* s)
|
|
{
|
|
return strcpy(fmtbuf(strlen(s) + 1), s);
|
|
}
|
|
|
|
/*
|
|
* synthesize state for fp
|
|
* fp==0 initializes from getenv(state.name)
|
|
* value==0 just does lookup
|
|
* otherwise state is set to value
|
|
*/
|
|
|
|
static char*
|
|
synthesize(register Feature_t* fp, const char* path, const char* value)
|
|
{
|
|
register char* s;
|
|
register char* d;
|
|
register char* v;
|
|
register char* p;
|
|
register int n;
|
|
|
|
#if DEBUG_astconf
|
|
if (fp)
|
|
error(-6, "astconf synthesize name=%s path=%s value=%s fp=%p%s", fp->name, path, value, fp, state.synthesizing ? " SYNTHESIZING" : "");
|
|
#endif
|
|
if (state.synthesizing)
|
|
return null;
|
|
if (!state.data)
|
|
{
|
|
char* se;
|
|
char* de;
|
|
char* ve;
|
|
|
|
state.prefix = strlen(state.name) + 1;
|
|
n = state.prefix + 3 * MAXVAL;
|
|
if ((s = getenv(state.name)) || getenv(state.strict) && (s = (char*)state.standard))
|
|
n += strlen(s) + 1;
|
|
n = roundof(n, 32);
|
|
if (!(state.data = newof(0, char, n, 0)))
|
|
return 0;
|
|
state.last = state.data + n - 1;
|
|
strcpy(state.data, state.name);
|
|
state.data += state.prefix - 1;
|
|
*state.data++ = '=';
|
|
if (s)
|
|
strcpy(state.data, s);
|
|
ve = state.data;
|
|
state.synthesizing = 1;
|
|
for (;;)
|
|
{
|
|
for (s = ve; isspace(*s); s++);
|
|
for (d = s; *d && !isspace(*d); d++);
|
|
for (se = d; isspace(*d); d++);
|
|
for (v = d; *v && !isspace(*v); v++);
|
|
for (de = v; isspace(*v); v++);
|
|
if (!*v)
|
|
break;
|
|
for (ve = v; *ve && !isspace(*ve); ve++);
|
|
if (*ve)
|
|
*ve = 0;
|
|
else
|
|
ve = 0;
|
|
*de = 0;
|
|
*se = 0;
|
|
feature(0, s, d, v, 0, 0);
|
|
*se = ' ';
|
|
*de = ' ';
|
|
if (!ve)
|
|
break;
|
|
*ve++ = ' ';
|
|
}
|
|
state.synthesizing = 0;
|
|
}
|
|
if (!fp)
|
|
return state.data;
|
|
if (!state.last)
|
|
{
|
|
if (!value)
|
|
return 0;
|
|
n = strlen(value);
|
|
goto ok;
|
|
}
|
|
s = (char*)fp->name;
|
|
n = fp->length;
|
|
d = state.data;
|
|
for (;;)
|
|
{
|
|
while (isspace(*d))
|
|
d++;
|
|
if (!*d)
|
|
break;
|
|
if (strneq(d, s, n) && isspace(d[n]))
|
|
{
|
|
if (!value)
|
|
{
|
|
for (d += n + 1; *d && !isspace(*d); d++);
|
|
for (; isspace(*d); d++);
|
|
for (s = d; *s && !isspace(*s); s++);
|
|
n = s - d;
|
|
value = (const char*)d;
|
|
goto ok;
|
|
}
|
|
for (s = p = d + n + 1; *s && !isspace(*s); s++);
|
|
for (; isspace(*s); s++);
|
|
for (v = s; *s && !isspace(*s); s++);
|
|
n = s - v;
|
|
if ((!path || *path == *p && strlen(path) == (v - p - 1) && !memcmp(path, p, v - p - 1)) && strneq(v, value, n))
|
|
goto ok;
|
|
for (; isspace(*s); s++);
|
|
if (*s)
|
|
for (; *d = *s++; d++);
|
|
else if (d != state.data)
|
|
d--;
|
|
break;
|
|
}
|
|
for (; *d && !isspace(*d); d++);
|
|
for (; isspace(*d); d++);
|
|
for (; *d && !isspace(*d); d++);
|
|
for (; isspace(*d); d++);
|
|
for (; *d && !isspace(*d); d++);
|
|
}
|
|
if (!value)
|
|
{
|
|
if (!fp->op)
|
|
{
|
|
if (fp->flags & CONF_ALLOC)
|
|
fp->value[0] = 0;
|
|
else
|
|
fp->value = null;
|
|
}
|
|
return 0;
|
|
}
|
|
if (!value[0])
|
|
value = "0";
|
|
if (!path || !path[0] || path[0] == '/' && !path[1])
|
|
path = "-";
|
|
n += strlen(path) + strlen(value) + 3;
|
|
if (d + n >= state.last)
|
|
{
|
|
int c;
|
|
int i;
|
|
|
|
i = d - state.data;
|
|
state.data -= state.prefix;
|
|
c = n + state.last - state.data + 3 * MAXVAL;
|
|
c = roundof(c, 32);
|
|
if (!(state.data = newof(state.data, char, c, 0)))
|
|
return 0;
|
|
state.last = state.data + c - 1;
|
|
state.data += state.prefix;
|
|
d = state.data + i;
|
|
}
|
|
if (d != state.data)
|
|
*d++ = ' ';
|
|
for (s = (char*)fp->name; *d = *s++; d++);
|
|
*d++ = ' ';
|
|
for (s = (char*)path; *d = *s++; d++);
|
|
*d++ = ' ';
|
|
for (s = (char*)value; *d = *s++; d++);
|
|
#if DEBUG_astconf
|
|
error(-7, "astconf synthesize %s", state.data - state.prefix);
|
|
#endif
|
|
setenviron(state.data - state.prefix);
|
|
if (state.notify)
|
|
(*state.notify)(NiL, NiL, state.data - state.prefix);
|
|
n = s - (char*)value - 1;
|
|
ok:
|
|
if (!(fp->flags & CONF_ALLOC))
|
|
fp->value = 0;
|
|
if (n == 1 && (*value == '0' || *value == '-'))
|
|
n = 0;
|
|
if (!(fp->value = newof(fp->value, char, n, 1)))
|
|
fp->value = null;
|
|
else
|
|
{
|
|
fp->flags |= CONF_ALLOC;
|
|
memcpy(fp->value, value, n);
|
|
fp->value[n] = 0;
|
|
}
|
|
return fp->value;
|
|
}
|
|
|
|
/*
|
|
* initialize the value for fp
|
|
* if command!=0 then it is checked for on $PATH
|
|
* synthesize(fp,path,succeed) called on success
|
|
* otherwise synthesize(fp,path,fail) called
|
|
*/
|
|
|
|
static void
|
|
initialize(register Feature_t* fp, const char* path, const char* command, const char* succeed, const char* fail)
|
|
{
|
|
register char* p;
|
|
register int ok = 1;
|
|
|
|
#if DEBUG_astconf
|
|
error(-6, "astconf initialize name=%s path=%s command=%s succeed=%s fail=%s fp=%p%s", fp->name, path, command, succeed, fail, fp, state.synthesizing ? " SYNTHESIZING" : "");
|
|
#endif
|
|
switch (fp->op)
|
|
{
|
|
case OP_architecture:
|
|
ok = 1;
|
|
break;
|
|
case OP_conformance:
|
|
ok = getenv(state.strict) != 0;
|
|
break;
|
|
case OP_hosttype:
|
|
ok = 1;
|
|
break;
|
|
case OP_path_attributes:
|
|
ok = 1;
|
|
break;
|
|
case OP_path_resolve:
|
|
ok = 0;
|
|
break;
|
|
case OP_universe:
|
|
ok = streq(_UNIV_DEFAULT, DEFAULT(OP_universe));
|
|
/*FALLTHROUGH...*/
|
|
default:
|
|
if (p = getenv("PATH"))
|
|
{
|
|
register int r = 1;
|
|
register char* d = p;
|
|
Sfio_t* tmp;
|
|
|
|
#if DEBUG_astconf
|
|
error(-6, "astconf initialize name=%s ok=%d PATH=%s", fp->name, ok, p);
|
|
#endif
|
|
if (tmp = sfstropen())
|
|
{
|
|
for (;;)
|
|
{
|
|
switch (*p++)
|
|
{
|
|
case 0:
|
|
break;
|
|
case ':':
|
|
if (command && fp->op != OP_universe)
|
|
{
|
|
if (r = p - d - 1)
|
|
{
|
|
sfwrite(tmp, d, r);
|
|
sfputc(tmp, '/');
|
|
sfputr(tmp, command, 0);
|
|
if ((d = sfstruse(tmp)) && !eaccess(d, X_OK))
|
|
{
|
|
ok = 1;
|
|
if (fp->op != OP_universe)
|
|
break;
|
|
}
|
|
}
|
|
d = p;
|
|
}
|
|
r = 1;
|
|
continue;
|
|
case '/':
|
|
if (r)
|
|
{
|
|
r = 0;
|
|
if (fp->op == OP_universe)
|
|
{
|
|
if (p[0] == 'u' && p[1] == 's' && p[2] == 'r' && p[3] == '/')
|
|
for (p += 4; *p == '/'; p++);
|
|
if (p[0] == 'b' && p[1] == 'i' && p[2] == 'n')
|
|
{
|
|
for (p += 3; *p == '/'; p++);
|
|
if (!*p || *p == ':')
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (fp->op == OP_universe)
|
|
{
|
|
if (strneq(p, "xpg", 3))
|
|
{
|
|
ok = 1;
|
|
break;
|
|
}
|
|
if (strneq(p, "bsd", 3) || strneq(p, "ucb", 3))
|
|
{
|
|
ok = 0;
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
default:
|
|
r = 0;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
sfclose(tmp);
|
|
}
|
|
else
|
|
ok = 1;
|
|
}
|
|
break;
|
|
}
|
|
#if DEBUG_astconf
|
|
error(-6, "state.std=%d %s [%s] std=%s ast=%s value=%s ok=%d", state.std, fp->name, ok ? succeed : fail, fp->std, fp->ast, fp->value, ok);
|
|
#endif
|
|
synthesize(fp, path, ok ? succeed : fail);
|
|
}
|
|
|
|
/*
|
|
* format synthesized value
|
|
*/
|
|
|
|
static char*
|
|
format(register Feature_t* fp, const char* path, const char* value, unsigned int flags, Error_f conferror)
|
|
{
|
|
register Feature_t* sp;
|
|
register int n;
|
|
#if _UWIN && ( _X86_ || _X64_ )
|
|
struct stat st;
|
|
#else
|
|
static struct utsname uts;
|
|
#endif
|
|
|
|
#if DEBUG_astconf
|
|
error(-6, "astconf format name=%s path=%s value=%s flags=%04x fp=%p%s", fp->name, path, value, flags, fp, state.synthesizing ? " SYNTHESIZING" : "");
|
|
#endif
|
|
if (value)
|
|
fp->flags &= ~CONF_GLOBAL;
|
|
else if (fp->flags & CONF_GLOBAL)
|
|
return fp->value;
|
|
switch (fp->op)
|
|
{
|
|
|
|
case OP_architecture:
|
|
#if _UWIN && ( _X86_ || _X64_ )
|
|
if (!stat("/", &st))
|
|
{
|
|
if (st.st_ino == 64)
|
|
{
|
|
fp->value = "x64";
|
|
break;
|
|
}
|
|
if (st.st_ino == 32)
|
|
{
|
|
fp->value = "x86";
|
|
break;
|
|
}
|
|
}
|
|
#if _X64_
|
|
fp->value = "x64";
|
|
#else
|
|
fp->value = "x86";
|
|
#endif
|
|
#else
|
|
if (!uname(&uts))
|
|
return fp->value = uts.machine;
|
|
if (!(fp->value = getenv("HOSTNAME")))
|
|
fp->value = "unknown";
|
|
#endif
|
|
break;
|
|
|
|
case OP_conformance:
|
|
if (value && STANDARD(value))
|
|
value = fp->std;
|
|
state.std = streq(fp->value, fp->std);
|
|
#if DEBUG_astconf
|
|
error(-6, "state.std=%d %s [%s] std=%s ast=%s value=%s", state.std, fp->name, value, fp->std, fp->ast, fp->value);
|
|
#endif
|
|
if (state.synthesizing && value == (char*)fp->std)
|
|
fp->value = (char*)value;
|
|
else if (!synthesize(fp, path, value))
|
|
initialize(fp, path, NiL, fp->std, fp->value);
|
|
#if DEBUG_astconf
|
|
error(-6, "state.std=%d %s [%s] std=%s ast=%s value=%s", state.std, fp->name, value, fp->std, fp->ast, fp->value);
|
|
#endif
|
|
if (!state.std && value == fp->std)
|
|
{
|
|
state.std = 1;
|
|
for (sp = state.features; sp; sp = sp->next)
|
|
if (sp->std && sp->op && sp->op != OP_conformance)
|
|
feature(sp, 0, path, sp->std, 0, 0);
|
|
}
|
|
#if DEBUG_astconf
|
|
error(-6, "state.std=%d %s [%s] std=%s ast=%s value=%s", state.std, fp->name, value, fp->std, fp->ast, fp->value);
|
|
#endif
|
|
break;
|
|
|
|
case OP_hosttype:
|
|
break;
|
|
|
|
case OP_path_attributes:
|
|
#ifdef _PC_PATH_ATTRIBUTES
|
|
{
|
|
register char* s;
|
|
register char* e;
|
|
intmax_t v;
|
|
|
|
/*
|
|
* _PC_PATH_ATTRIBUTES is a bitmap for 'a' to 'z'
|
|
*/
|
|
|
|
if ((v = pathconf(path, _PC_PATH_ATTRIBUTES)) == -1L)
|
|
return 0;
|
|
s = fp->value;
|
|
e = s + sizeof(fp->value) - 1;
|
|
for (n = 'a'; n <= 'z'; n++)
|
|
if (v & (1 << (n - 'a')))
|
|
{
|
|
*s++ = n;
|
|
if (s >= e)
|
|
break;
|
|
}
|
|
*s = 0;
|
|
}
|
|
#else
|
|
fp->value = pathicase(path) > 0 ? "c" : null;
|
|
#endif
|
|
break;
|
|
|
|
case OP_path_resolve:
|
|
if (state.synthesizing && value == (char*)fp->std)
|
|
fp->value = (char*)value;
|
|
else if (!synthesize(fp, path, value))
|
|
initialize(fp, path, NiL, "logical", DEFAULT(OP_path_resolve));
|
|
break;
|
|
|
|
case OP_universe:
|
|
#if _lib_universe
|
|
if (getuniverse(fp->value) < 0)
|
|
strcpy(fp->value, DEFAULT(OP_universe));
|
|
if (value)
|
|
setuniverse(value);
|
|
#else
|
|
#ifdef UNIV_MAX
|
|
n = 0;
|
|
if (value)
|
|
{
|
|
while (n < univ_max && !streq(value, univ_name[n]))
|
|
n++;
|
|
if (n >= univ_max)
|
|
{
|
|
if (conferror)
|
|
(*conferror)(&state, &state, 2, "%s: %s: universe value too large", fp->name, value);
|
|
return 0;
|
|
}
|
|
}
|
|
#ifdef ATT_UNIV
|
|
n = setuniverse(n + 1);
|
|
if (!value && n > 0)
|
|
setuniverse(n);
|
|
#else
|
|
n = universe(value ? n + 1 : U_GET);
|
|
#endif
|
|
if (n <= 0 || n >= univ_max)
|
|
n = 1;
|
|
strcpy(fp->value, univ_name[n - 1]);
|
|
#else
|
|
if (value && streq(path, "="))
|
|
{
|
|
if (state.synthesizing)
|
|
{
|
|
if (!(fp->flags & CONF_ALLOC))
|
|
fp->value = 0;
|
|
n = strlen(value);
|
|
if (!(fp->value = newof(fp->value, char, n, 1)))
|
|
fp->value = null;
|
|
else
|
|
{
|
|
fp->flags |= CONF_ALLOC;
|
|
memcpy(fp->value, value, n);
|
|
fp->value[n] = 0;
|
|
}
|
|
}
|
|
else
|
|
synthesize(fp, path, value);
|
|
}
|
|
else
|
|
initialize(fp, path, "echo", DEFAULT(OP_universe), "ucb");
|
|
#endif
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
if (state.synthesizing && value == (char*)fp->std)
|
|
fp->value = (char*)value;
|
|
else
|
|
synthesize(fp, path, value);
|
|
break;
|
|
|
|
}
|
|
if (streq(path, "="))
|
|
fp->flags |= CONF_GLOBAL;
|
|
return fp->value;
|
|
}
|
|
|
|
/*
|
|
* value==0 get feature name
|
|
* value!=0 set feature name
|
|
* 0 returned if error or not defined; otherwise previous value
|
|
*/
|
|
|
|
static char*
|
|
feature(register Feature_t* fp, const char* name, const char* path, const char* value, unsigned int flags, Error_f conferror)
|
|
{
|
|
register int n;
|
|
|
|
if (value && (streq(value, "-") || streq(value, "0")))
|
|
value = null;
|
|
if (!fp)
|
|
for (fp = state.features; fp && !streq(fp->name, name); fp = fp->next);
|
|
#if DEBUG_astconf
|
|
error(-6, "astconf feature name=%s path=%s value=%s flags=%04x fp=%p%s", name, path, value, flags, fp, state.synthesizing ? " SYNTHESIZING" : "");
|
|
#endif
|
|
if (!fp)
|
|
{
|
|
if (!value)
|
|
return 0;
|
|
if (state.notify && !(*state.notify)(name, path, value))
|
|
return 0;
|
|
n = strlen(name);
|
|
if (!(fp = newof(0, Feature_t, 1, n + 1)))
|
|
{
|
|
if (conferror)
|
|
(*conferror)(&state, &state, 2, "%s: out of memory", name);
|
|
return 0;
|
|
}
|
|
fp->op = -1;
|
|
fp->name = (const char*)fp + sizeof(Feature_t);
|
|
strcpy((char*)fp->name, name);
|
|
fp->length = n;
|
|
fp->std = &null[0];
|
|
fp->next = state.features;
|
|
state.features = fp;
|
|
}
|
|
else if (value)
|
|
{
|
|
if (fp->flags & CONF_READONLY)
|
|
{
|
|
if (conferror)
|
|
(*conferror)(&state, &state, 2, "%s: cannot set readonly symbol", fp->name);
|
|
return 0;
|
|
}
|
|
if (state.notify && !streq(fp->value, value) && !(*state.notify)(name, path, value))
|
|
return 0;
|
|
}
|
|
else
|
|
state.recent = fp;
|
|
return format(fp, path, value, flags, conferror);
|
|
}
|
|
|
|
/*
|
|
* binary search for name in conf[]
|
|
*/
|
|
|
|
static int
|
|
lookup(register Lookup_t* look, const char* name, unsigned int flags)
|
|
{
|
|
register Conf_t* mid = (Conf_t*)conf;
|
|
register Conf_t* lo = mid;
|
|
register Conf_t* hi = mid + conf_elements;
|
|
register int v;
|
|
register int c;
|
|
char* e;
|
|
const Prefix_t* p;
|
|
|
|
static Conf_t num;
|
|
|
|
look->flags = 0;
|
|
look->call = -1;
|
|
look->standard = (flags & ASTCONF_AST) ? CONF_AST : -1;
|
|
look->section = -1;
|
|
while (*name == '_')
|
|
name++;
|
|
again:
|
|
for (p = prefix; p < &prefix[prefix_elements]; p++)
|
|
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] == '_')))
|
|
{
|
|
if (p->call < 0)
|
|
{
|
|
if (look->standard >= 0)
|
|
break;
|
|
look->standard = p->standard;
|
|
}
|
|
else
|
|
{
|
|
if (look->call >= 0)
|
|
break;
|
|
look->call = p->call;
|
|
}
|
|
if (name[p->length] == '(' || name[p->length] == '#')
|
|
{
|
|
look->conf = #
|
|
strlcpy((char*)num.name, name, sizeof(num.name));
|
|
num.call = p->call;
|
|
num.flags = *name == 'C' ? CONF_STRING : 0;
|
|
num.op = (short)strtol(name + p->length + 1, &e, 10);
|
|
if (name[p->length] == '(' && *e == ')')
|
|
e++;
|
|
if (*e)
|
|
break;
|
|
return 1;
|
|
}
|
|
name += p->length + c;
|
|
if (look->section < 0 && !c && v)
|
|
{
|
|
look->section = name[0] - '0';
|
|
name += 2;
|
|
}
|
|
goto again;
|
|
}
|
|
#if HUH_2006_02_10
|
|
if (look->section < 0)
|
|
look->section = 1;
|
|
#endif
|
|
look->name = name;
|
|
#if DEBUG_astconf
|
|
error(-6, "astconf normal name=%s standard=%d section=%d call=%d flags=%04x elements=%d", look->name, look->standard, look->section, look->call, flags, conf_elements);
|
|
#endif
|
|
c = *((unsigned char*)name);
|
|
while (lo <= hi)
|
|
{
|
|
mid = lo + (hi - lo) / 2;
|
|
#if DEBUG_astconf
|
|
error(-7, "astconf lookup name=%s mid=%s", name, mid->name);
|
|
#endif
|
|
if (!(v = c - *((unsigned char*)mid->name)) && !(v = strcmp(name, mid->name)))
|
|
{
|
|
hi = mid;
|
|
lo = (Conf_t*)conf;
|
|
do
|
|
{
|
|
if ((look->standard < 0 || look->standard == mid->standard) &&
|
|
(look->section < 0 || look->section == mid->section) &&
|
|
(look->call < 0 || look->call == mid->call))
|
|
goto found;
|
|
} while (mid-- > lo && streq(mid->name, look->name));
|
|
mid = hi;
|
|
hi = lo + conf_elements - 1;
|
|
while (++mid < hi && streq(mid->name, look->name))
|
|
{
|
|
if ((look->standard < 0 || look->standard == mid->standard) &&
|
|
(look->section < 0 || look->section == mid->section) &&
|
|
(look->call < 0 || look->call == mid->call))
|
|
goto found;
|
|
}
|
|
break;
|
|
}
|
|
else if (v > 0)
|
|
lo = mid + 1;
|
|
else
|
|
hi = mid - 1;
|
|
}
|
|
return 0;
|
|
found:
|
|
if (look->call < 0 && look->standard >= 0 && (look->section <= 1 || (mid->flags & CONF_MINMAX)))
|
|
look->flags |= CONF_MINMAX;
|
|
look->conf = mid;
|
|
#if DEBUG_astconf
|
|
error(-6, "astconf lookup name=%s standard=%d:%d section=%d:%d call=%d:%d", look->name, look->standard, mid->standard, look->section, mid->section, look->call, mid->call);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* return a tolower'd copy of s
|
|
*/
|
|
|
|
static char*
|
|
fmtlower(register const char* s)
|
|
{
|
|
register int c;
|
|
register char* t;
|
|
char* b;
|
|
|
|
b = t = fmtbuf(strlen(s) + 1);
|
|
while (c = *s++)
|
|
{
|
|
if (isupper(c))
|
|
c = tolower(c);
|
|
*t++ = c;
|
|
}
|
|
*t = 0;
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* print value line for p
|
|
* if !name then value prefixed by "p->name="
|
|
* if (flags & CONF_MINMAX) then default minmax value used
|
|
*/
|
|
|
|
static char*
|
|
print(Sfio_t* sp, register Lookup_t* look, const char* name, const char* path, int listflags, Error_f conferror)
|
|
{
|
|
register Conf_t* p = look->conf;
|
|
register unsigned int flags = look->flags;
|
|
Feature_t* fp;
|
|
char* call;
|
|
char* f;
|
|
const char* s;
|
|
int i;
|
|
int n;
|
|
int olderrno;
|
|
int drop;
|
|
int defined;
|
|
intmax_t v;
|
|
char buf[PATH_MAX];
|
|
char flg[16];
|
|
|
|
if (!name && !(p->flags & CONF_STRING) && (p->flags & (CONF_FEATURE|CONF_LIMIT|CONF_MINMAX)) && (p->flags & (CONF_LIMIT|CONF_PREFIXED)) != CONF_LIMIT)
|
|
flags |= CONF_PREFIXED;
|
|
olderrno = errno;
|
|
errno = 0;
|
|
#if DEBUG_astconf
|
|
error(-5, "astconf name=%s:%s:%s standard=%d section=%d call=%s op=%d flags=|%s%s%s%s%s:|%s%s%s%s%s%s%s%s%s%s"
|
|
, name, look->name, p->name, p->standard, p->section, prefix[p->call + CONF_call].name, p->op
|
|
, (flags & CONF_FEATURE) ? "FEATURE|" : ""
|
|
, (flags & CONF_LIMIT) ? "LIMIT|" : ""
|
|
, (flags & CONF_MINMAX) ? "MINMAX|" : ""
|
|
, (flags & CONF_PREFIXED) ? "PREFIXED|" : ""
|
|
, (flags & CONF_STRING) ? "STRING|" : ""
|
|
, (p->flags & CONF_DEFER_CALL) ? "DEFER_CALL|" : ""
|
|
, (p->flags & CONF_DEFER_MM) ? "DEFER_MM|" : ""
|
|
, (p->flags & CONF_FEATURE) ? "FEATURE|" : ""
|
|
, (p->flags & CONF_LIMIT_DEF) ? "LIMIT_DEF|" : (p->flags & CONF_LIMIT) ? "LIMIT|" : ""
|
|
, (p->flags & CONF_MINMAX_DEF) ? "MINMAX_DEF|" : (p->flags & CONF_MINMAX) ? "MINMAX|" : ""
|
|
, (p->flags & CONF_NOUNDERSCORE) ? "NOUNDERSCORE|" : ""
|
|
, (p->flags & CONF_PREFIXED) ? "PREFIXED|" : ""
|
|
, (p->flags & CONF_PREFIX_ONLY) ? "PREFIX_ONLY|" : ""
|
|
, (p->flags & CONF_STANDARD) ? "STANDARD|" : ""
|
|
, (p->flags & CONF_STRING) ? "STRING|" : ""
|
|
, (p->flags & CONF_UNDERSCORE) ? "UNDERSCORE|" : ""
|
|
);
|
|
#endif
|
|
flags |= CONF_LIMIT_DEF|CONF_MINMAX_DEF;
|
|
if (conferror && name)
|
|
{
|
|
if ((p->flags & CONF_PREFIX_ONLY) && look->standard < 0)
|
|
goto bad;
|
|
if (!(flags & CONF_MINMAX) || !(p->flags & CONF_MINMAX))
|
|
{
|
|
switch (p->call)
|
|
{
|
|
case CONF_pathconf:
|
|
if (path == root)
|
|
{
|
|
(*conferror)(&state, &state, 2, "%s: path expected", name);
|
|
goto bad;
|
|
}
|
|
break;
|
|
default:
|
|
if (path != root)
|
|
{
|
|
(*conferror)(&state, &state, 2, "%s: path not expected", name);
|
|
goto bad;
|
|
}
|
|
break;
|
|
}
|
|
#ifdef _pth_getconf
|
|
if (p->flags & CONF_DEFER_CALL)
|
|
goto bad;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (path != root)
|
|
{
|
|
(*conferror)(&state, &state, 2, "%s: path not expected", name);
|
|
goto bad;
|
|
}
|
|
#ifdef _pth_getconf
|
|
if ((p->flags & CONF_DEFER_MM) || !(p->flags & CONF_MINMAX_DEF))
|
|
goto bad;
|
|
#endif
|
|
}
|
|
if (look->standard >= 0 && (name[0] != '_' && ((p->flags & CONF_UNDERSCORE) || look->section <= 1) || name[0] == '_' && (p->flags & CONF_NOUNDERSCORE)) || look->standard < 0 && name[0] == '_')
|
|
goto bad;
|
|
}
|
|
s = 0;
|
|
defined = 1;
|
|
switch (i = (p->op < 0 || (flags & CONF_MINMAX) && (p->flags & CONF_MINMAX_DEF)) ? 0 : p->call)
|
|
{
|
|
case CONF_confstr:
|
|
call = "confstr";
|
|
#if _lib_confstr
|
|
if (!(v = confstr(p->op, buf, sizeof(buf))))
|
|
{
|
|
defined = 0;
|
|
v = -1;
|
|
errno = EINVAL;
|
|
}
|
|
else if (v > 0)
|
|
{
|
|
buf[sizeof(buf) - 1] = 0;
|
|
s = (const char*)buf;
|
|
}
|
|
else
|
|
defined = 0;
|
|
break;
|
|
#else
|
|
goto predef;
|
|
#endif
|
|
case CONF_pathconf:
|
|
call = "pathconf";
|
|
#if _lib_pathconf
|
|
if ((v = pathconf(path, p->op)) < 0)
|
|
defined = 0;
|
|
break;
|
|
#else
|
|
goto predef;
|
|
#endif
|
|
case CONF_sysconf:
|
|
call = "sysconf";
|
|
#if _lib_sysconf
|
|
if ((v = sysconf(p->op)) < 0)
|
|
defined = 0;
|
|
break;
|
|
#else
|
|
goto predef;
|
|
#endif
|
|
case CONF_sysinfo:
|
|
call = "sysinfo";
|
|
#if _lib_sysinfo
|
|
if ((v = sysinfo(p->op, buf, sizeof(buf))) >= 0)
|
|
{
|
|
buf[sizeof(buf) - 1] = 0;
|
|
s = (const char*)buf;
|
|
}
|
|
else
|
|
defined = 0;
|
|
break;
|
|
#else
|
|
goto predef;
|
|
#endif
|
|
default:
|
|
call = "synthesis";
|
|
errno = EINVAL;
|
|
v = -1;
|
|
defined = 0;
|
|
break;
|
|
case 0:
|
|
call = 0;
|
|
if (p->standard == CONF_AST)
|
|
{
|
|
if (streq(p->name, "RELEASE") && (i = open("/proc/version", O_RDONLY|O_cloexec)) >= 0)
|
|
{
|
|
n = read(i, buf, sizeof(buf) - 1);
|
|
close(i);
|
|
if (n > 0 && buf[n - 1] == '\n')
|
|
n--;
|
|
if (n > 0 && buf[n - 1] == '\r')
|
|
n--;
|
|
buf[n] = 0;
|
|
if (buf[0])
|
|
{
|
|
v = 0;
|
|
s = buf;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (p->flags & CONF_MINMAX_DEF)
|
|
{
|
|
if (!((p->flags & CONF_LIMIT_DEF)))
|
|
flags |= CONF_MINMAX;
|
|
listflags &= ~ASTCONF_system;
|
|
}
|
|
predef:
|
|
if (look->standard == CONF_AST)
|
|
{
|
|
if (streq(p->name, "VERSION"))
|
|
{
|
|
v = ast.version;
|
|
break;
|
|
}
|
|
}
|
|
if (flags & CONF_MINMAX)
|
|
{
|
|
if ((p->flags & CONF_MINMAX_DEF) && (!(listflags & ASTCONF_system) || !(p->flags & CONF_DEFER_MM)))
|
|
{
|
|
v = p->minmax.number;
|
|
s = p->minmax.string;
|
|
break;
|
|
}
|
|
}
|
|
else if ((p->flags & CONF_LIMIT_DEF) && (!(listflags & ASTCONF_system) || !(p->flags & CONF_DEFER_CALL)))
|
|
{
|
|
v = p->limit.number;
|
|
s = p->limit.string;
|
|
break;
|
|
}
|
|
flags &= ~(CONF_LIMIT_DEF|CONF_MINMAX_DEF);
|
|
v = -1;
|
|
errno = EINVAL;
|
|
defined = 0;
|
|
break;
|
|
}
|
|
if (!defined)
|
|
{
|
|
if (!errno)
|
|
{
|
|
if ((p->flags & CONF_FEATURE) || !(p->flags & (CONF_LIMIT|CONF_MINMAX)))
|
|
flags &= ~(CONF_LIMIT_DEF|CONF_MINMAX_DEF);
|
|
}
|
|
else if (flags & CONF_PREFIXED)
|
|
flags &= ~(CONF_LIMIT_DEF|CONF_MINMAX_DEF);
|
|
else if (errno != EINVAL || !i)
|
|
{
|
|
if (!sp)
|
|
{
|
|
if (conferror)
|
|
{
|
|
if (call)
|
|
(*conferror)(&state, &state, ERROR_SYSTEM|2, "%s: %s error", p->name, call);
|
|
else if (!(listflags & ASTCONF_system))
|
|
(*conferror)(&state, &state, 2, "%s: unknown name", p->name);
|
|
}
|
|
goto bad;
|
|
}
|
|
else
|
|
{
|
|
flags &= ~(CONF_LIMIT_DEF|CONF_MINMAX_DEF);
|
|
flags |= CONF_ERROR;
|
|
}
|
|
}
|
|
}
|
|
errno = olderrno;
|
|
if ((listflags & ASTCONF_defined) && !(flags & (CONF_LIMIT_DEF|CONF_MINMAX_DEF)))
|
|
goto bad;
|
|
if ((drop = !sp) && !(sp = sfstropen()))
|
|
goto bad;
|
|
if (listflags & ASTCONF_table)
|
|
{
|
|
f = flg;
|
|
if (p->flags & CONF_DEFER_CALL)
|
|
*f++ = 'C';
|
|
if (p->flags & CONF_DEFER_MM)
|
|
*f++ = 'D';
|
|
if (p->flags & CONF_FEATURE)
|
|
*f++ = 'F';
|
|
if (p->flags & CONF_LIMIT)
|
|
*f++ = 'L';
|
|
if (p->flags & CONF_MINMAX)
|
|
*f++ = 'M';
|
|
if (p->flags & CONF_NOSECTION)
|
|
*f++ = 'N';
|
|
if (p->flags & CONF_PREFIXED)
|
|
*f++ = 'P';
|
|
if (p->flags & CONF_STANDARD)
|
|
*f++ = 'S';
|
|
if (p->flags & CONF_UNDERSCORE)
|
|
*f++ = 'U';
|
|
if (p->flags & CONF_NOUNDERSCORE)
|
|
*f++ = 'V';
|
|
if (p->flags & CONF_PREFIX_ONLY)
|
|
*f++ = 'W';
|
|
if (f == flg)
|
|
*f++ = 'X';
|
|
*f = 0;
|
|
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);
|
|
if (p->flags & CONF_LIMIT_DEF)
|
|
{
|
|
if (p->limit.string)
|
|
sfprintf(sp, "L[%s] ", (listflags & ASTCONF_quote) ? fmtquote(p->limit.string, "\"", "\"", strlen(p->limit.string), FMT_SHELL|FMT_ALWAYS) : p->limit.string);
|
|
else
|
|
sfprintf(sp, "L[%I*d] ", sizeof(p->limit.number), p->limit.number);
|
|
}
|
|
if (p->flags & CONF_MINMAX_DEF)
|
|
{
|
|
if (p->minmax.string)
|
|
sfprintf(sp, "M[%s] ", (listflags & ASTCONF_quote) ? fmtquote(p->minmax.string, "\"", "\"", strlen(p->minmax.string), FMT_SHELL|FMT_ALWAYS) : p->minmax.string);
|
|
else
|
|
sfprintf(sp, "M[%I*d] ", sizeof(p->minmax.number), p->minmax.number);
|
|
}
|
|
if (flags & CONF_ERROR)
|
|
sfprintf(sp, "error");
|
|
else if (defined)
|
|
{
|
|
if (s)
|
|
sfprintf(sp, "%s", (listflags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL|FMT_ALWAYS) : s);
|
|
else if (v != -1)
|
|
sfprintf(sp, "%I*d", sizeof(v), v);
|
|
else
|
|
sfprintf(sp, "%I*u", sizeof(v), v);
|
|
}
|
|
sfprintf(sp, "\n");
|
|
}
|
|
else
|
|
{
|
|
if (!(flags & CONF_PREFIXED) || (listflags & ASTCONF_base))
|
|
{
|
|
if (!name)
|
|
{
|
|
if ((p->flags & (CONF_PREFIXED|CONF_STRING)) == (CONF_PREFIXED|CONF_STRING) && (!(listflags & ASTCONF_base) || p->standard != CONF_POSIX))
|
|
{
|
|
if ((p->flags & CONF_UNDERSCORE) && !(listflags & ASTCONF_base))
|
|
sfprintf(sp, "_");
|
|
sfprintf(sp, "%s", (listflags & ASTCONF_lower) ? fmtlower(prefix[p->standard].name) : prefix[p->standard].name);
|
|
if (p->section > 1)
|
|
sfprintf(sp, "%d", p->section);
|
|
sfprintf(sp, "_");
|
|
}
|
|
sfprintf(sp, "%s=", (listflags & ASTCONF_lower) ? fmtlower(p->name) : p->name);
|
|
}
|
|
if (flags & CONF_ERROR)
|
|
sfprintf(sp, "error");
|
|
else if (defined)
|
|
{
|
|
if (s)
|
|
sfprintf(sp, "%s", (listflags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL|FMT_ALWAYS) : s);
|
|
else if (v != -1)
|
|
sfprintf(sp, "%I*d", sizeof(v), v);
|
|
else
|
|
sfprintf(sp, "%I*u", sizeof(v), v);
|
|
}
|
|
else
|
|
sfprintf(sp, "undefined");
|
|
if (!name)
|
|
sfprintf(sp, "\n");
|
|
}
|
|
if (!name && !(listflags & ASTCONF_base) && !(p->flags & CONF_STRING) && (p->flags & (CONF_FEATURE|CONF_MINMAX)))
|
|
{
|
|
if (p->flags & CONF_UNDERSCORE)
|
|
sfprintf(sp, "_");
|
|
sfprintf(sp, "%s", (listflags & ASTCONF_lower) ? fmtlower(prefix[p->standard].name) : prefix[p->standard].name);
|
|
if (p->section > 1)
|
|
sfprintf(sp, "%d", p->section);
|
|
sfprintf(sp, "_%s=", (listflags & ASTCONF_lower) ? fmtlower(p->name) : p->name);
|
|
if (!defined && !name && (p->flags & CONF_MINMAX_DEF))
|
|
{
|
|
defined = 1;
|
|
v = p->minmax.number;
|
|
}
|
|
if (v != -1)
|
|
sfprintf(sp, "%I*d", sizeof(v), v);
|
|
else if (defined)
|
|
sfprintf(sp, "%I*u", sizeof(v), v);
|
|
else
|
|
sfprintf(sp, "undefined");
|
|
sfprintf(sp, "\n");
|
|
}
|
|
}
|
|
if (drop)
|
|
{
|
|
if (call = sfstruse(sp))
|
|
call = buffer(call);
|
|
else
|
|
call = "[ out of space ]";
|
|
sfclose(sp);
|
|
return call;
|
|
}
|
|
bad:
|
|
if (!(listflags & ~(ASTCONF_error|ASTCONF_system)))
|
|
for (fp = state.features; fp; fp = fp->next)
|
|
if (streq(name, fp->name))
|
|
return format(fp, path, 0, listflags, conferror);
|
|
return (listflags & ASTCONF_error) ? (char*)0 : null;
|
|
}
|
|
|
|
/*
|
|
* return read stream to native getconf utility
|
|
*/
|
|
|
|
#ifdef _pth_getconf_a
|
|
static Sfio_t*
|
|
nativeconf(Proc_t** pp, const char* operand)
|
|
{
|
|
Sfio_t* sp;
|
|
char* cmd[3];
|
|
long ops[2];
|
|
|
|
#if DEBUG_astconf
|
|
error(-6, "astconf defer %s %s", _pth_getconf, operand);
|
|
#endif
|
|
cmd[0] = (char*)state.id;
|
|
cmd[1] = (char*)operand;
|
|
cmd[2] = 0;
|
|
ops[0] = PROC_FD_DUP(open("/dev/null",O_WRONLY,0), 2, PROC_FD_CHILD);
|
|
ops[1] = 0;
|
|
if (*pp = procopen(_pth_getconf, cmd, environ, ops, PROC_READ))
|
|
{
|
|
if (sp = sfnew(NiL, NiL, SF_UNBOUND, (*pp)->rfd, SF_READ))
|
|
{
|
|
sfdisc(sp, SF_POPDISC);
|
|
return sp;
|
|
}
|
|
procclose(*pp);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* value==0 gets value for name
|
|
* value!=0 sets value for name and returns previous value
|
|
* path==0 implies path=="/"
|
|
*
|
|
* settable return values are in permanent store
|
|
* non-settable return values copied to a tmp fmtbuf() buffer
|
|
*
|
|
* if (streq(astgetconf("PATH_RESOLVE", NiL, NiL, 0, 0), "logical"))
|
|
* our_way();
|
|
*
|
|
* universe = astgetconf("UNIVERSE", NiL, "att", 0, 0);
|
|
* astgetconf("UNIVERSE", NiL, universe, 0, 0);
|
|
*
|
|
* if (flags&ASTCONF_error)!=0 then error return value is 0
|
|
* otherwise 0 not returned
|
|
*/
|
|
|
|
#define ALT 16
|
|
|
|
char*
|
|
astgetconf(const char* name, const char* path, const char* value, int flags, Error_f conferror)
|
|
{
|
|
register char* s;
|
|
int n;
|
|
Lookup_t look;
|
|
Sfio_t* tmp;
|
|
|
|
if (!name)
|
|
{
|
|
if (path)
|
|
return null;
|
|
if (!(name = value))
|
|
{
|
|
if (state.data)
|
|
{
|
|
Ast_confdisc_f notify;
|
|
|
|
#if _HUH20000515 /* doesn't work for shell builtins */
|
|
free(state.data - state.prefix);
|
|
#endif
|
|
state.data = 0;
|
|
notify = state.notify;
|
|
state.notify = 0;
|
|
INITIALIZE();
|
|
state.notify = notify;
|
|
}
|
|
return null;
|
|
}
|
|
value = 0;
|
|
}
|
|
INITIALIZE();
|
|
if (!path)
|
|
path = root;
|
|
if (state.recent && streq(name, state.recent->name) && (s = format(state.recent, path, value, flags, conferror)))
|
|
return s;
|
|
if (lookup(&look, name, flags))
|
|
{
|
|
if (value)
|
|
{
|
|
ro:
|
|
errno = EINVAL;
|
|
if (conferror)
|
|
(*conferror)(&state, &state, 2, "%s: cannot set value", name);
|
|
return (flags & ASTCONF_error) ? (char*)0 : null;
|
|
}
|
|
return print(NiL, &look, name, path, flags, conferror);
|
|
}
|
|
if ((n = strlen(name)) > 3 && n < (ALT + 3))
|
|
{
|
|
if (streq(name + n - 3, "DEV"))
|
|
{
|
|
if (tmp = sfstropen())
|
|
{
|
|
sfprintf(tmp, "/dev/");
|
|
for (s = (char*)name; s < (char*)name + n - 3; s++)
|
|
sfputc(tmp, isupper(*s) ? tolower(*s) : *s);
|
|
if ((s = sfstruse(tmp)) && !access(s, F_OK))
|
|
{
|
|
if (value)
|
|
goto ro;
|
|
s = buffer(s);
|
|
sfclose(tmp);
|
|
return s;
|
|
}
|
|
sfclose(tmp);
|
|
}
|
|
}
|
|
else if (streq(name + n - 3, "DIR"))
|
|
{
|
|
Lookup_t altlook;
|
|
char altname[ALT];
|
|
|
|
static const char* dirs[] = { "/usr/lib", "/usr", null };
|
|
|
|
strcpy(altname, name);
|
|
altname[n - 3] = 0;
|
|
if (lookup(&altlook, altname, flags))
|
|
{
|
|
if (value)
|
|
{
|
|
errno = EINVAL;
|
|
if (conferror)
|
|
(*conferror)(&state, &state, 2, "%s: cannot set value", altname);
|
|
return (flags & ASTCONF_error) ? (char*)0 : null;
|
|
}
|
|
return print(NiL, &altlook, altname, path, flags, conferror);
|
|
}
|
|
for (s = altname; *s; s++)
|
|
if (isupper(*s))
|
|
*s = tolower(*s);
|
|
if (tmp = sfstropen())
|
|
{
|
|
for (n = 0; n < elementsof(dirs); n++)
|
|
{
|
|
sfprintf(tmp, "%s/%s/.", dirs[n], altname);
|
|
if ((s = sfstruse(tmp)) && !access(s, F_OK))
|
|
{
|
|
if (value)
|
|
goto ro;
|
|
s = buffer(s);
|
|
sfclose(tmp);
|
|
return s;
|
|
}
|
|
}
|
|
sfclose(tmp);
|
|
}
|
|
}
|
|
}
|
|
if ((look.standard < 0 || look.standard == CONF_AST) && look.call <= 0 && look.section <= 1 && (s = feature(0, look.name, path, value, flags, conferror)))
|
|
return s;
|
|
errno = EINVAL;
|
|
if (conferror && !(flags & ASTCONF_system))
|
|
(*conferror)(&state, &state, 2, "%s: unknown name", name);
|
|
return (flags & ASTCONF_error) ? (char*)0 : null;
|
|
}
|
|
|
|
/*
|
|
* astconf() never returns 0
|
|
*/
|
|
|
|
char*
|
|
astconf(const char* name, const char* path, const char* value)
|
|
{
|
|
return astgetconf(name, path, value, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* set discipline function to be called when features change
|
|
* old discipline function returned
|
|
*/
|
|
|
|
Ast_confdisc_f
|
|
astconfdisc(Ast_confdisc_f new_notify)
|
|
{
|
|
Ast_confdisc_f old_notify;
|
|
|
|
INITIALIZE();
|
|
old_notify = state.notify;
|
|
state.notify = new_notify;
|
|
return old_notify;
|
|
}
|
|
|
|
/*
|
|
* list all name=value entries on sp
|
|
* path==0 implies path=="/"
|
|
*/
|
|
|
|
void
|
|
astconflist(Sfio_t* sp, const char* path, int flags, const char* pattern)
|
|
{
|
|
char* s;
|
|
char* f;
|
|
char* call;
|
|
Feature_t* fp;
|
|
Lookup_t look;
|
|
regex_t re;
|
|
regdisc_t redisc;
|
|
int olderrno;
|
|
char flg[8];
|
|
#ifdef _pth_getconf_a
|
|
Proc_t* proc;
|
|
Sfio_t* pp;
|
|
#endif
|
|
|
|
INITIALIZE();
|
|
if (!path)
|
|
path = root;
|
|
else if (access(path, F_OK))
|
|
{
|
|
errorf(&state, &state, 2, "%s: not found", path);
|
|
return;
|
|
}
|
|
olderrno = errno;
|
|
look.flags = 0;
|
|
if (!(flags & (ASTCONF_read|ASTCONF_write|ASTCONF_parse)))
|
|
flags |= ASTCONF_read|ASTCONF_write;
|
|
else if (flags & ASTCONF_parse)
|
|
flags |= ASTCONF_write;
|
|
if (!(flags & (ASTCONF_matchcall|ASTCONF_matchname|ASTCONF_matchstandard)))
|
|
pattern = 0;
|
|
if (pattern)
|
|
{
|
|
memset(&redisc, 0, sizeof(redisc));
|
|
redisc.re_version = REG_VERSION;
|
|
redisc.re_errorf = (regerror_t)errorf;
|
|
re.re_disc = &redisc;
|
|
if (regcomp(&re, pattern, REG_DISCIPLINE|REG_EXTENDED|REG_LENIENT|REG_NULL))
|
|
return;
|
|
}
|
|
if (flags & ASTCONF_read)
|
|
{
|
|
for (look.conf = (Conf_t*)conf; look.conf < (Conf_t*)&conf[conf_elements]; look.conf++)
|
|
{
|
|
if (pattern)
|
|
{
|
|
if (flags & ASTCONF_matchcall)
|
|
{
|
|
if (regexec(&re, prefix[look.conf->call + CONF_call].name, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
else if (flags & ASTCONF_matchname)
|
|
{
|
|
if (regexec(&re, look.conf->name, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
else if (flags & ASTCONF_matchstandard)
|
|
{
|
|
if (regexec(&re, prefix[look.conf->standard].name, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
}
|
|
look.standard = look.conf->standard;
|
|
look.section = look.conf->section;
|
|
print(sp, &look, NiL, path, flags, errorf);
|
|
}
|
|
#ifdef _pth_getconf_a
|
|
if (pp = nativeconf(&proc, _pth_getconf_a))
|
|
{
|
|
call = "GC";
|
|
while (f = sfgetr(pp, '\n', 1))
|
|
{
|
|
for (s = f; *s && *s != '=' && *s != ':' && !isspace(*s); s++);
|
|
if (*s)
|
|
for (*s++ = 0; isspace(*s); s++);
|
|
if (!lookup(&look, f, flags))
|
|
{
|
|
if(pattern)
|
|
{
|
|
if (flags & ASTCONF_matchcall)
|
|
{
|
|
if (regexec(&re, prefix[look.conf->call + CONF_call].name, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
else if (flags & ASTCONF_matchname)
|
|
{
|
|
if (regexec(&re, f, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
else if (flags & ASTCONF_matchstandard)
|
|
{
|
|
if (regexec(&re, prefix[look.standard].name, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
}
|
|
if (flags & ASTCONF_table)
|
|
{
|
|
if (look.standard < 0)
|
|
look.standard = 0;
|
|
if (look.section < 1)
|
|
look.section = 1;
|
|
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);
|
|
}
|
|
else if (flags & ASTCONF_parse)
|
|
sfprintf(sp, "%s %s - %s\n", state.id, (flags & ASTCONF_lower) ? fmtlower(f) : f, (flags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL|FMT_ALWAYS) : s);
|
|
else
|
|
sfprintf(sp, "%s=%s\n", (flags & ASTCONF_lower) ? fmtlower(f) : f, (flags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL|FMT_ALWAYS) : s);
|
|
}
|
|
}
|
|
sfclose(pp);
|
|
procclose(proc);
|
|
}
|
|
#endif
|
|
}
|
|
if (flags & ASTCONF_write)
|
|
{
|
|
call = "AC";
|
|
for (fp = state.features; fp; fp = fp->next)
|
|
{
|
|
if (pattern)
|
|
{
|
|
if (flags & ASTCONF_matchcall)
|
|
{
|
|
if (regexec(&re, call, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
else if (flags & ASTCONF_matchname)
|
|
{
|
|
if (regexec(&re, fp->name, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
else if (flags & ASTCONF_matchstandard)
|
|
{
|
|
if (regexec(&re, prefix[fp->standard].name, 0, NiL, 0))
|
|
continue;
|
|
}
|
|
}
|
|
if (!(s = feature(fp, 0, path, NiL, 0, 0)) || !*s)
|
|
s = "0";
|
|
if (flags & ASTCONF_table)
|
|
{
|
|
f = flg;
|
|
if (fp->flags & CONF_ALLOC)
|
|
*f++ = 'A';
|
|
if (fp->flags & CONF_READONLY)
|
|
*f++ = 'R';
|
|
if (f == flg)
|
|
*f++ = 'X';
|
|
*f = 0;
|
|
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);
|
|
}
|
|
else if (flags & ASTCONF_parse)
|
|
sfprintf(sp, "%s %s - %s\n", state.id, (flags & ASTCONF_lower) ? fmtlower(fp->name) : fp->name, (flags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL|FMT_ALWAYS) : s);
|
|
else
|
|
sfprintf(sp, "%s=%s\n", (flags & ASTCONF_lower) ? fmtlower(fp->name) : fp->name, (flags & ASTCONF_quote) ? fmtquote(s, "\"", "\"", strlen(s), FMT_SHELL|FMT_ALWAYS) : s);
|
|
}
|
|
}
|
|
if (pattern)
|
|
regfree(&re);
|
|
errno = olderrno;
|
|
}
|