2008-02-24 16:20:52 +01:00
|
|
|
/* $OpenBSD: var.c,v 1.34 2007/10/15 02:16:35 deraadt Exp $ */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-05-16 18:59:42 +02:00
|
|
|
/*-
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
2009-05-16 18:59:42 +02:00
|
|
|
* Thorsten Glaser <tg@mirbsd.org>
|
|
|
|
*
|
|
|
|
* Provided that these terms and disclaimer and all copyright notices
|
|
|
|
* are retained or reproduced in an accompanying document, permission
|
|
|
|
* is granted to deal in this work without restriction, including un-
|
|
|
|
* limited rights to use, publicly perform, distribute, sell, modify,
|
|
|
|
* merge, give away, or sublicence.
|
|
|
|
*
|
|
|
|
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
|
|
|
|
* the utmost extent permitted by applicable law, neither express nor
|
|
|
|
* implied; without malicious intent or gross negligence. In no event
|
|
|
|
* may a licensor, author or contributor be held liable for indirect,
|
|
|
|
* direct, other damage, loss, or other issues arising in any way out
|
|
|
|
* of dealing in the work, even if advised of the possibility of such
|
|
|
|
* damage or existence of a defect, except proven that it results out
|
|
|
|
* of said person's immediate fault when using the work as intended.
|
|
|
|
*/
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
#include "sh.h"
|
|
|
|
|
2010-07-11 13:17:33 +02:00
|
|
|
#if defined(__OpenBSD__)
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.107 2010/07/11 11:17:33 tg Exp $");
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Variables
|
|
|
|
*
|
|
|
|
* WARNING: unreadable code, needs a rewrite
|
|
|
|
*
|
|
|
|
* if (flag&INTEGER), val.i contains integer value, and type contains base.
|
|
|
|
* otherwise, (val.s + type) contains string value.
|
|
|
|
* if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.
|
|
|
|
*/
|
2006-01-29 21:10:16 +01:00
|
|
|
static struct tbl vtemp;
|
|
|
|
static struct table specials;
|
2009-04-07 21:06:44 +02:00
|
|
|
static char *formatstr(struct tbl *, const char *);
|
|
|
|
static void export(struct tbl *, const char *);
|
|
|
|
static int special(const char *);
|
|
|
|
static void unspecial(const char *);
|
|
|
|
static void getspec(struct tbl *);
|
|
|
|
static void setspec(struct tbl *);
|
|
|
|
static void unsetspec(struct tbl *);
|
2009-09-26 05:40:03 +02:00
|
|
|
static int getint(struct tbl *, mksh_ari_t *, bool);
|
|
|
|
static mksh_ari_t intval(struct tbl *);
|
2007-10-18 22:32:33 +02:00
|
|
|
static struct tbl *arraysearch(struct tbl *, uint32_t);
|
|
|
|
static const char *array_index_calc(const char *, bool *, uint32_t *);
|
2009-10-17 23:16:05 +02:00
|
|
|
static uint32_t oaathash_update(register uint32_t, register const uint8_t *,
|
|
|
|
register size_t);
|
2010-07-04 19:33:58 +02:00
|
|
|
static uint32_t oaathash_finalise(register uint32_t);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-09-06 19:42:15 +02:00
|
|
|
uint8_t set_refflag = 0;
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
/*
|
|
|
|
* create a new block for function calls and simple commands
|
|
|
|
* assume caller has allocated and set up e->loc
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
newblock(void)
|
|
|
|
{
|
|
|
|
struct block *l;
|
2007-03-04 01:13:17 +01:00
|
|
|
static const char *empty[] = { null };
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
l = alloc(sizeof(struct block), ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
l->flags = 0;
|
2008-12-13 18:02:18 +01:00
|
|
|
ainit(&l->area); /* todo: could use e->area (l->area => l->areap) */
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!e->loc) {
|
|
|
|
l->argc = 0;
|
2006-11-12 15:58:16 +01:00
|
|
|
l->argv = empty;
|
2005-05-23 05:06:10 +02:00
|
|
|
} else {
|
|
|
|
l->argc = e->loc->argc;
|
|
|
|
l->argv = e->loc->argv;
|
|
|
|
}
|
|
|
|
l->exit = l->error = NULL;
|
2008-12-13 18:02:18 +01:00
|
|
|
ktinit(&l->vars, &l->area, 0);
|
|
|
|
ktinit(&l->funs, &l->area, 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
l->next = e->loc;
|
|
|
|
e->loc = l;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pop a block handling special variables
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
popblock(void)
|
|
|
|
{
|
|
|
|
struct block *l = e->loc;
|
|
|
|
struct tbl *vp, **vpp = l->vars.tbls, *vq;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
e->loc = l->next; /* pop block */
|
|
|
|
for (i = l->vars.size; --i >= 0; )
|
|
|
|
if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) {
|
|
|
|
if ((vq = global(vp->name))->flag & ISSET)
|
|
|
|
setspec(vq);
|
|
|
|
else
|
|
|
|
unsetspec(vq);
|
|
|
|
}
|
|
|
|
if (l->flags & BF_DOGETOPTS)
|
|
|
|
user_opt = l->getopts_state;
|
2008-12-13 18:02:18 +01:00
|
|
|
afreeall(&l->area);
|
2005-05-23 05:06:10 +02:00
|
|
|
afree(l, ATEMP);
|
|
|
|
}
|
|
|
|
|
2006-11-10 07:45:28 +01:00
|
|
|
/* called by main() to initialise variable data structures */
|
2009-09-26 05:40:03 +02:00
|
|
|
#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"
|
|
|
|
};
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
void
|
|
|
|
initvar(void)
|
|
|
|
{
|
2009-09-26 05:40:03 +02:00
|
|
|
int i = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *tp;
|
|
|
|
|
2009-08-28 20:54:01 +02:00
|
|
|
ktinit(&specials, APERM,
|
|
|
|
/* must be 80% of 2^n (currently 12 specials) */ 16);
|
2009-09-26 05:40:03 +02:00
|
|
|
while (i < V_MAX - 1) {
|
|
|
|
tp = ktenter(&specials, initvar_names[i],
|
|
|
|
hash(initvar_names[i]));
|
2005-05-23 05:06:10 +02:00
|
|
|
tp->flag = DEFINED|ISSET;
|
2009-09-26 05:40:03 +02:00
|
|
|
tp->type = ++i;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-10 20:12:51 +02:00
|
|
|
/* Used to calculate an array index for global()/local(). Sets *arrayp to
|
2009-09-06 19:42:15 +02:00
|
|
|
* true if this is an array, sets *valp to the array index, returns
|
2005-05-23 05:06:10 +02:00
|
|
|
* the basename of the array.
|
|
|
|
*/
|
|
|
|
static const char *
|
2007-10-18 22:32:33 +02:00
|
|
|
array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
int len;
|
2009-09-06 19:42:15 +02:00
|
|
|
char *ap = NULL;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
*arrayp = false;
|
2009-09-06 19:42:15 +02:00
|
|
|
redo_from_ref:
|
2005-05-23 05:06:10 +02:00
|
|
|
p = skip_varname(n, false);
|
2009-09-06 19:42:15 +02:00
|
|
|
if (!set_refflag && (p != n) && ksh_isalphx(n[0])) {
|
|
|
|
struct block *l = e->loc;
|
|
|
|
struct tbl *vp;
|
|
|
|
char *vn;
|
|
|
|
uint32_t h;
|
|
|
|
|
|
|
|
strndupx(vn, n, p - n, ATEMP);
|
|
|
|
h = hash(vn);
|
|
|
|
/* check if this is a reference */
|
2009-12-05 18:43:50 +01:00
|
|
|
do {
|
|
|
|
vp = ktsearch(&l->vars, vn, h);
|
|
|
|
} while (!vp && (l = l->next));
|
2009-09-06 19:42:15 +02:00
|
|
|
afree(vn, ATEMP);
|
|
|
|
if (vp && (vp->flag & (DEFINED|ASSOC|ARRAY)) ==
|
|
|
|
(DEFINED|ASSOC)) {
|
|
|
|
char *cp;
|
|
|
|
|
|
|
|
/* gotcha! */
|
|
|
|
cp = shf_smprintf("%s%s", str_val(vp), p);
|
|
|
|
afree(ap, ATEMP);
|
|
|
|
n = ap = cp;
|
|
|
|
goto redo_from_ref;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
if (p != n && *p == '[' && (len = array_ref_len(p))) {
|
|
|
|
char *sub, *tmp;
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_ari_t rval;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* Calculate the value of the subscript */
|
|
|
|
*arrayp = true;
|
2008-10-28 15:32:43 +01:00
|
|
|
strndupx(tmp, p + 1, len - 2, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
sub = substitute(tmp, 0);
|
|
|
|
afree(tmp, ATEMP);
|
2008-10-28 15:32:43 +01:00
|
|
|
strndupx(n, n, p - n, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
|
2007-10-25 18:10:16 +02:00
|
|
|
*valp = (uint32_t)rval;
|
2005-05-23 05:06:10 +02:00
|
|
|
afree(sub, ATEMP);
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (n);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search for variable, if not found create globally.
|
|
|
|
*/
|
|
|
|
struct tbl *
|
|
|
|
global(const char *n)
|
|
|
|
{
|
|
|
|
struct block *l = e->loc;
|
|
|
|
struct tbl *vp;
|
|
|
|
int c;
|
2007-10-18 22:32:33 +02:00
|
|
|
bool array;
|
2009-08-28 20:54:01 +02:00
|
|
|
uint32_t h, val;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* Check to see if this is an array */
|
|
|
|
n = array_index_calc(n, &array, &val);
|
|
|
|
h = hash(n);
|
|
|
|
c = n[0];
|
2006-11-10 08:52:04 +01:00
|
|
|
if (!ksh_isalphx(c)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
if (array)
|
|
|
|
errorf("bad substitution");
|
|
|
|
vp = &vtemp;
|
|
|
|
vp->flag = DEFINED;
|
|
|
|
vp->type = 0;
|
|
|
|
vp->areap = ATEMP;
|
|
|
|
*vp->name = c;
|
2006-11-10 08:52:04 +01:00
|
|
|
if (ksh_isdigit(c)) {
|
|
|
|
for (c = 0; ksh_isdigit(*n); n++)
|
2005-05-23 05:06:10 +02:00
|
|
|
c = c*10 + *n-'0';
|
|
|
|
if (c <= l->argc)
|
|
|
|
/* setstr can't fail here */
|
|
|
|
setstr(vp, l->argv[c], KSH_RETURN_ERROR);
|
|
|
|
vp->flag |= RDONLY;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
vp->flag |= RDONLY;
|
|
|
|
if (n[1] != '\0')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
vp->flag |= ISSET|INTEGER;
|
|
|
|
switch (c) {
|
|
|
|
case '$':
|
|
|
|
vp->val.i = kshpid;
|
|
|
|
break;
|
|
|
|
case '!':
|
|
|
|
/* If no job, expand to nothing */
|
|
|
|
if ((vp->val.i = j_async()) == 0)
|
|
|
|
vp->flag &= ~(ISSET|INTEGER);
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
vp->val.i = exstat;
|
|
|
|
break;
|
|
|
|
case '#':
|
|
|
|
vp->val.i = l->argc;
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
vp->flag &= ~INTEGER;
|
|
|
|
vp->val.s = getoptions();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
vp->flag &= ~(ISSET|INTEGER);
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
for (l = e->loc; ; l = l->next) {
|
2009-08-28 22:30:59 +02:00
|
|
|
vp = ktsearch(&l->vars, n, h);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (vp != NULL) {
|
|
|
|
if (array)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (arraysearch(vp, val));
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (l->next == NULL)
|
|
|
|
break;
|
|
|
|
}
|
2009-08-28 22:30:59 +02:00
|
|
|
vp = ktenter(&l->vars, n, h);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (array)
|
|
|
|
vp = arraysearch(vp, val);
|
|
|
|
vp->flag |= DEFINED;
|
|
|
|
if (special(n))
|
|
|
|
vp->flag |= SPECIAL;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search for local variable, if not found create locally.
|
|
|
|
*/
|
|
|
|
struct tbl *
|
|
|
|
local(const char *n, bool copy)
|
|
|
|
{
|
|
|
|
struct block *l = e->loc;
|
|
|
|
struct tbl *vp;
|
2007-10-18 22:32:33 +02:00
|
|
|
bool array;
|
2009-08-28 20:54:01 +02:00
|
|
|
uint32_t h, val;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* Check to see if this is an array */
|
|
|
|
n = array_index_calc(n, &array, &val);
|
|
|
|
h = hash(n);
|
2006-11-10 08:52:04 +01:00
|
|
|
if (!ksh_isalphx(*n)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
vp = &vtemp;
|
|
|
|
vp->flag = DEFINED|RDONLY;
|
|
|
|
vp->type = 0;
|
|
|
|
vp->areap = ATEMP;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-08-28 22:30:59 +02:00
|
|
|
vp = ktenter(&l->vars, n, h);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (copy && !(vp->flag & DEFINED)) {
|
|
|
|
struct block *ll = l;
|
|
|
|
struct tbl *vq = NULL;
|
|
|
|
|
2009-08-28 22:30:59 +02:00
|
|
|
while ((ll = ll->next) && !(vq = ktsearch(&ll->vars, n, h)))
|
2005-05-23 05:06:10 +02:00
|
|
|
;
|
|
|
|
if (vq) {
|
|
|
|
vp->flag |= vq->flag &
|
|
|
|
(EXPORT | INTEGER | RDONLY | LJUST | RJUST |
|
|
|
|
ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L);
|
|
|
|
if (vq->flag & INTEGER)
|
|
|
|
vp->type = vq->type;
|
|
|
|
vp->u2.field = vq->u2.field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (array)
|
|
|
|
vp = arraysearch(vp, val);
|
|
|
|
vp->flag |= DEFINED;
|
|
|
|
if (special(n))
|
|
|
|
vp->flag |= SPECIAL;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* get variable string value */
|
|
|
|
char *
|
|
|
|
str_val(struct tbl *vp)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
if ((vp->flag&SPECIAL))
|
|
|
|
getspec(vp);
|
|
|
|
if (!(vp->flag&ISSET))
|
|
|
|
s = null; /* special to dollar() */
|
|
|
|
else if (!(vp->flag&INTEGER)) /* string source */
|
|
|
|
s = vp->val.s + vp->type;
|
|
|
|
else { /* integer source */
|
2009-06-10 20:12:51 +02:00
|
|
|
/* worst case number length is when base=2 */
|
|
|
|
/* 1 (minus) + 2 (base, up to 36) + 1 ('#') + number of bits
|
|
|
|
* in the mksh_uari_t + 1 (NUL) */
|
2009-06-08 22:06:50 +02:00
|
|
|
char strbuf[1 + 2 + 1 + 8 * sizeof(mksh_uari_t) + 1];
|
2005-05-23 05:06:10 +02:00
|
|
|
const char *digits = (vp->flag & UCASEV_AL) ?
|
2009-09-20 15:08:12 +02:00
|
|
|
digits_uc : digits_lc;
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_uari_t n;
|
2005-05-23 05:06:10 +02:00
|
|
|
int base;
|
|
|
|
|
|
|
|
s = strbuf + sizeof(strbuf);
|
|
|
|
if (vp->flag & INT_U)
|
2009-03-14 19:12:55 +01:00
|
|
|
n = vp->val.u;
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
|
|
|
n = (vp->val.i < 0) ? -vp->val.i : vp->val.i;
|
|
|
|
base = (vp->type == 0) ? 10 : vp->type;
|
|
|
|
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
if (base == 1) {
|
|
|
|
size_t sz = 1;
|
|
|
|
|
|
|
|
*(s = strbuf) = '1';
|
|
|
|
s[1] = '#';
|
2008-12-04 19:11:08 +01:00
|
|
|
if (!UTFMODE || ((n & 0xFF80) == 0xEF80))
|
2009-09-26 06:01:34 +02:00
|
|
|
/* OPTU-16 -> raw octet */
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
s[2] = n & 0xFF;
|
|
|
|
else
|
|
|
|
sz = utf_wctomb(s + 2, n);
|
|
|
|
s[2 + sz] = '\0';
|
|
|
|
} else {
|
|
|
|
*--s = '\0';
|
|
|
|
do {
|
|
|
|
*--s = digits[n % base];
|
|
|
|
n /= base;
|
|
|
|
} while (n != 0);
|
|
|
|
if (base != 10) {
|
|
|
|
*--s = '#';
|
|
|
|
*--s = digits[base % 10];
|
|
|
|
if (base >= 10)
|
|
|
|
*--s = digits[base / 10];
|
|
|
|
}
|
|
|
|
if (!(vp->flag & INT_U) && vp->val.i < 0)
|
|
|
|
*--s = '-';
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (vp->flag & (RJUST|LJUST)) /* case already dealt with */
|
|
|
|
s = formatstr(vp, s);
|
2006-05-27 00:17:21 +02:00
|
|
|
else
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(s, s, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (s);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* get variable integer value, with error checking */
|
2009-09-26 05:40:03 +02:00
|
|
|
static mksh_ari_t
|
2005-05-23 05:06:10 +02:00
|
|
|
intval(struct tbl *vp)
|
|
|
|
{
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_ari_t num;
|
2005-05-23 05:06:10 +02:00
|
|
|
int base;
|
|
|
|
|
|
|
|
base = getint(vp, &num, false);
|
|
|
|
if (base == -1)
|
|
|
|
/* XXX check calls - is error here ok by POSIX? */
|
|
|
|
errorf("%s: bad number", str_val(vp));
|
2009-03-14 19:12:55 +01:00
|
|
|
return (num);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set variable to string value */
|
|
|
|
int
|
|
|
|
setstr(struct tbl *vq, const char *s, int error_ok)
|
|
|
|
{
|
2007-09-09 20:06:42 +02:00
|
|
|
char *salloc = NULL;
|
2005-05-23 05:06:10 +02:00
|
|
|
int no_ro_check = error_ok & 0x4;
|
2007-09-09 20:06:42 +02:00
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
error_ok &= ~0x4;
|
|
|
|
if ((vq->flag & RDONLY) && !no_ro_check) {
|
|
|
|
warningf(true, "%s: is read only", vq->name);
|
|
|
|
if (!error_ok)
|
2007-07-22 16:01:50 +02:00
|
|
|
errorfz();
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (!(vq->flag&INTEGER)) { /* string dest */
|
|
|
|
if ((vq->flag&ALLOC)) {
|
|
|
|
/* debugging */
|
|
|
|
if (s >= vq->val.s &&
|
|
|
|
s <= vq->val.s + strlen(vq->val.s))
|
2007-05-13 19:51:24 +02:00
|
|
|
internal_errorf(
|
2005-05-23 05:06:10 +02:00
|
|
|
"setstr: %s=%s: assigning to self",
|
|
|
|
vq->name, s);
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(vq->val.s, vq->areap);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
vq->flag &= ~(ISSET|ALLOC);
|
|
|
|
vq->type = 0;
|
|
|
|
if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
|
2007-09-09 20:06:42 +02:00
|
|
|
s = salloc = formatstr(vq, s);
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vq->flag&EXPORT))
|
|
|
|
export(vq, s);
|
|
|
|
else {
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(vq->val.s, s, vq->areap);
|
2005-05-23 05:06:10 +02:00
|
|
|
vq->flag |= ALLOC;
|
|
|
|
}
|
|
|
|
} else { /* integer dest */
|
|
|
|
if (!v_evaluate(vq, s, error_ok, true))
|
2007-09-09 20:06:42 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
vq->flag |= ISSET;
|
|
|
|
if ((vq->flag&SPECIAL))
|
|
|
|
setspec(vq);
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(salloc, ATEMP);
|
2007-09-09 20:06:42 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set variable to integer */
|
|
|
|
void
|
2009-03-14 19:12:55 +01:00
|
|
|
setint(struct tbl *vq, mksh_ari_t n)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
if (!(vq->flag&INTEGER)) {
|
|
|
|
struct tbl *vp = &vtemp;
|
|
|
|
vp->flag = (ISSET|INTEGER);
|
|
|
|
vp->type = 0;
|
|
|
|
vp->areap = ATEMP;
|
|
|
|
vp->val.i = n;
|
|
|
|
/* setstr can't fail here */
|
|
|
|
setstr(vq, str_val(vp), KSH_RETURN_ERROR);
|
|
|
|
} else
|
|
|
|
vq->val.i = n;
|
|
|
|
vq->flag |= ISSET;
|
|
|
|
if ((vq->flag&SPECIAL))
|
|
|
|
setspec(vq);
|
|
|
|
}
|
|
|
|
|
2009-09-26 05:40:03 +02:00
|
|
|
static int
|
2009-03-14 19:12:55 +01:00
|
|
|
getint(struct tbl *vp, mksh_ari_t *nump, bool arith)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
char *s;
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
int c, base, neg;
|
|
|
|
bool have_base = false;
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_ari_t num;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (vp->flag&SPECIAL)
|
|
|
|
getspec(vp);
|
|
|
|
/* XXX is it possible for ISSET to be set and val.s to be 0? */
|
|
|
|
if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (-1);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (vp->flag&INTEGER) {
|
|
|
|
*nump = vp->val.i;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp->type);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
s = vp->val.s + vp->type;
|
|
|
|
if (s == NULL) /* redundant given initial test */
|
|
|
|
s = null;
|
|
|
|
base = 10;
|
|
|
|
num = 0;
|
|
|
|
neg = 0;
|
|
|
|
if (arith && *s == '0' && *(s+1)) {
|
|
|
|
s++;
|
|
|
|
if (*s == 'x' || *s == 'X') {
|
|
|
|
s++;
|
|
|
|
base = 16;
|
2006-05-10 20:54:13 +02:00
|
|
|
} else if (vp->flag & ZEROFIL) {
|
|
|
|
while (*s == '0')
|
|
|
|
s++;
|
2005-05-23 05:06:10 +02:00
|
|
|
} else
|
|
|
|
base = 8;
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
have_base = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
for (c = *s++; c ; c = *s++) {
|
|
|
|
if (c == '-') {
|
|
|
|
neg++;
|
2006-11-10 08:52:04 +01:00
|
|
|
continue;
|
2005-05-23 05:06:10 +02:00
|
|
|
} else if (c == '#') {
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
base = (int)num;
|
|
|
|
if (have_base || base < 1 || base > 36)
|
|
|
|
return (-1);
|
|
|
|
if (base == 1) {
|
|
|
|
unsigned int wc;
|
|
|
|
|
2008-12-04 19:11:08 +01:00
|
|
|
if (!UTFMODE)
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
wc = *(unsigned char *)s;
|
|
|
|
else if (utf_mbtowc(&wc, s) == (size_t)-1)
|
2009-09-26 06:01:34 +02:00
|
|
|
/* OPTU-8 -> OPTU-16 */
|
|
|
|
/*
|
|
|
|
* (with a twist: 1#\uEF80 converts
|
|
|
|
* the same as 1#\x80 does, thus is
|
|
|
|
* not round-tripping correctly XXX)
|
|
|
|
*/
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
wc = 0xEF00 + *(unsigned char *)s;
|
2009-03-14 19:12:55 +01:00
|
|
|
*nump = (mksh_ari_t)wc;
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
return (1);
|
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
num = 0;
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
have_base = true;
|
2006-11-10 08:52:04 +01:00
|
|
|
continue;
|
|
|
|
} else if (ksh_isdigit(c))
|
|
|
|
c -= '0';
|
|
|
|
else if (ksh_islower(c))
|
|
|
|
c -= 'a' - 10;
|
|
|
|
else if (ksh_isupper(c))
|
|
|
|
c -= 'A' - 10;
|
|
|
|
else
|
2009-06-08 22:06:50 +02:00
|
|
|
return (-1);
|
2006-11-10 08:52:04 +01:00
|
|
|
if (c < 0 || c >= base)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (-1);
|
2006-11-10 08:52:04 +01:00
|
|
|
num = num * base + c;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (neg)
|
|
|
|
num = -num;
|
|
|
|
*nump = num;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (base);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* convert variable vq to integer variable, setting its value from vp
|
|
|
|
* (vq and vp may be the same)
|
|
|
|
*/
|
|
|
|
struct tbl *
|
|
|
|
setint_v(struct tbl *vq, struct tbl *vp, bool arith)
|
|
|
|
{
|
|
|
|
int base;
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_ari_t num;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if ((base = getint(vp, &num, arith)) == -1)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (NULL);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
|
|
|
|
vq->flag &= ~ALLOC;
|
|
|
|
afree(vq->val.s, vq->areap);
|
|
|
|
}
|
|
|
|
vq->val.i = num;
|
|
|
|
if (vq->type == 0) /* default base */
|
|
|
|
vq->type = base;
|
|
|
|
vq->flag |= ISSET|INTEGER;
|
|
|
|
if (vq->flag&SPECIAL)
|
|
|
|
setspec(vq);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vq);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
formatstr(struct tbl *vp, const char *s)
|
|
|
|
{
|
|
|
|
int olen, nlen;
|
|
|
|
char *p, *q;
|
2008-04-19 19:21:55 +02:00
|
|
|
size_t psiz;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-11-28 15:28:03 +01:00
|
|
|
olen = utf_mbswidth(s);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (vp->flag & (RJUST|LJUST)) {
|
|
|
|
if (!vp->u2.field) /* default field width */
|
|
|
|
vp->u2.field = olen;
|
|
|
|
nlen = vp->u2.field;
|
|
|
|
} else
|
|
|
|
nlen = olen;
|
|
|
|
|
2008-12-13 18:02:18 +01:00
|
|
|
p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (vp->flag & (RJUST|LJUST)) {
|
2008-05-02 20:55:37 +02:00
|
|
|
int slen = olen, i = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (vp->flag & RJUST) {
|
2008-04-19 19:21:55 +02:00
|
|
|
const char *qq = s;
|
2008-04-19 23:04:09 +02:00
|
|
|
int n = 0;
|
2008-04-19 19:21:55 +02:00
|
|
|
|
2008-05-02 20:55:37 +02:00
|
|
|
while (i < slen)
|
|
|
|
i += utf_widthadj(qq, &qq);
|
2009-06-11 14:42:21 +02:00
|
|
|
/* strip trailing spaces (AT&T uses qq[-1] == ' ') */
|
2008-04-19 19:21:55 +02:00
|
|
|
while (qq > s && ksh_isspace(qq[-1])) {
|
2005-05-23 05:06:10 +02:00
|
|
|
--qq;
|
2008-04-19 19:21:55 +02:00
|
|
|
--slen;
|
|
|
|
}
|
2008-04-19 23:04:09 +02:00
|
|
|
if (vp->flag & ZEROFIL && vp->flag & INTEGER) {
|
|
|
|
if (s[1] == '#')
|
|
|
|
n = 2;
|
|
|
|
else if (s[2] == '#')
|
|
|
|
n = 3;
|
|
|
|
if (vp->u2.field <= n)
|
|
|
|
n = 0;
|
|
|
|
}
|
|
|
|
if (n) {
|
|
|
|
memcpy(p, s, n);
|
|
|
|
s += n;
|
|
|
|
}
|
2008-05-02 20:55:37 +02:00
|
|
|
while (slen > vp->u2.field)
|
|
|
|
slen -= utf_widthadj(s, &s);
|
2006-11-10 02:13:52 +01:00
|
|
|
if (vp->u2.field - slen)
|
2008-04-19 23:04:09 +02:00
|
|
|
memset(p + n, (vp->flag & ZEROFIL) ? '0' : ' ',
|
2006-11-10 02:13:52 +01:00
|
|
|
vp->u2.field - slen);
|
2008-04-19 23:04:09 +02:00
|
|
|
slen -= n;
|
2006-11-10 02:13:52 +01:00
|
|
|
shf_snprintf(p + vp->u2.field - slen,
|
2008-04-19 19:21:55 +02:00
|
|
|
psiz - (vp->u2.field - slen),
|
2006-11-10 02:13:52 +01:00
|
|
|
"%.*s", slen, s);
|
2005-05-23 05:06:10 +02:00
|
|
|
} else {
|
|
|
|
/* strip leading spaces/zeros */
|
2006-11-10 08:18:58 +01:00
|
|
|
while (ksh_isspace(*s))
|
2005-05-23 05:06:10 +02:00
|
|
|
s++;
|
|
|
|
if (vp->flag & ZEROFIL)
|
|
|
|
while (*s == '0')
|
|
|
|
s++;
|
|
|
|
shf_snprintf(p, nlen + 1, "%-*.*s",
|
|
|
|
vp->u2.field, vp->u2.field, s);
|
|
|
|
}
|
|
|
|
} else
|
2008-04-19 19:21:55 +02:00
|
|
|
memcpy(p, s, strlen(s) + 1);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (vp->flag & UCASEV_AL) {
|
|
|
|
for (q = p; *q; q++)
|
2006-11-10 08:18:58 +01:00
|
|
|
*q = ksh_toupper(*q);
|
2005-05-23 05:06:10 +02:00
|
|
|
} else if (vp->flag & LCASEV) {
|
|
|
|
for (q = p; *q; q++)
|
2006-11-10 08:18:58 +01:00
|
|
|
*q = ksh_tolower(*q);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (p);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make vp->val.s be "name=value" for quick exporting.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
export(struct tbl *vp, const char *val)
|
|
|
|
{
|
|
|
|
char *xp;
|
|
|
|
char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
|
|
|
|
int namelen = strlen(vp->name);
|
|
|
|
int vallen = strlen(val) + 1;
|
|
|
|
|
|
|
|
vp->flag |= ALLOC;
|
2008-12-13 18:02:18 +01:00
|
|
|
xp = alloc(namelen + 1 + vallen, vp->areap);
|
2005-05-23 05:06:10 +02:00
|
|
|
memcpy(vp->val.s = xp, vp->name, namelen);
|
|
|
|
xp += namelen;
|
|
|
|
*xp++ = '=';
|
|
|
|
vp->type = xp - vp->val.s; /* offset to value */
|
|
|
|
memcpy(xp, val, vallen);
|
|
|
|
if (op != NULL)
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(op, vp->areap);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* lookup variable (according to (set&LOCAL)),
|
|
|
|
* set its attributes (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL,
|
|
|
|
* LCASEV, UCASEV_AL), and optionally set its value if an assignment.
|
|
|
|
*/
|
|
|
|
struct tbl *
|
|
|
|
typeset(const char *var, Tflag set, Tflag clr, int field, int base)
|
|
|
|
{
|
|
|
|
struct tbl *vp;
|
|
|
|
struct tbl *vpbase, *t;
|
|
|
|
char *tvar;
|
|
|
|
const char *val;
|
2010-01-25 15:25:16 +01:00
|
|
|
int len;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* check for valid variable name, search for value */
|
|
|
|
val = skip_varname(var, false);
|
|
|
|
if (val == var)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (NULL);
|
2010-01-28 21:26:52 +01:00
|
|
|
mkssert(var != NULL);
|
|
|
|
mkssert(*var != 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (*val == '[') {
|
2009-09-06 19:42:15 +02:00
|
|
|
if (set_refflag)
|
|
|
|
errorf("%s: reference variable cannot be an array",
|
|
|
|
var);
|
2005-05-23 05:06:10 +02:00
|
|
|
len = array_ref_len(val);
|
|
|
|
if (len == 0)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (NULL);
|
2005-05-23 05:06:10 +02:00
|
|
|
/* IMPORT is only used when the shell starts up and is
|
2009-06-10 20:12:51 +02:00
|
|
|
* setting up its environment. Allow only simple array
|
2005-05-23 05:06:10 +02:00
|
|
|
* references at this time since parameter/command substitution
|
2006-01-30 13:37:24 +01:00
|
|
|
* is preformed on the [expression] which would be a major
|
2005-05-23 05:06:10 +02:00
|
|
|
* security hole.
|
|
|
|
*/
|
|
|
|
if (set & IMPORT) {
|
|
|
|
int i;
|
|
|
|
for (i = 1; i < len - 1; i++)
|
2006-11-10 08:52:04 +01:00
|
|
|
if (!ksh_isdigit(val[i]))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (NULL);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
val += len;
|
|
|
|
}
|
2008-10-28 15:32:43 +01:00
|
|
|
if (*val == '=')
|
|
|
|
strndupx(tvar, var, val++ - var, ATEMP);
|
|
|
|
else {
|
2005-05-23 05:06:10 +02:00
|
|
|
/* Importing from original environment: must have an = */
|
|
|
|
if (set & IMPORT)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (NULL);
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(tvar, var, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
val = NULL;
|
2010-01-25 15:25:16 +01:00
|
|
|
/* handle foo[*] ⇒ foo (whole array) mapping for R39b */
|
|
|
|
len = strlen(tvar);
|
|
|
|
if (len > 3 && tvar[len-3] == '[' && tvar[len-2] == '*' &&
|
|
|
|
tvar[len-1] == ']')
|
|
|
|
tvar[len-3] = '\0';
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Prevent typeset from creating a local PATH/ENV/SHELL */
|
|
|
|
if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
|
|
|
|
strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
|
|
|
|
errorf("%s: restricted", tvar);
|
|
|
|
|
|
|
|
vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? true : false) :
|
|
|
|
global(tvar);
|
2009-09-06 19:42:15 +02:00
|
|
|
if (set_refflag == 2 && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
|
|
|
|
vp->flag &= ~ASSOC;
|
|
|
|
else if (set_refflag == 1) {
|
|
|
|
if (vp->flag & ARRAY) {
|
|
|
|
struct tbl *a, *tmp;
|
|
|
|
|
|
|
|
/* Free up entire array */
|
|
|
|
for (a = vp->u.array; a; ) {
|
|
|
|
tmp = a;
|
|
|
|
a = a->u.array;
|
|
|
|
if (tmp->flag & ALLOC)
|
|
|
|
afree(tmp->val.s, tmp->areap);
|
|
|
|
afree(tmp, tmp->areap);
|
|
|
|
}
|
|
|
|
vp->u.array = NULL;
|
|
|
|
vp->flag &= ~ARRAY;
|
|
|
|
}
|
|
|
|
vp->flag |= ASSOC;
|
|
|
|
}
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
set &= ~(LOCAL|LOCAL_COPY);
|
|
|
|
|
|
|
|
vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp;
|
|
|
|
|
2009-06-11 14:42:21 +02:00
|
|
|
/* only allow export flag to be set. AT&T ksh allows any attribute to
|
2006-01-30 13:37:24 +01:00
|
|
|
* be changed which means it can be truncated or modified (-L/-R/-Z/-i)
|
2005-05-23 05:06:10 +02:00
|
|
|
*/
|
|
|
|
if ((vpbase->flag&RDONLY) &&
|
|
|
|
(val || clr || (set & ~EXPORT)))
|
|
|
|
/* XXX check calls - is error here ok by POSIX? */
|
|
|
|
errorf("%s: is read only", tvar);
|
2006-11-12 15:58:16 +01:00
|
|
|
afree(tvar, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* most calls are with set/clr == 0 */
|
|
|
|
if (set | clr) {
|
2010-01-25 15:25:16 +01:00
|
|
|
bool ok = true;
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
/* XXX if x[0] isn't set, there will be problems: need to have
|
|
|
|
* one copy of attributes for arrays...
|
|
|
|
*/
|
|
|
|
for (t = vpbase; t; t = t->u.array) {
|
2010-01-25 15:25:16 +01:00
|
|
|
bool fake_assign;
|
2005-05-23 05:06:10 +02:00
|
|
|
char *s = NULL;
|
|
|
|
char *free_me = NULL;
|
|
|
|
|
|
|
|
fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
|
|
|
|
((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||
|
|
|
|
((t->flag & INTEGER) && (clr & INTEGER)) ||
|
|
|
|
(!(t->flag & INTEGER) && (set & INTEGER)));
|
|
|
|
if (fake_assign) {
|
|
|
|
if (t->flag & INTEGER) {
|
|
|
|
s = str_val(t);
|
|
|
|
free_me = NULL;
|
|
|
|
} else {
|
|
|
|
s = t->val.s + t->type;
|
|
|
|
free_me = (t->flag & ALLOC) ? t->val.s :
|
|
|
|
NULL;
|
|
|
|
}
|
|
|
|
t->flag &= ~ALLOC;
|
|
|
|
}
|
|
|
|
if (!(t->flag & INTEGER) && (set & INTEGER)) {
|
|
|
|
t->type = 0;
|
|
|
|
t->flag &= ~ALLOC;
|
|
|
|
}
|
|
|
|
t->flag = (t->flag | set) & ~clr;
|
|
|
|
/* Don't change base if assignment is to be done,
|
|
|
|
* in case assignment fails.
|
|
|
|
*/
|
|
|
|
if ((set & INTEGER) && base > 0 && (!val || t != vp))
|
|
|
|
t->type = base;
|
|
|
|
if (set & (LJUST|RJUST|ZEROFIL))
|
|
|
|
t->u2.field = field;
|
|
|
|
if (fake_assign) {
|
|
|
|
if (!setstr(t, s, KSH_RETURN_ERROR)) {
|
|
|
|
/* Somewhat arbitrary action here:
|
|
|
|
* zap contents of variable, but keep
|
|
|
|
* the flag settings.
|
|
|
|
*/
|
2010-01-25 15:25:16 +01:00
|
|
|
ok = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (t->flag & INTEGER)
|
|
|
|
t->flag &= ~ISSET;
|
|
|
|
else {
|
|
|
|
if (t->flag & ALLOC)
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(t->val.s, t->areap);
|
2005-05-23 05:06:10 +02:00
|
|
|
t->flag &= ~(ISSET|ALLOC);
|
|
|
|
t->type = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (free_me)
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(free_me, t->areap);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!ok)
|
2007-07-22 16:01:50 +02:00
|
|
|
errorfz();
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (val != NULL) {
|
|
|
|
if (vp->flag&INTEGER) {
|
|
|
|
/* do not zero base before assignment */
|
|
|
|
setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
|
|
|
|
/* Done after assignment to override default */
|
|
|
|
if (base > 0)
|
|
|
|
vp->type = base;
|
|
|
|
} else
|
|
|
|
/* setstr can't fail (readonly check already done) */
|
|
|
|
setstr(vp, val, KSH_RETURN_ERROR | 0x4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* only x[0] is ever exported, so use vpbase */
|
|
|
|
if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
|
|
|
|
vpbase->type == 0)
|
|
|
|
export(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
/**
|
|
|
|
* Unset a variable. The flags can be:
|
|
|
|
* |1 = tear down entire array
|
|
|
|
* |2 = keep attributes, only unset content
|
2005-05-23 05:06:10 +02:00
|
|
|
*/
|
|
|
|
void
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
unset(struct tbl *vp, int flags)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
if (vp->flag & ALLOC)
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(vp->val.s, vp->areap);
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
if ((vp->flag & ARRAY) && (flags & 1)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *a, *tmp;
|
|
|
|
|
|
|
|
/* Free up entire array */
|
|
|
|
for (a = vp->u.array; a; ) {
|
|
|
|
tmp = a;
|
|
|
|
a = a->u.array;
|
|
|
|
if (tmp->flag & ALLOC)
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(tmp->val.s, tmp->areap);
|
2005-05-23 05:06:10 +02:00
|
|
|
afree(tmp, tmp->areap);
|
|
|
|
}
|
|
|
|
vp->u.array = NULL;
|
|
|
|
}
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
if (flags & 2) {
|
|
|
|
vp->flag &= ~(ALLOC|ISSET);
|
|
|
|
return;
|
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
/* If foo[0] is being unset, the remainder of the array is kept... */
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
vp->flag &= SPECIAL | ((flags & 1) ? 0 : ARRAY|DEFINED);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (vp->flag & SPECIAL)
|
|
|
|
unsetspec(vp); /* responsible for 'unspecial'ing var */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return a pointer to the first char past a legal variable name (returns the
|
2009-09-06 19:42:15 +02:00
|
|
|
* argument if there is no legal name, returns a pointer to the terminating
|
|
|
|
* NUL if whole string is legal).
|
2005-05-23 05:06:10 +02:00
|
|
|
*/
|
2006-11-12 15:58:16 +01:00
|
|
|
const char *
|
2005-05-23 05:06:10 +02:00
|
|
|
skip_varname(const char *s, int aok)
|
|
|
|
{
|
|
|
|
int alen;
|
|
|
|
|
2006-11-10 08:52:04 +01:00
|
|
|
if (s && ksh_isalphx(*s)) {
|
|
|
|
while (*++s && ksh_isalnux(*s))
|
2005-05-23 05:06:10 +02:00
|
|
|
;
|
|
|
|
if (aok && *s == '[' && (alen = array_ref_len(s)))
|
|
|
|
s += alen;
|
|
|
|
}
|
2006-11-12 15:58:16 +01:00
|
|
|
return (s);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-06-10 20:12:51 +02:00
|
|
|
/* Return a pointer to the first character past any legal variable name */
|
2006-11-12 15:58:16 +01:00
|
|
|
const char *
|
2005-05-23 05:06:10 +02:00
|
|
|
skip_wdvarname(const char *s,
|
|
|
|
int aok) /* skip array de-reference? */
|
|
|
|
{
|
2006-11-10 08:52:04 +01:00
|
|
|
if (s[0] == CHAR && ksh_isalphx(s[1])) {
|
2005-05-23 05:06:10 +02:00
|
|
|
do {
|
|
|
|
s += 2;
|
2006-11-10 08:52:04 +01:00
|
|
|
} while (s[0] == CHAR && ksh_isalnux(s[1]));
|
2005-05-23 05:06:10 +02:00
|
|
|
if (aok && s[0] == CHAR && s[1] == '[') {
|
|
|
|
/* skip possible array de-reference */
|
|
|
|
const char *p = s;
|
|
|
|
char c;
|
|
|
|
int depth = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (p[0] != CHAR)
|
|
|
|
break;
|
|
|
|
c = p[1];
|
|
|
|
p += 2;
|
|
|
|
if (c == '[')
|
|
|
|
depth++;
|
|
|
|
else if (c == ']' && --depth == 0) {
|
|
|
|
s = p;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-11-12 15:58:16 +01:00
|
|
|
return (s);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if coded string s is a variable name */
|
|
|
|
int
|
|
|
|
is_wdvarname(const char *s, int aok)
|
|
|
|
{
|
2006-11-12 15:58:16 +01:00
|
|
|
const char *p = skip_wdvarname(s, aok);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (p != s && p[0] == EOS);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if coded string s is a variable assignment */
|
|
|
|
int
|
|
|
|
is_wdvarassign(const char *s)
|
|
|
|
{
|
2006-11-12 15:58:16 +01:00
|
|
|
const char *p = skip_wdvarname(s, true);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (p != s && p[0] == CHAR && p[1] == '=');
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make the exported environment from the exported names in the dictionary.
|
|
|
|
*/
|
|
|
|
char **
|
|
|
|
makenv(void)
|
|
|
|
{
|
2007-07-01 23:10:29 +02:00
|
|
|
struct block *l;
|
2009-04-07 21:27:49 +02:00
|
|
|
XPtrV denv;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *vp, **vpp;
|
|
|
|
int i;
|
|
|
|
|
2009-04-07 21:27:49 +02:00
|
|
|
XPinit(denv, 64);
|
2005-05-23 05:06:10 +02:00
|
|
|
for (l = e->loc; l != NULL; l = l->next)
|
|
|
|
for (vpp = l->vars.tbls, i = l->vars.size; --i >= 0; )
|
|
|
|
if ((vp = *vpp++) != NULL &&
|
|
|
|
(vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) {
|
|
|
|
struct block *l2;
|
|
|
|
struct tbl *vp2;
|
2009-08-28 20:54:01 +02:00
|
|
|
uint32_t h = hash(vp->name);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* unexport any redefined instances */
|
|
|
|
for (l2 = l->next; l2 != NULL; l2 = l2->next) {
|
2009-08-28 22:30:59 +02:00
|
|
|
vp2 = ktsearch(&l2->vars, vp->name, h);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (vp2 != NULL)
|
|
|
|
vp2->flag &= ~EXPORT;
|
|
|
|
}
|
|
|
|
if ((vp->flag&INTEGER)) {
|
|
|
|
/* integer to string */
|
|
|
|
char *val;
|
|
|
|
val = str_val(vp);
|
2009-09-27 12:31:06 +02:00
|
|
|
vp->flag &= ~(INTEGER|RDONLY|SPECIAL);
|
2005-05-23 05:06:10 +02:00
|
|
|
/* setstr can't fail here */
|
|
|
|
setstr(vp, val, KSH_RETURN_ERROR);
|
|
|
|
}
|
2009-04-07 21:27:49 +02:00
|
|
|
XPput(denv, vp->val.s);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-04-07 21:27:49 +02:00
|
|
|
XPput(denv, NULL);
|
|
|
|
return ((char **)XPclose(denv));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2010-07-04 19:33:58 +02:00
|
|
|
/* Bob Jenkins' one-at-a-time hash */
|
2009-10-17 23:16:05 +02:00
|
|
|
static uint32_t
|
|
|
|
oaathash_update(register uint32_t h, register const uint8_t *cp,
|
|
|
|
register size_t n)
|
2007-08-12 15:42:23 +02:00
|
|
|
{
|
2009-10-17 23:16:05 +02:00
|
|
|
while (n--) {
|
|
|
|
h += *cp++;
|
|
|
|
h += h << 10;
|
|
|
|
h ^= h >> 6;
|
|
|
|
}
|
2007-08-12 15:42:23 +02:00
|
|
|
|
2009-10-17 23:16:05 +02:00
|
|
|
return (h);
|
2007-08-12 15:42:23 +02:00
|
|
|
}
|
|
|
|
|
2010-07-04 19:33:58 +02:00
|
|
|
static uint32_t
|
|
|
|
oaathash_finalise(register uint32_t h)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2009-09-26 05:40:03 +02:00
|
|
|
h += h << 3;
|
|
|
|
h ^= h >> 11;
|
|
|
|
h += h << 15;
|
2007-08-12 15:42:23 +02:00
|
|
|
|
2010-07-04 19:33:58 +02:00
|
|
|
return (h);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
oaathash_full(register const uint8_t *bp)
|
|
|
|
{
|
|
|
|
register uint32_t h = 0;
|
|
|
|
register uint8_t c;
|
|
|
|
|
|
|
|
while ((c = *bp++)) {
|
|
|
|
h += c;
|
|
|
|
h += h << 10;
|
|
|
|
h ^= h >> 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (oaathash_finalise(h));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
change_random(const void *vp, size_t n)
|
|
|
|
{
|
|
|
|
register uint32_t h = 0x100;
|
2010-07-11 13:17:33 +02:00
|
|
|
#if defined(__OpenBSD__)
|
|
|
|
int mib[2];
|
|
|
|
uint8_t k[3];
|
|
|
|
size_t klen;
|
|
|
|
#endif
|
2010-07-04 19:33:58 +02:00
|
|
|
|
|
|
|
kshstate_v.cr_dp = vp;
|
|
|
|
kshstate_v.cr_dsz = n;
|
|
|
|
gettimeofday(&kshstate_v.cr_tv, NULL);
|
|
|
|
h = oaathash_update(oaathash_update(h, (void *)&kshstate_v,
|
|
|
|
sizeof(kshstate_v)), vp, n);
|
|
|
|
kshstate_v.lcg_state_ = oaathash_finalise(h);
|
2010-07-11 13:17:33 +02:00
|
|
|
|
|
|
|
#if defined(__OpenBSD__)
|
|
|
|
/* OpenBSD, MirBSD: proper kernel entropy comes at zero cost */
|
|
|
|
|
|
|
|
mib[0] = CTL_KERN;
|
|
|
|
mib[1] = KERN_ARND;
|
|
|
|
klen = sizeof(k);
|
|
|
|
sysctl(mib, 2, k, &klen, &kshstate_v.lcg_state_,
|
|
|
|
sizeof(kshstate_v.lcg_state_));
|
|
|
|
/* we ignore failures and take in k anyway */
|
|
|
|
h = oaathash_update(h, k, sizeof(k));
|
|
|
|
kshstate_v.lcg_state_ = oaathash_finalise(h);
|
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* handle special variables with side effects - PATH, SECONDS.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Test if name is a special parameter */
|
|
|
|
static int
|
|
|
|
special(const char *name)
|
|
|
|
{
|
|
|
|
struct tbl *tp;
|
|
|
|
|
2009-08-28 22:30:59 +02:00
|
|
|
tp = ktsearch(&specials, name, hash(name));
|
2009-06-08 22:06:50 +02:00
|
|
|
return (tp && (tp->flag & ISSET) ? tp->type : V_NONE);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Make a variable non-special */
|
|
|
|
static void
|
|
|
|
unspecial(const char *name)
|
|
|
|
{
|
2009-08-28 22:30:59 +02:00
|
|
|
struct tbl *tp;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-08-28 22:30:59 +02:00
|
|
|
tp = ktsearch(&specials, name, hash(name));
|
|
|
|
if (tp)
|
|
|
|
ktdelete(tp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-08-28 21:57:43 +02:00
|
|
|
static time_t seconds; /* time SECONDS last set */
|
|
|
|
static int user_lineno; /* what user set $LINENO to */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
static void
|
|
|
|
getspec(struct tbl *vp)
|
|
|
|
{
|
2009-10-17 23:16:05 +02:00
|
|
|
register mksh_ari_t i;
|
|
|
|
int st;
|
2008-12-29 22:34:22 +01:00
|
|
|
|
2009-10-17 23:16:05 +02:00
|
|
|
switch ((st = special(vp->name))) {
|
2005-05-23 05:06:10 +02:00
|
|
|
case V_SECONDS:
|
2009-10-17 23:16:05 +02:00
|
|
|
/*
|
|
|
|
* On start up the value of SECONDS is used before
|
|
|
|
* it has been set - don't do anything in this case
|
2005-05-23 05:06:10 +02:00
|
|
|
* (see initcoms[] in main.c).
|
|
|
|
*/
|
2006-11-10 05:22:13 +01:00
|
|
|
if (vp->flag & ISSET) {
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
2009-10-17 23:16:05 +02:00
|
|
|
i = tv.tv_sec - seconds;
|
|
|
|
} else
|
|
|
|
return;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case V_RANDOM:
|
2009-10-17 23:16:05 +02:00
|
|
|
/*
|
|
|
|
* this is the same Linear Congruential PRNG as Borland
|
|
|
|
* C/C++ allegedly uses in its built-in rand() function
|
|
|
|
*/
|
2010-07-04 19:33:58 +02:00
|
|
|
i = ((kshstate_v.lcg_state_ =
|
|
|
|
22695477 * kshstate_v.lcg_state_ + 1) >> 16) & 0x7FFF;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case V_HISTSIZE:
|
2009-10-17 23:16:05 +02:00
|
|
|
i = histsize;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case V_OPTIND:
|
2009-10-17 23:16:05 +02:00
|
|
|
i = user_opt.uoptind;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case V_LINENO:
|
2009-10-17 23:16:05 +02:00
|
|
|
i = current_lineno + user_lineno;
|
2008-12-29 22:34:22 +01:00
|
|
|
break;
|
|
|
|
case V_COLUMNS:
|
|
|
|
case V_LINES:
|
2009-10-17 23:16:05 +02:00
|
|
|
/*
|
|
|
|
* Do NOT export COLUMNS/LINES. Many applications
|
2008-12-29 22:34:22 +01:00
|
|
|
* check COLUMNS/LINES before checking ws.ws_col/row,
|
|
|
|
* so if the app is started with C/L in the environ
|
|
|
|
* and the window is then resized, the app won't
|
|
|
|
* see the change cause the environ doesn't change.
|
|
|
|
*/
|
|
|
|
change_winsz();
|
2009-10-17 23:16:05 +02:00
|
|
|
i = st == V_COLUMNS ? x_cols : x_lins;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
2009-10-17 23:16:05 +02:00
|
|
|
default:
|
|
|
|
/* do nothing, do not touch vp at all */
|
|
|
|
return;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-10-17 23:16:05 +02:00
|
|
|
vp->flag &= ~SPECIAL;
|
|
|
|
setint(vp, i);
|
|
|
|
vp->flag |= SPECIAL;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
setspec(struct tbl *vp)
|
|
|
|
{
|
2009-10-17 23:16:05 +02:00
|
|
|
mksh_ari_t i;
|
2005-05-23 05:06:10 +02:00
|
|
|
char *s;
|
2009-10-17 23:16:05 +02:00
|
|
|
int st;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-10-17 23:16:05 +02:00
|
|
|
switch ((st = special(vp->name))) {
|
2005-05-23 05:06:10 +02:00
|
|
|
case V_PATH:
|
|
|
|
if (path)
|
|
|
|
afree(path, APERM);
|
2007-06-07 01:28:17 +02:00
|
|
|
s = str_val(vp);
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(path, s, APERM);
|
2005-05-23 05:06:10 +02:00
|
|
|
flushcom(1); /* clear tracked aliases */
|
2009-10-17 23:16:05 +02:00
|
|
|
return;
|
2005-05-23 05:06:10 +02:00
|
|
|
case V_IFS:
|
|
|
|
setctypes(s = str_val(vp), C_IFS);
|
|
|
|
ifs0 = *s;
|
2009-10-17 23:16:05 +02:00
|
|
|
return;
|
2005-05-23 05:06:10 +02:00
|
|
|
case V_TMPDIR:
|
|
|
|
if (tmpdir) {
|
|
|
|
afree(tmpdir, APERM);
|
|
|
|
tmpdir = NULL;
|
|
|
|
}
|
|
|
|
/* Use tmpdir iff it is an absolute path, is writable and
|
|
|
|
* searchable and is a directory...
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct stat statb;
|
|
|
|
|
|
|
|
s = str_val(vp);
|
2006-11-10 07:16:25 +01:00
|
|
|
if (s[0] == '/' && access(s, W_OK|X_OK) == 0 &&
|
2005-05-23 05:06:10 +02:00
|
|
|
stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(tmpdir, s, APERM);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
break;
|
2007-03-03 22:36:08 +01:00
|
|
|
#if HAVE_PERSISTENT_HISTORY
|
2005-05-23 05:06:10 +02:00
|
|
|
case V_HISTFILE:
|
|
|
|
sethistfile(str_val(vp));
|
|
|
|
break;
|
2006-11-10 06:23:14 +01:00
|
|
|
#endif
|
2009-10-17 23:16:05 +02:00
|
|
|
case V_TMOUT:
|
|
|
|
/* AT&T ksh seems to do this (only listen if integer) */
|
|
|
|
if (vp->flag & INTEGER)
|
|
|
|
ksh_tmout = vp->val.i >= 0 ? vp->val.i : 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* common sub-cases */
|
|
|
|
case V_OPTIND:
|
|
|
|
case V_HISTSIZE:
|
2005-05-23 05:06:10 +02:00
|
|
|
case V_COLUMNS:
|
2009-10-17 23:16:05 +02:00
|
|
|
case V_LINES:
|
|
|
|
case V_RANDOM:
|
|
|
|
case V_SECONDS:
|
|
|
|
case V_LINENO:
|
2008-12-29 22:34:22 +01:00
|
|
|
vp->flag &= ~SPECIAL;
|
2009-10-17 23:16:05 +02:00
|
|
|
i = intval(vp);
|
2008-12-29 22:34:22 +01:00
|
|
|
vp->flag |= SPECIAL;
|
|
|
|
break;
|
2009-10-17 23:16:05 +02:00
|
|
|
default:
|
|
|
|
/* do nothing, do not touch vp at all */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* process the singular parts of the common cases */
|
|
|
|
|
|
|
|
switch (st) {
|
|
|
|
case V_OPTIND:
|
|
|
|
getopts_reset((int)i);
|
|
|
|
break;
|
|
|
|
case V_HISTSIZE:
|
|
|
|
sethistsize((int)i);
|
|
|
|
break;
|
|
|
|
case V_COLUMNS:
|
|
|
|
if (i >= MIN_COLS)
|
|
|
|
x_cols = i;
|
|
|
|
break;
|
2008-12-29 22:34:22 +01:00
|
|
|
case V_LINES:
|
2009-10-17 23:16:05 +02:00
|
|
|
if (i >= MIN_LINS)
|
2008-12-29 22:34:22 +01:00
|
|
|
x_lins = i;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case V_RANDOM:
|
2009-10-17 23:16:05 +02:00
|
|
|
/*
|
2010-07-04 19:33:58 +02:00
|
|
|
* mksh R39d+ no longer has the traditional repeatability
|
2009-10-17 23:16:05 +02:00
|
|
|
* of $RANDOM sequences, but always retains state
|
|
|
|
*/
|
|
|
|
change_random(&i, sizeof(i));
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case V_SECONDS:
|
2006-11-10 05:22:13 +01:00
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
2009-10-17 23:16:05 +02:00
|
|
|
seconds = tv.tv_sec - i;
|
2006-11-10 05:22:13 +01:00
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case V_LINENO:
|
|
|
|
/* The -1 is because line numbering starts at 1. */
|
2009-10-17 23:16:05 +02:00
|
|
|
user_lineno = (unsigned int)i - current_lineno - 1;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
unsetspec(struct tbl *vp)
|
|
|
|
{
|
|
|
|
switch (special(vp->name)) {
|
|
|
|
case V_PATH:
|
|
|
|
if (path)
|
|
|
|
afree(path, APERM);
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(path, def_path, APERM);
|
2005-05-23 05:06:10 +02:00
|
|
|
flushcom(1); /* clear tracked aliases */
|
|
|
|
break;
|
|
|
|
case V_IFS:
|
|
|
|
setctypes(" \t\n", C_IFS);
|
|
|
|
ifs0 = ' ';
|
|
|
|
break;
|
|
|
|
case V_TMPDIR:
|
|
|
|
/* should not become unspecial */
|
|
|
|
if (tmpdir) {
|
|
|
|
afree(tmpdir, APERM);
|
|
|
|
tmpdir = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case V_LINENO:
|
|
|
|
case V_RANDOM:
|
|
|
|
case V_SECONDS:
|
2009-06-11 14:42:21 +02:00
|
|
|
case V_TMOUT: /* AT&T ksh leaves previous value in place */
|
2005-05-23 05:06:10 +02:00
|
|
|
unspecial(vp->name);
|
|
|
|
break;
|
|
|
|
|
2010-07-04 19:33:58 +02:00
|
|
|
/*
|
|
|
|
* AT&T ksh man page says OPTIND, OPTARG and _ lose special
|
|
|
|
* meaning, but OPTARG does not (still set by getopts) and _ is
|
|
|
|
* also still set in various places. Don't know what AT&T does
|
|
|
|
* for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not
|
|
|
|
* loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR
|
2009-06-10 20:12:51 +02:00
|
|
|
*/
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search for (and possibly create) a table entry starting with
|
|
|
|
* vp, indexed by val.
|
|
|
|
*/
|
|
|
|
static struct tbl *
|
2007-10-18 22:32:33 +02:00
|
|
|
arraysearch(struct tbl *vp, uint32_t val)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
struct tbl *prev, *curr, *new;
|
2009-08-01 22:32:45 +02:00
|
|
|
size_t len;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-09-06 19:42:15 +02:00
|
|
|
vp->flag = (vp->flag | (ARRAY|DEFINED)) & ~ASSOC;
|
2005-05-23 05:06:10 +02:00
|
|
|
/* The table entry is always [0] */
|
2006-05-27 00:17:21 +02:00
|
|
|
if (val == 0)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
prev = vp;
|
|
|
|
curr = vp->u.array;
|
2009-08-28 23:01:27 +02:00
|
|
|
while (curr && curr->ua.index < val) {
|
2005-05-23 05:06:10 +02:00
|
|
|
prev = curr;
|
|
|
|
curr = curr->u.array;
|
|
|
|
}
|
2009-08-28 23:01:27 +02:00
|
|
|
if (curr && curr->ua.index == val) {
|
2005-05-23 05:06:10 +02:00
|
|
|
if (curr->flag&ISSET)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (curr);
|
2009-08-01 22:32:45 +02:00
|
|
|
new = curr;
|
2005-05-23 05:06:10 +02:00
|
|
|
} else
|
2009-08-01 22:32:45 +02:00
|
|
|
new = NULL;
|
|
|
|
len = strlen(vp->name) + 1;
|
2009-08-28 23:01:27 +02:00
|
|
|
if (!new) {
|
2009-08-01 22:32:45 +02:00
|
|
|
new = alloc(offsetof(struct tbl, name[0]) + len, vp->areap);
|
2009-08-28 23:01:27 +02:00
|
|
|
memcpy(new->name, vp->name, len);
|
|
|
|
}
|
|
|
|
new->flag = (vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL)) | AINDEX;
|
2005-05-23 05:06:10 +02:00
|
|
|
new->type = vp->type;
|
|
|
|
new->areap = vp->areap;
|
|
|
|
new->u2.field = vp->u2.field;
|
2009-08-28 23:01:27 +02:00
|
|
|
new->ua.index = val;
|
2009-08-28 22:30:59 +02:00
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
if (curr != new) { /* not reusing old array entry */
|
|
|
|
prev->u.array = new;
|
|
|
|
new->u.array = curr;
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (new);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the length of an array reference (eg, [1+2]) - cp is assumed
|
2009-06-10 20:12:51 +02:00
|
|
|
* to point to the open bracket. Returns 0 if there is no matching closing
|
2005-05-23 05:06:10 +02:00
|
|
|
* bracket.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
array_ref_len(const char *cp)
|
|
|
|
{
|
|
|
|
const char *s = cp;
|
|
|
|
int c;
|
|
|
|
int depth = 0;
|
|
|
|
|
|
|
|
while ((c = *s++) && (c != ']' || --depth))
|
|
|
|
if (c == '[')
|
|
|
|
depth++;
|
|
|
|
if (!c)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
|
|
|
return (s - cp);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a copy of the base of an array name
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
arrayname(const char *str)
|
|
|
|
{
|
|
|
|
const char *p;
|
2008-10-28 15:32:43 +01:00
|
|
|
char *rv;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
if ((p = cstrchr(str, '[')) == 0)
|
2005-05-23 05:06:10 +02:00
|
|
|
/* Shouldn't happen, but why worry? */
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(rv, str, ATEMP);
|
|
|
|
else
|
|
|
|
strndupx(rv, str, p - str, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2008-10-28 15:32:43 +01:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-08-28 23:07:27 +02:00
|
|
|
/* set (or overwrite, if reset) the array variable var to the values in vals */
|
2009-08-29 00:23:34 +02:00
|
|
|
mksh_uari_t
|
2009-08-28 23:04:18 +02:00
|
|
|
set_array(const char *var, bool reset, const char **vals)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
struct tbl *vp, *vq;
|
2009-12-01 20:15:35 +01:00
|
|
|
mksh_uari_t i;
|
2009-08-29 00:44:47 +02:00
|
|
|
const char *ccp;
|
2009-09-23 20:04:58 +02:00
|
|
|
#ifndef MKSH_SMALL
|
2009-08-29 00:44:47 +02:00
|
|
|
char *cp;
|
2009-12-01 20:15:35 +01:00
|
|
|
mksh_uari_t j;
|
2009-09-23 20:04:58 +02:00
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* to get local array, use "typeset foo; set -A foo" */
|
|
|
|
vp = global(var);
|
|
|
|
|
2009-06-11 14:42:21 +02:00
|
|
|
/* Note: AT&T ksh allows set -A but not set +A of a read-only var */
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&RDONLY))
|
|
|
|
errorf("%s: is read only", var);
|
|
|
|
/* This code is quite non-optimal */
|
2009-08-28 23:04:18 +02:00
|
|
|
if (reset)
|
2005-05-23 05:06:10 +02:00
|
|
|
/* trash existing values and attributes */
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
unset(vp, 1);
|
2005-05-23 05:06:10 +02:00
|
|
|
/* todo: would be nice for assignment to completely succeed or
|
2009-06-10 20:12:51 +02:00
|
|
|
* completely fail. Only really effects integer arrays:
|
2005-05-23 05:06:10 +02:00
|
|
|
* evaluation of some of vals[] may fail...
|
|
|
|
*/
|
2009-12-01 20:15:35 +01:00
|
|
|
i = 0;
|
|
|
|
#ifndef MKSH_SMALL
|
|
|
|
j = 0;
|
|
|
|
#else
|
|
|
|
#define j i
|
|
|
|
#endif
|
|
|
|
while ((ccp = vals[i])) {
|
2009-09-23 20:04:58 +02:00
|
|
|
#ifndef MKSH_SMALL
|
2009-08-29 00:44:47 +02:00
|
|
|
if (*ccp == '[') {
|
|
|
|
int level = 0;
|
|
|
|
|
|
|
|
while (*ccp) {
|
|
|
|
if (*ccp == ']' && --level == 0)
|
|
|
|
break;
|
|
|
|
if (*ccp == '[')
|
|
|
|
++level;
|
|
|
|
++ccp;
|
|
|
|
}
|
|
|
|
if (*ccp == ']' && level == 0 && ccp[1] == '=') {
|
|
|
|
strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
|
|
|
|
ATEMP);
|
2009-12-01 20:15:35 +01:00
|
|
|
evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
|
2009-08-29 00:44:47 +02:00
|
|
|
KSH_UNWIND_ERROR, true);
|
2009-12-01 20:15:35 +01:00
|
|
|
afree(cp, ATEMP);
|
2009-08-29 00:44:47 +02:00
|
|
|
ccp += 2;
|
|
|
|
} else
|
|
|
|
ccp = vals[i];
|
|
|
|
}
|
2009-09-23 20:04:58 +02:00
|
|
|
#endif
|
2009-08-29 00:44:47 +02:00
|
|
|
|
2009-12-01 20:15:35 +01:00
|
|
|
vq = arraysearch(vp, j);
|
2005-05-23 05:06:10 +02:00
|
|
|
/* would be nice to deal with errors here... (see above) */
|
2009-08-29 00:44:47 +02:00
|
|
|
setstr(vq, ccp, KSH_RETURN_ERROR);
|
2009-12-01 20:15:35 +01:00
|
|
|
i++;
|
|
|
|
#ifndef MKSH_SMALL
|
|
|
|
j++;
|
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-08-28 23:07:27 +02:00
|
|
|
|
|
|
|
return (i);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-12-29 22:05:15 +01:00
|
|
|
|
|
|
|
void
|
|
|
|
change_winsz(void)
|
|
|
|
{
|
|
|
|
if (x_lins < 0) {
|
|
|
|
/* first time initialisation */
|
|
|
|
#ifdef TIOCGWINSZ
|
|
|
|
if (tty_fd < 0)
|
|
|
|
/* non-FTALKING, try to get an fd anyway */
|
|
|
|
tty_init(false, false);
|
|
|
|
#endif
|
|
|
|
x_cols = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TIOCGWINSZ
|
|
|
|
/* check if window size has changed since first time */
|
|
|
|
if (tty_fd >= 0) {
|
|
|
|
struct winsize ws;
|
|
|
|
|
|
|
|
if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
|
|
|
|
if (ws.ws_col)
|
|
|
|
x_cols = ws.ws_col;
|
|
|
|
if (ws.ws_row)
|
|
|
|
x_lins = ws.ws_row;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* bounds check for sane values, use defaults otherwise */
|
|
|
|
if (x_cols < MIN_COLS)
|
|
|
|
x_cols = 80;
|
|
|
|
if (x_lins < MIN_LINS)
|
|
|
|
x_lins = 24;
|
2010-07-04 19:45:17 +02:00
|
|
|
|
|
|
|
#ifdef SIGWINCH
|
|
|
|
got_winch = 0;
|
|
|
|
#endif
|
2008-12-29 22:05:15 +01:00
|
|
|
}
|
2010-07-04 19:33:58 +02:00
|
|
|
|
|
|
|
uint32_t
|
|
|
|
evilhash(const char *s)
|
|
|
|
{
|
|
|
|
register uint32_t h = 0x100;
|
|
|
|
|
|
|
|
h = oaathash_update(h, (void *)&kshstate_f, sizeof(kshstate_f));
|
|
|
|
kshstate_f.h = oaathash_full((const uint8_t *)s);
|
|
|
|
return (oaathash_finalise(oaathash_update(h,
|
|
|
|
(void *)&kshstate_f.h, sizeof(kshstate_f.h))));
|
|
|
|
}
|