further optimise and simplify the handling of $RANDOM, reads and writes

to it are now either arc4random or rand/srand, but srand retains the old
state; set +o arc4random is no longer possible, but if it's there we use
arc4random(3), if not, we use rand(3) for $RANDOM reads; optimise special
variable handling too and fix a few consts and other minor things
This commit is contained in:
tg 2009-09-26 03:40:03 +00:00
parent b50b43e3bf
commit a59d14b565
10 changed files with 158 additions and 242 deletions

View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.310 2009/09/24 17:15:29 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.311 2009/09/26 03:39:57 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -25,7 +25,7 @@
# http://www.research.att.com/~gsf/public/ifs.sh
expected-stdout:
@(#)MIRBSD KSH R39 2009/09/24
@(#)MIRBSD KSH R39 2009/09/25
description:
Check version of shell.
stdin:
@ -4042,8 +4042,8 @@ description:
isn't exec-ed
stdin:
sortprog=$(whence -p sort) || sortprog=cat
env | $sortprog >bar1
FOO=bar exec; env | $sortprog >bar2
env | $sortprog | grep -v '^RANDOM=' >bar1
FOO=bar exec; env | $sortprog | grep -v '^RANDOM=' >bar2
cmp -s bar1 bar2
---
name: xxx-what-do-you-call-this-1

View File

@ -25,7 +25,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.134 2009/09/23 18:04:56 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.135 2009/09/26 03:39:58 tg Exp $");
#if HAVE_KILLPG
/*
@ -94,7 +94,7 @@ const struct builtin mkshbuiltins[] = {
#endif
{"pwd", c_pwd},
{"*=readonly", c_typeset},
{"=typeset", c_typeset},
{T__typeset, c_typeset},
{"+unalias", c_unalias},
{"whence", c_whence},
#ifndef MKSH_UNEMPLOYED
@ -2309,7 +2309,7 @@ c_set(const char **wp)
const char **owp;
if (wp[1] == NULL) {
static const char *args [] = { "set", "-", NULL };
static const char *args[] = { "set", "-", NULL };
return (c_typeset(args));
}

6
jobs.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.60 2009/09/20 16:40:55 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.61 2009/09/26 03:39:59 tg Exp $");
#if HAVE_KILLPG
#define mksh_killpg killpg
@ -416,8 +416,8 @@ exchild(struct op *t, int flags,
else
p->pid = i;
#if !HAVE_ARC4RANDOM || !defined(MKSH_SMALL)
/* Ensure next child gets a (slightly) different $RANDOM sequence */
#if !HAVE_ARC4RANDOM
/* ensure next child gets a (slightly) different $RANDOM sequence */
change_random(((unsigned long)p->pid << 1) | (ischild ? 1 : 0));
#endif

84
main.c
View File

@ -33,7 +33,7 @@
#include <locale.h>
#endif
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.148 2009/09/26 01:08:27 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.149 2009/09/26 03:40:00 tg Exp $");
extern char **environ;
@ -47,17 +47,17 @@ static void remove_temps(struct temp *);
static const char initifs[] = "IFS= \t\n";
static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }";
static const char initsubs[] =
"${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0}";
static const char *initcoms[] = {
"typeset", "-r", initvsn, NULL,
"typeset", "-x", "SHELL", "PATH", "HOME", NULL,
"typeset", "-i10", "COLUMNS=0", "LINES=0", "OPTIND=1", NULL,
"typeset", "-Ui10", "PGRP", "PPID", "RANDOM", "USER_ID", NULL,
"eval", "typeset -i10 SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"",
NULL,
"alias", "integer=typeset -i", "local=typeset", NULL,
T_typeset, "-r", initvsn, NULL,
T_typeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
T_typeset, "-i10", "COLUMNS", "LINES", "OPTIND", "PGRP", "PPID",
"RANDOM", "SECONDS", "TMOUT", "USER_ID", NULL,
"alias",
"integer=typeset -i",
T_local_typeset,
"hash=alias -t", /* not "alias -t --": hash -r needs to work */
"type=whence -v",
#ifndef MKSH_UNEMPLOYED
@ -101,8 +101,14 @@ main(int argc, const char *argv[])
char *cp;
#endif
kshpid = procpid = getpid();
ksheuid = geteuid();
kshpgrp = getpgrp();
ppid = getppid();
#if !HAVE_ARC4RANDOM
change_random((unsigned long)time(NULL) * getpid());
change_random((unsigned long)time(NULL));
change_random(((unsigned long)ksheuid << 16) | kshpid);
#endif
/* make sure argv[] is sane */
@ -204,13 +210,14 @@ main(int argc, const char *argv[])
#endif
#ifdef MKSH_BINSHREDUCED
/* Set FPOSIX if we're called as -sh or /bin/sh or so */
/* set FPOSIX if we're called as -sh or /bin/sh or so */
{
const char *cc;
cc = kshname;
i = 0; argi = 0;
while (cc[i] != '\0')
/* the following line matches '-' and '/' ;-) */
if ((cc[i++] | 2) == '/')
argi = i;
if (((cc[argi] | 0x20) == 's') && ((cc[argi + 1] | 0x20) == 'h'))
@ -223,7 +230,6 @@ main(int argc, const char *argv[])
for (wp = (const char **)environ; *wp != NULL; wp++)
typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
kshpid = procpid = getpid();
typeset(initifs, 0, 0, 0, 0); /* for security */
/* assign default shell variable values */
@ -252,31 +258,38 @@ main(int argc, const char *argv[])
/* setstr can't fail here */
setstr(pwd_v, current_wd, KSH_RETURN_ERROR);
}
ppid = getppid();
#if !HAVE_ARC4RANDOM || !defined(MKSH_SMALL)
change_random(((unsigned long)kshname) ^
((unsigned long)time(NULL) * kshpid * ppid));
#endif
#if HAVE_ARC4RANDOM
Flag(FARC4RANDOM) = 2; /* use arc4random(3) until $RANDOM is written */
#endif
for (wp = initcoms; *wp != NULL; wp++) {
shcomexec(wp);
while (*wp != NULL)
wp++;
}
setint(global("COLUMNS"), 0);
setint(global("LINES"), 0);
setint(global("OPTIND"), 1);
vp = global("RANDOM");
#if HAVE_ARC4RANDOM
/* avoid calling setspec */
Flag(FARC4RANDOM) = 1;
vp->flag |= ISSET | INT_U;
#else
vp->flag |= INT_U;
setint(vp, (mksh_ari_t)((unsigned long)kshname + 33 * ppid));
#endif
safe_prompt = (ksheuid = geteuid()) ? "$ " : "# ";
safe_prompt = ksheuid ? "$ " : "# ";
vp = global("PS1");
/* Set PS1 if unset or we are root and prompt doesn't contain a # */
if (!(vp->flag & ISSET) ||
(!ksheuid && !strchr(str_val(vp), '#')))
/* setstr can't fail here */
setstr(vp, safe_prompt, KSH_RETURN_ERROR);
setint(global("PGRP"), (mksh_uari_t)(kshpgrp = getpgrp()));
setint(global("PPID"), (mksh_uari_t)ppid);
setint(global("USER_ID"), (mksh_uari_t)ksheuid);
setint((vp = global("PGRP")), (mksh_uari_t)kshpgrp);
vp->flag |= INT_U;
setint((vp = global("PPID")), (mksh_uari_t)ppid);
vp->flag |= INT_U;
setint((vp = global("USER_ID")), (mksh_uari_t)ksheuid);
vp->flag |= INT_U;
/* Set this before parsing arguments */
#if HAVE_SETRESUGID
@ -408,7 +421,7 @@ main(int argc, const char *argv[])
if (restricted) {
static const char *restr_com[] = {
"typeset", "-r", "PATH",
T_typeset, "-r", "PATH",
"ENV", "SHELL",
NULL
};
@ -1263,27 +1276,6 @@ hash(const char *cp)
return (h);
}
#if !HAVE_ARC4RANDOM || !defined(MKSH_SMALL)
uint32_t
hashmem(const void *cp, size_t len)
{
register uint32_t h = 0;
register const uint8_t *bp = (const uint8_t *)cp;
while (len--) {
h += *bp++;
h += h << 10;
h ^= h >> 6;
}
h += h << 3;
h ^= h >> 11;
h += h << 15;
return (h);
}
#endif
static void
texpand(struct table *tp, size_t nsize)
{

26
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.191 2009/09/20 17:23:51 tg Exp $
.\" $MirOS: src/bin/mksh/mksh.1,v 1.192 2009/09/26 03:40:00 tg Exp $
.\" $OpenBSD: ksh.1,v 1.129 2009/05/28 06:09:06 jmc Exp $
.\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
@ -48,7 +48,7 @@
.el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
..
.\"-
.Dd $Mdocdate: September 20 2009 $
.Dd $Mdocdate: September 26 2009 $
.Dt MKSH 1
.Os MirBSD
.Sh NAME
@ -1807,10 +1807,11 @@ if the shell doesn't know where it is.
Every time
.Ev RANDOM
is referenced, it is assigned a 15-bit pseudo-random number, i.e. between
0 and 32767, first.
See the description of
.Ic set \-o Ic arc4random
below for details.
0 and 32767, first, which is taken from
.Xr arc4random 3
if available,
.Xr rand 3
otherwise.
.It Ev REPLY
Default parameter for the
.Ic read
@ -3735,17 +3736,10 @@ Print commands and parameter assignments when they are executed, preceded by
the value of
.Ev PS4 .
.It Ic arc4random
Use
If this shell option, which cannot be unset, exists,
.Xr arc4random 3
high-quality random numbers for the value of
.Ev RANDOM
if set (to either 1 or 2), or a semi-predictable sequence from
.Xr rand 3
if unset.
Setting this flag will change its value to 1; the default value is 2,
which means it automatically switches to 0 if
.Ev RANDOM
is written to.
is used for the value of
.Ev RANDOM .
.It Ic bgnice
Background jobs are run with lower priority.
.It Ic braceexpand

30
sh.h
View File

@ -134,9 +134,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.348 2009/09/24 17:15:32 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.349 2009/09/26 03:40:01 tg Exp $");
#endif
#define MKSH_VERSION "R39 2009/09/24"
#define MKSH_VERSION "R39 2009/09/25"
#ifndef MKSH_INCLUDES_ONLY
@ -567,8 +567,11 @@ EXTERN char null[] I__("");
/* helpers for string pooling */
#define T_synerr "syntax error"
EXTERN const char r_fc_e_[] I__("r=fc -e -");
#define fc_e_ (r_fc_e_ + 2) /* "fc -e -" */
#define fc_e_n 7 /* strlen(fc_e_) */
#define fc_e_ (r_fc_e_ + 2) /* "fc -e -" */
#define fc_e_n 7 /* strlen(fc_e_) */
EXTERN const char T_local_typeset[] I__("local=typeset");
#define T__typeset (T_local_typeset + 5) /* "=typeset" */
#define T_typeset (T_local_typeset + 6) /* "typeset" */
enum temp_type {
TT_HEREDOC_EXP, /* expanded heredoc */
@ -963,21 +966,6 @@ struct builtin {
extern const struct builtin mkshbuiltins[];
/* var spec values */
#define V_NONE 0
#define V_COLUMNS 1
#define V_HISTFILE 2
#define V_HISTSIZE 3
#define V_IFS 4
#define V_LINENO 5
#define V_LINES 6
#define V_OPTIND 7
#define V_PATH 8
#define V_RANDOM 9
#define V_SECONDS 10
#define V_TMOUT 11
#define V_TMPDIR 12
/* values for set_prompt() */
#define PS1 0 /* command */
#define PS2 1 /* command continuation */
@ -1601,11 +1589,9 @@ void initvar(void);
struct tbl *global(const char *);
struct tbl *local(const char *, bool);
char *str_val(struct tbl *);
mksh_ari_t intval(struct tbl *);
int setstr(struct tbl *, const char *, int);
struct tbl *setint_v(struct tbl *, struct tbl *, bool);
void setint(struct tbl *, mksh_ari_t);
int getint(struct tbl *, mksh_ari_t *, bool);
struct tbl *typeset(const char *, Tflag, Tflag, int, int);
void unset(struct tbl *, int);
const char *skip_varname(const char *, int);
@ -1613,7 +1599,7 @@ const char *skip_wdvarname(const char *, int);
int is_wdvarname(const char *, int);
int is_wdvarassign(const char *);
char **makenv(void);
#if !HAVE_ARC4RANDOM || !defined(MKSH_SMALL)
#if !HAVE_ARC4RANDOM
void change_random(unsigned long);
#endif
void change_winsz(void);

View File

@ -1,5 +1,5 @@
#if defined(SHFLAGS_DEFNS)
__RCSID("$MirOS: src/bin/mksh/sh_flags.h,v 1.2 2009/09/24 17:15:33 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh_flags.h,v 1.3 2009/09/26 03:40:02 tg Exp $");
#define FN(sname,cname,ochar,flags) /* nothing */
#elif defined(SHFLAGS_ENUMS)
#define FN(sname,cname,ochar,flags) cname,
@ -22,9 +22,8 @@ __RCSID("$MirOS: src/bin/mksh/sh_flags.h,v 1.2 2009/09/24 17:15:33 tg Exp $");
F0("allexport", FEXPORT, 'a', OF_ANY)
#if HAVE_ARC4RANDOM
/* ./. for $RANDOM (non-standard), use the following function scheme: */
/* 0:rand(3) 1:arc4random(3) 2:switch from 1 to 0 on write */
FN("arc4random", FARC4RANDOM, 0, OF_ANY)
/* ./. backwards compat: available if arc4random(3) is used for $RANDOM */
FN("arc4random", FARC4RANDOM, 0, OF_INTERNAL)
#endif
#if HAVE_NICE

4
syn.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.43 2009/09/23 18:04:58 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.44 2009/09/26 03:40:02 tg Exp $");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
@ -877,7 +877,7 @@ assign_command(char *s)
return ((strcmp(s, "alias") == 0) ||
(strcmp(s, "export") == 0) ||
(strcmp(s, "readonly") == 0) ||
(strcmp(s, "typeset") == 0));
(strcmp(s, T_typeset) == 0));
}
/* Check if we are in the middle of reading an alias */

190
var.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.91 2009/09/23 18:22:38 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.92 2009/09/26 03:40:02 tg Exp $");
/*
* Variables
@ -42,10 +42,16 @@ static void unspecial(const char *);
static void getspec(struct tbl *);
static void setspec(struct tbl *);
static void unsetspec(struct tbl *);
static int getint(struct tbl *, mksh_ari_t *, bool);
static mksh_ari_t intval(struct tbl *);
static struct tbl *arraysearch(struct tbl *, uint32_t);
static const char *array_index_calc(const char *, bool *, uint32_t *);
static int rnd_get(void);
#if HAVE_ARC4RANDOM
static void rnd_set(unsigned long);
#else
#define rnd_set change_random
#endif
uint8_t set_refflag = 0;
@ -101,38 +107,33 @@ popblock(void)
}
/* called by main() to initialise variable data structures */
#define VARSPEC_DEFNS
#include "var_spec.h"
enum var_specs {
#define VARSPEC_ENUMS
#include "var_spec.h"
V_MAX
};
static const char * const initvar_names[] = {
#define VARSPEC_ITEMS
#include "var_spec.h"
};
void
initvar(void)
{
static const struct {
const char *name;
int v;
} names[] = {
{ "COLUMNS", V_COLUMNS },
#if HAVE_PERSISTENT_HISTORY
{ "HISTFILE", V_HISTFILE },
#endif
{ "HISTSIZE", V_HISTSIZE },
{ "IFS", V_IFS },
{ "LINENO", V_LINENO },
{ "LINES", V_LINES },
{ "OPTIND", V_OPTIND },
{ "PATH", V_PATH },
{ "RANDOM", V_RANDOM },
{ "SECONDS", V_SECONDS },
{ "TMOUT", V_TMOUT },
{ "TMPDIR", V_TMPDIR },
{ NULL, 0 }
};
int i;
int i = 0;
struct tbl *tp;
ktinit(&specials, APERM,
/* must be 80% of 2^n (currently 12 specials) */ 16);
for (i = 0; names[i].name; i++) {
tp = ktenter(&specials, names[i].name, hash(names[i].name));
while (i < V_MAX - 1) {
tp = ktenter(&specials, initvar_names[i],
hash(initvar_names[i]));
tp->flag = DEFINED|ISSET;
tp->type = names[i].v;
tp->type = ++i;
}
}
@ -384,7 +385,7 @@ str_val(struct tbl *vp)
}
/* get variable integer value, with error checking */
mksh_ari_t
static mksh_ari_t
intval(struct tbl *vp)
{
mksh_ari_t num;
@ -461,7 +462,7 @@ setint(struct tbl *vq, mksh_ari_t n)
setspec(vq);
}
int
static int
getint(struct tbl *vp, mksh_ari_t *nump, bool arith)
{
char *s;
@ -964,150 +965,55 @@ makenv(void)
return ((char **)XPclose(denv));
}
/*
* Get us a random number, either from rand(3) or arc4random(3), with
* the latter being preferred. If Flag(FARC4RANDOM) is 0, we use rand(3),
* otherwise arc4random(3). We have static caches to make change_random
* and writes to $RANDOM a cheap operation.
*/
#if HAVE_ARC4RANDOM && !defined(MKSH_SMALL)
static uint32_t rnd_cache[2];
static unsigned char rnd_lastflag = 2;
#if HAVE_ARC4RANDOM_PUSHB
static bool rnd_wpush = false;
#define rnd_mix(v,w) rnd_cmix(v,w)
#else
#define rnd_mix(v,w) rnd_cmix(v)
#endif
static void rnd_mix(unsigned long, bool);
static void
rnd_mix(unsigned long newval, bool wantpush)
{
struct {
union {
int rval;
uint32_t arval;
} v0;
uint32_t v1;
uint32_t v2;
unsigned long v3;
#if HAVE_ARC4RANDOM_PUSHB
bool neededpush;
bool wantingpush;
#endif
} v;
#if HAVE_ARC4RANDOM_PUSHB
v.neededpush = rnd_wpush;
v.wantingpush = wantpush;
rnd_wpush = v.neededpush || v.wantingpush;
#endif
v.v0.rval = rand();
v.v1 = rnd_cache[0];
v.v2 = rnd_cache[1];
v.v3 = newval;
rnd_cache[0] = hashmem(&v, sizeof(v));
v.v0.arval = arc4random();
rnd_cache[1] = hashmem(&v, sizeof(v));
}
#endif
#if HAVE_ARC4RANDOM
static int
rnd_get(void)
{
#if HAVE_ARC4RANDOM && defined(MKSH_SMALL)
return (arc4random() & 0x7FFF);
#else
#if HAVE_ARC4RANDOM
#if HAVE_ARC4RANDOM_PUSHB
uint32_t rv = 0;
#endif
if (Flag(FARC4RANDOM) != rnd_lastflag) {
if (Flag(FARC4RANDOM) == 0)
/* transition to 0 by set: srand */
srand(arc4random() & 0x7FFF);
else if (rnd_lastflag == 0)
/* transition from 0: addrandom */
rnd_mix(rand(), true);
rnd_lastflag = Flag(FARC4RANDOM);
}
if (Flag(FARC4RANDOM)) {
if (rnd_cache[0] || rnd_cache[1]) {
#if HAVE_ARC4RANDOM_PUSHB
if (rnd_wpush) {
rv = arc4random_pushb(rnd_cache,
sizeof(rnd_cache));
rnd_wpush = false;
} else
#endif
arc4random_addrandom((void *)rnd_cache,
sizeof(rnd_cache));
}
rnd_cache[0] = rnd_cache[1] = 0;
return ((
#if HAVE_ARC4RANDOM_PUSHB
rv ? rv :
#endif
arc4random()) & 0x7FFF);
}
#endif
return (rand() & 0x7FFF);
#endif
}
static void
rnd_set(unsigned long newval)
{
#if HAVE_ARC4RANDOM && defined(MKSH_SMALL)
#if HAVE_ARC4RANDOM_PUSHB
arc4random_pushb(&newval, sizeof(newval));
#else
arc4random_addrandom((void *)&newval, sizeof(newval));
#endif
}
#else
#if HAVE_ARC4RANDOM
rnd_mix(newval, true);
if (Flag(FARC4RANDOM) == 1)
return;
if (Flag(FARC4RANDOM) == 2)
Flag(FARC4RANDOM) = 0;
/* transition to 0 by write: only srand */
rnd_lastflag = 0;
#endif
srand(newval & 0x7FFF);
#endif
static int
rnd_get(void)
{
return (rand() & 0x7FFF);
}
#if !HAVE_ARC4RANDOM || !defined(MKSH_SMALL)
/*
* Called after a fork to bump the random number generator.
* Done to ensure children will not get the same random number sequence
* as the parent processes.
* Also called as rnd_set - mksh R40+ no longer has the traditional
* repeatability of randomness sequences, state is always retained.
*/
void
change_random(unsigned long newval)
{
int rval = 0;
register unsigned int h;
#if HAVE_ARC4RANDOM
if (Flag(FARC4RANDOM)) {
rnd_mix(newval, false);
return;
h = rand();
while (newval) {
h += (newval & 0xFF);
h += h << 10;
h ^= h >> 6;
newval >>= 8;
}
#endif
rval += newval & 0x7FFF;
newval >>= 15;
rval += newval & 0x7FFF;
newval >>= 15;
rval += newval + rand();
rval = (rval & 0x7FFF) ^ (rval >> 15);
h += h << 3;
h ^= h >> 11;
h += h << 15;
srand(rval);
/* pass all of it, in case RAND_MAX is large */
srand(h);
}
#endif

39
var_spec.h Normal file
View File

@ -0,0 +1,39 @@
#if defined(VARSPEC_DEFNS)
__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.1 2009/09/26 03:40:03 tg Exp $");
#define FN(name) /* nothing */
#elif defined(VARSPEC_ENUMS)
#define FN(name) V_##name,
#define F0(name) V_##name = 0,
#elif defined(VARSPEC_ITEMS)
#define F0(name) /* nothing */
#define FN(name) #name,
#endif
#ifndef F0
#define F0 FN
#endif
/* 0 is always V_NONE */
F0(NONE)
/* 1 and up are special variables */
FN(COLUMNS)
#if HAVE_PERSISTENT_HISTORY
FN(HISTFILE)
#endif
FN(HISTSIZE)
FN(IFS)
FN(LINENO)
FN(LINES)
FN(OPTIND)
FN(PATH)
FN(RANDOM)
FN(SECONDS)
FN(TMOUT)
FN(TMPDIR)
#undef FN
#undef F0
#undef VARSPEC_DEFNS
#undef VARSPEC_ENUMS
#undef VARSPEC_ITEMS