2015-10-09 18:11:19 +02:00
|
|
|
|
/* $OpenBSD: var.c,v 1.44 2015/09/10 11:37:42 jca Exp $ */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2009-05-16 18:59:42 +02:00
|
|
|
|
/*-
|
2012-03-24 00:25:30 +01:00
|
|
|
|
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
2017-03-26 01:10:26 +01:00
|
|
|
|
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
2015-10-09 18:11:19 +02:00
|
|
|
|
* mirabilos <m@mirbsd.org>
|
2009-05-16 18:59:42 +02:00
|
|
|
|
*
|
|
|
|
|
* 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"
|
2014-01-11 19:09:43 +01:00
|
|
|
|
#include "mirhash.h"
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2010-07-11 13:17:33 +02:00
|
|
|
|
#if defined(__OpenBSD__)
|
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-04-30 00:04:31 +02:00
|
|
|
|
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.217 2017/04/29 22:04:31 tg Exp $");
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/*-
|
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.
|
|
|
|
|
*/
|
2011-06-05 21:58:21 +02:00
|
|
|
|
|
2006-01-29 21:10:16 +01:00
|
|
|
|
static struct table specials;
|
2014-01-11 19:09:43 +01:00
|
|
|
|
static uint32_t lcg_state = 5381, qh_state = 4711;
|
2014-05-27 15:22:46 +02:00
|
|
|
|
/* may only be set by typeset() just before call to array_index_calc() */
|
|
|
|
|
static enum namerefflag innermost_refflag = SRF_NOP;
|
2011-01-21 22:04:48 +01:00
|
|
|
|
|
2017-04-02 18:25:23 +02:00
|
|
|
|
static void c_typeset_vardump(struct tbl *, uint32_t, int, int, bool, bool);
|
2017-04-02 17:51:20 +02:00
|
|
|
|
static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
|
|
|
|
|
bool);
|
2009-04-07 21:06:44 +02:00
|
|
|
|
static char *formatstr(struct tbl *, const char *);
|
2010-07-18 00:09:40 +02:00
|
|
|
|
static void exportprep(struct tbl *, const char *);
|
2009-04-07 21:06:44 +02:00
|
|
|
|
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 *);
|
2013-04-01 04:37:53 +02:00
|
|
|
|
static int getint(struct tbl *, mksh_ari_u *, bool);
|
2007-10-18 22:32:33 +02:00
|
|
|
|
static const char *array_index_calc(const char *, bool *, uint32_t *);
|
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;
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* TODO: could use e->area (l->area => l->areap) */
|
|
|
|
|
ainit(&l->area);
|
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;
|
2012-07-01 17:38:09 +02:00
|
|
|
|
ktinit(&l->area, &l->vars, 0);
|
|
|
|
|
ktinit(&l->area, &l->funs, 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)
|
|
|
|
|
{
|
2011-06-05 21:58:21 +02:00
|
|
|
|
ssize_t i;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
struct block *l = e->loc;
|
|
|
|
|
struct tbl *vp, **vpp = l->vars.tbls, *vq;
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* pop block */
|
|
|
|
|
e->loc = l->next;
|
|
|
|
|
|
|
|
|
|
i = 1 << (l->vars.tshift);
|
|
|
|
|
while (--i >= 0)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* this is biased with -1 relative to VARSPEC_ENUMS */
|
2009-09-26 05:40:03 +02:00
|
|
|
|
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;
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
ktinit(APERM, &specials,
|
2016-11-12 00:31:39 +01:00
|
|
|
|
/* currently 18 specials: 75% of 32 = 2^5 */
|
2012-11-20 18:42:32 +01:00
|
|
|
|
5);
|
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
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-10 01:21:00 +02:00
|
|
|
|
/* common code for several functions below and c_typeset() */
|
|
|
|
|
struct block *
|
2011-06-21 23:08:50 +02:00
|
|
|
|
varsearch(struct block *l, struct tbl **vpp, const char *vn, uint32_t h)
|
|
|
|
|
{
|
|
|
|
|
register struct tbl *vp;
|
|
|
|
|
|
|
|
|
|
if (l) {
|
|
|
|
|
varsearch_loop:
|
|
|
|
|
if ((vp = ktsearch(&l->vars, vn, h)) != NULL)
|
|
|
|
|
goto varsearch_out;
|
|
|
|
|
if (l->next != NULL) {
|
|
|
|
|
l = l->next;
|
|
|
|
|
goto varsearch_loop;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
vp = NULL;
|
|
|
|
|
varsearch_out:
|
|
|
|
|
*vpp = vp;
|
|
|
|
|
return (l);
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/*
|
|
|
|
|
* Used to calculate an array index for global()/local(). Sets *arrayp
|
|
|
|
|
* to true if this is an array, sets *valp to the array index, returns
|
2014-05-27 15:22:46 +02:00
|
|
|
|
* the basename of the array. May only be called from global()/local()
|
|
|
|
|
* and must be their first callee.
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
|
|
|
|
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;
|
2011-08-27 20:06:52 +02:00
|
|
|
|
size_t 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);
|
2017-04-27 21:33:53 +02:00
|
|
|
|
if (innermost_refflag == SRF_NOP && (p != n) && ctype(n[0], C_ALPHX)) {
|
2009-09-06 19:42:15 +02:00
|
|
|
|
struct tbl *vp;
|
|
|
|
|
char *vn;
|
|
|
|
|
|
|
|
|
|
strndupx(vn, n, p - n, ATEMP);
|
|
|
|
|
/* check if this is a reference */
|
2011-06-21 23:08:50 +02:00
|
|
|
|
varsearch(e->loc, &vp, vn, hash(vn));
|
2009-09-06 19:42:15 +02:00
|
|
|
|
afree(vn, ATEMP);
|
2014-05-27 15:22:46 +02:00
|
|
|
|
if (vp && (vp->flag & (DEFINED | ASSOC | ARRAY)) ==
|
|
|
|
|
(DEFINED | ASSOC)) {
|
2009-09-06 19:42:15 +02:00
|
|
|
|
char *cp;
|
|
|
|
|
|
|
|
|
|
/* gotcha! */
|
2016-07-25 02:04:48 +02:00
|
|
|
|
cp = shf_smprintf(Tf_ss, str_val(vp), p);
|
2009-09-06 19:42:15 +02:00
|
|
|
|
afree(ap, ATEMP);
|
|
|
|
|
n = ap = cp;
|
|
|
|
|
goto redo_from_ref;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-27 15:22:46 +02:00
|
|
|
|
innermost_refflag = SRF_NOP;
|
2009-09-06 19:42:15 +02:00
|
|
|
|
|
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
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* calculate the value of the subscript */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*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
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 21:21:39 +01:00
|
|
|
|
#define vn vname.ro
|
2005-05-23 05:06:10 +02:00
|
|
|
|
/*
|
|
|
|
|
* Search for variable, if not found create globally.
|
|
|
|
|
*/
|
|
|
|
|
struct tbl *
|
|
|
|
|
global(const char *n)
|
2017-04-02 18:07:04 +02:00
|
|
|
|
{
|
|
|
|
|
return (isglobal(n, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* search for variable; if not found, return NULL or create globally */
|
|
|
|
|
struct tbl *
|
|
|
|
|
isglobal(const char *n, bool docreate)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
{
|
|
|
|
|
struct tbl *vp;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
union mksh_cchack vname;
|
|
|
|
|
struct block *l = e->loc;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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
|
|
|
|
|
2014-05-27 15:22:46 +02:00
|
|
|
|
/*
|
|
|
|
|
* check to see if this is an array;
|
|
|
|
|
* dereference namerefs; must come first
|
|
|
|
|
*/
|
2016-01-14 21:21:39 +01:00
|
|
|
|
vn = array_index_calc(n, &array, &val);
|
|
|
|
|
h = hash(vn);
|
|
|
|
|
c = (unsigned char)vn[0];
|
2017-04-27 21:33:53 +02:00
|
|
|
|
if (!ctype(c, C_ALPHX)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if (array)
|
2016-01-21 19:24:45 +01:00
|
|
|
|
errorf(Tbadsubst);
|
2016-02-26 19:48:14 +01:00
|
|
|
|
vp = vtemp;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
vp->flag = DEFINED;
|
|
|
|
|
vp->type = 0;
|
|
|
|
|
vp->areap = ATEMP;
|
2017-04-27 21:33:53 +02:00
|
|
|
|
if (ctype(c, C_DIGIT)) {
|
2016-02-26 19:48:14 +01:00
|
|
|
|
if (getn(vn, &c)) {
|
|
|
|
|
/* main.c:main_init() says 12 */
|
2016-07-25 02:04:48 +02:00
|
|
|
|
shf_snprintf(vp->name, 12, Tf_d, c);
|
2016-02-26 19:48:14 +01:00
|
|
|
|
if (c <= l->argc) {
|
|
|
|
|
/* setstr can't fail here */
|
|
|
|
|
setstr(vp, l->argv[c],
|
|
|
|
|
KSH_RETURN_ERROR);
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
vp->name[0] = '\0';
|
2005-05-23 05:06:10 +02:00
|
|
|
|
vp->flag |= RDONLY;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
goto out;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
2016-02-26 19:48:14 +01:00
|
|
|
|
vp->name[0] = c;
|
|
|
|
|
vp->name[1] = '\0';
|
2005-05-23 05:06:10 +02:00
|
|
|
|
vp->flag |= RDONLY;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
if (vn[1] != '\0')
|
|
|
|
|
goto out;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
vp->flag |= ISSET|INTEGER;
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '$':
|
|
|
|
|
vp->val.i = kshpid;
|
|
|
|
|
break;
|
|
|
|
|
case '!':
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* if no job, expand to nothing */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if ((vp->val.i = j_async()) == 0)
|
|
|
|
|
vp->flag &= ~(ISSET|INTEGER);
|
|
|
|
|
break;
|
|
|
|
|
case '?':
|
2012-10-21 23:39:06 +02:00
|
|
|
|
vp->val.i = exstat & 0xFF;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
break;
|
|
|
|
|
case '#':
|
|
|
|
|
vp->val.i = l->argc;
|
|
|
|
|
break;
|
|
|
|
|
case '-':
|
|
|
|
|
vp->flag &= ~INTEGER;
|
|
|
|
|
vp->val.s = getoptions();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
vp->flag &= ~(ISSET|INTEGER);
|
|
|
|
|
}
|
2016-01-14 21:21:39 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
l = varsearch(e->loc, &vp, vn, h);
|
2017-04-02 18:07:04 +02:00
|
|
|
|
if (vp == NULL && docreate)
|
|
|
|
|
vp = ktenter(&l->vars, vn, h);
|
|
|
|
|
else
|
|
|
|
|
docreate = false;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
if (vp != NULL) {
|
|
|
|
|
if (array)
|
|
|
|
|
vp = arraysearch(vp, val);
|
2017-04-02 18:07:04 +02:00
|
|
|
|
if (docreate) {
|
|
|
|
|
vp->flag |= DEFINED;
|
|
|
|
|
if (special(vn))
|
|
|
|
|
vp->flag |= SPECIAL;
|
|
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
2016-01-14 21:21:39 +01:00
|
|
|
|
out:
|
2016-01-14 23:49:33 +01:00
|
|
|
|
last_lookup_was_array = array;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
if (vn != n)
|
|
|
|
|
afree(vname.rw, ATEMP);
|
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 tbl *vp;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
union mksh_cchack vname;
|
|
|
|
|
struct block *l = e->loc;
|
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
|
|
|
|
|
2014-05-27 15:22:46 +02:00
|
|
|
|
/*
|
|
|
|
|
* check to see if this is an array;
|
|
|
|
|
* dereference namerefs; must come first
|
|
|
|
|
*/
|
2016-01-14 21:21:39 +01:00
|
|
|
|
vn = array_index_calc(n, &array, &val);
|
|
|
|
|
h = hash(vn);
|
2017-04-27 21:33:53 +02:00
|
|
|
|
if (!ctype(*vn, C_ALPHX)) {
|
2016-02-26 19:48:14 +01:00
|
|
|
|
vp = vtemp;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
vp->flag = DEFINED|RDONLY;
|
|
|
|
|
vp->type = 0;
|
|
|
|
|
vp->areap = ATEMP;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
goto out;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
2016-01-14 21:21:39 +01:00
|
|
|
|
vp = ktenter(&l->vars, vn, h);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if (copy && !(vp->flag & DEFINED)) {
|
2011-06-21 23:08:50 +02:00
|
|
|
|
struct tbl *vq;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2016-01-14 21:21:39 +01:00
|
|
|
|
varsearch(l->next, &vq, vn, h);
|
2011-06-21 23:08:50 +02:00
|
|
|
|
if (vq != NULL) {
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
if (special(vn))
|
2005-05-23 05:06:10 +02:00
|
|
|
|
vp->flag |= SPECIAL;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
out:
|
2016-01-14 23:49:33 +01:00
|
|
|
|
last_lookup_was_array = array;
|
2016-01-14 21:21:39 +01:00
|
|
|
|
if (vn != n)
|
|
|
|
|
afree(vname.rw, ATEMP);
|
2009-06-08 22:06:50 +02:00
|
|
|
|
return (vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
2016-01-14 21:21:39 +01:00
|
|
|
|
#undef vn
|
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))
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* special to dollar() */
|
|
|
|
|
s = null;
|
|
|
|
|
else if (!(vp->flag&INTEGER))
|
|
|
|
|
/* string source */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
s = vp->val.s + vp->type;
|
2011-06-05 21:58:21 +02:00
|
|
|
|
else {
|
|
|
|
|
/* integer source */
|
|
|
|
|
mksh_uari_t n;
|
2013-05-02 22:23:09 +02:00
|
|
|
|
unsigned int base;
|
2011-06-05 21:58:21 +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;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
|
|
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
|
2013-05-02 22:23:09 +02:00
|
|
|
|
n = (vp->val.i < 0) ? -vp->val.u : vp->val.u;
|
|
|
|
|
base = (vp->type == 0) ? 10U : (unsigned int)vp->type;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2011-05-29 04:18:57 +02:00
|
|
|
|
if (base == 1 && n == 0)
|
|
|
|
|
base = 2;
|
• 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
|
|
|
|
}
|
2011-06-05 21:58:21 +02:00
|
|
|
|
if (vp->flag & (RJUST|LJUST))
|
|
|
|
|
/* case already dealt with */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
2012-11-30 20:02:10 +01:00
|
|
|
|
bool no_ro_check = tobool(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) {
|
2016-07-25 02:04:48 +02:00
|
|
|
|
warningf(true, Tf_ro, vq->name);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if (!error_ok)
|
2011-03-27 20:50:06 +02:00
|
|
|
|
errorfxz(2);
|
2009-06-08 22:06:50 +02:00
|
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
2011-06-05 21:58:21 +02:00
|
|
|
|
if (!(vq->flag&INTEGER)) {
|
|
|
|
|
/* string dest */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if ((vq->flag&ALLOC)) {
|
2012-11-20 19:06:53 +01:00
|
|
|
|
#ifndef MKSH_SMALL
|
2005-05-23 05:06:10 +02:00
|
|
|
|
/* debugging */
|
|
|
|
|
if (s >= vq->val.s &&
|
2017-04-30 00:04:31 +02:00
|
|
|
|
s <= strnul(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);
|
2012-11-20 19:06:53 +01:00
|
|
|
|
}
|
|
|
|
|
#endif
|
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))
|
2010-07-18 00:09:40 +02:00
|
|
|
|
exportprep(vq, s);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2011-06-05 21:58:21 +02:00
|
|
|
|
} else {
|
|
|
|
|
/* integer dest */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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)) {
|
2016-02-26 19:48:14 +01:00
|
|
|
|
vtemp->flag = (ISSET|INTEGER);
|
|
|
|
|
vtemp->type = 0;
|
|
|
|
|
vtemp->areap = ATEMP;
|
|
|
|
|
vtemp->val.i = n;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
/* setstr can't fail here */
|
2016-02-26 19:48:14 +01:00
|
|
|
|
setstr(vq, str_val(vtemp), KSH_RETURN_ERROR);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
} else
|
|
|
|
|
vq->val.i = n;
|
|
|
|
|
vq->flag |= ISSET;
|
|
|
|
|
if ((vq->flag&SPECIAL))
|
|
|
|
|
setspec(vq);
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-26 05:40:03 +02:00
|
|
|
|
static int
|
2013-04-01 04:37:53 +02:00
|
|
|
|
getint(struct tbl *vp, mksh_ari_u *nump, bool arith)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
{
|
2015-04-19 20:13:31 +02:00
|
|
|
|
mksh_uari_t c, num = 0, base = 10;
|
• finally, the code and manual page text to deprecate, and code to not
handle any more, octal 010 style constants, as promised
• overhaul the manpage re. arithmetic expressions, make the guarantees
mksh code has explicitly, precisely, clear
• to reduce burden of the compiler, getint() now operates on mksh_uari_t
internally; it already applied the sign after operation, anyway (C99
guarantees wraparound on unsigned types, but for signed types we need
specific compiler support; apparently, this comes from hardware limits)
• use const and shuffle order of locals around while here
2011-12-10 14:34:19 +01:00
|
|
|
|
const char *s;
|
2013-04-01 04:37:53 +02:00
|
|
|
|
bool have_base = false, neg = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2015-04-19 20:13:31 +02:00
|
|
|
|
if (vp->flag & SPECIAL)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
getspec(vp);
|
2013-03-31 20:30:05 +02:00
|
|
|
|
/* XXX is it possible for ISSET to be set and val.s to be NULL? */
|
2015-04-19 20:13:31 +02:00
|
|
|
|
if (!(vp->flag & ISSET) || (!(vp->flag & INTEGER) && vp->val.s == NULL))
|
2009-06-08 22:06:50 +02:00
|
|
|
|
return (-1);
|
2015-04-19 20:13:31 +02:00
|
|
|
|
if (vp->flag & INTEGER) {
|
2013-04-01 04:37:53 +02:00
|
|
|
|
nump->i = 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;
|
2015-04-19 20:13:31 +02:00
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
c = (unsigned char)*s++;
|
2017-04-27 21:33:53 +02:00
|
|
|
|
} while (ctype(c, C_SPACE));
|
2015-04-19 20:13:31 +02:00
|
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '-':
|
|
|
|
|
neg = true;
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
case '+':
|
|
|
|
|
c = (unsigned char)*s++;
|
|
|
|
|
break;
|
• finally, the code and manual page text to deprecate, and code to not
handle any more, octal 010 style constants, as promised
• overhaul the manpage re. arithmetic expressions, make the guarantees
mksh code has explicitly, precisely, clear
• to reduce burden of the compiler, getint() now operates on mksh_uari_t
internally; it already applied the sign after operation, anyway (C99
guarantees wraparound on unsigned types, but for signed types we need
specific compiler support; apparently, this comes from hardware limits)
• use const and shuffle order of locals around while here
2011-12-10 14:34:19 +01:00
|
|
|
|
}
|
2015-04-19 20:13:31 +02:00
|
|
|
|
|
|
|
|
|
if (c == '0' && arith) {
|
2015-04-29 20:38:54 +02:00
|
|
|
|
if (ksh_eq(s[0], 'X', 'x')) {
|
2015-04-19 20:13:31 +02:00
|
|
|
|
/* interpret as hexadecimal */
|
|
|
|
|
base = 16;
|
|
|
|
|
++s;
|
|
|
|
|
goto getint_c_style_base;
|
2017-04-27 21:33:53 +02:00
|
|
|
|
} else if (Flag(FPOSIX) && ctype(s[0], C_DIGIT) &&
|
2015-04-19 20:13:31 +02:00
|
|
|
|
!(vp->flag & ZEROFIL)) {
|
|
|
|
|
/* interpret as octal (deprecated) */
|
|
|
|
|
base = 8;
|
|
|
|
|
getint_c_style_base:
|
|
|
|
|
have_base = true;
|
|
|
|
|
c = (unsigned char)*s++;
|
|
|
|
|
}
|
2012-06-25 18:31:18 +02:00
|
|
|
|
}
|
2015-04-19 20:13:31 +02:00
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
if (c == '#') {
|
|
|
|
|
/* ksh-style base determination */
|
2014-12-16 00:18:47 +01:00
|
|
|
|
if (have_base || num < 1)
|
• 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);
|
2013-05-02 22:23:09 +02:00
|
|
|
|
if ((base = num) == 1) {
|
2015-04-19 20:13:31 +02:00
|
|
|
|
/* mksh-specific extension */
|
• 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
|
|
|
|
unsigned int wc;
|
|
|
|
|
|
2008-12-04 19:11:08 +01:00
|
|
|
|
if (!UTFMODE)
|
• finally, the code and manual page text to deprecate, and code to not
handle any more, octal 010 style constants, as promised
• overhaul the manpage re. arithmetic expressions, make the guarantees
mksh code has explicitly, precisely, clear
• to reduce burden of the compiler, getint() now operates on mksh_uari_t
internally; it already applied the sign after operation, anyway (C99
guarantees wraparound on unsigned types, but for signed types we need
specific compiler support; apparently, this comes from hardware limits)
• use const and shuffle order of locals around while here
2011-12-10 14:34:19 +01:00
|
|
|
|
wc = *(const unsigned 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
|
|
|
|
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)
|
|
|
|
|
*/
|
• finally, the code and manual page text to deprecate, and code to not
handle any more, octal 010 style constants, as promised
• overhaul the manpage re. arithmetic expressions, make the guarantees
mksh code has explicitly, precisely, clear
• to reduce burden of the compiler, getint() now operates on mksh_uari_t
internally; it already applied the sign after operation, anyway (C99
guarantees wraparound on unsigned types, but for signed types we need
specific compiler support; apparently, this comes from hardware limits)
• use const and shuffle order of locals around while here
2011-12-10 14:34:19 +01:00
|
|
|
|
wc = 0xEF00 + *(const unsigned char *)s;
|
2013-04-01 04:37:53 +02:00
|
|
|
|
nump->u = (mksh_uari_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);
|
2014-12-16 00:18:47 +01:00
|
|
|
|
} else if (base > 36)
|
|
|
|
|
base = 10;
|
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;
|
2015-04-19 20:13:31 +02:00
|
|
|
|
}
|
2017-04-27 21:33:53 +02:00
|
|
|
|
if (ctype(c, C_DIGIT))
|
2015-04-29 22:07:35 +02:00
|
|
|
|
c = ksh_numdig(c);
|
2017-04-27 21:33:53 +02:00
|
|
|
|
else if (ctype(c, C_UPPER))
|
2015-04-29 22:07:35 +02:00
|
|
|
|
c = ksh_numuc(c) + 10;
|
2017-04-27 21:33:53 +02:00
|
|
|
|
else if (ctype(c, C_LOWER))
|
2015-04-29 22:07:35 +02:00
|
|
|
|
c = ksh_numlc(c) + 10;
|
2015-04-29 20:38:54 +02:00
|
|
|
|
else
|
|
|
|
|
return (-1);
|
2013-05-02 22:23:09 +02:00
|
|
|
|
if (c >= base)
|
2009-06-08 22:06:50 +02:00
|
|
|
|
return (-1);
|
2015-04-19 20:13:31 +02:00
|
|
|
|
/* handle overflow as truncation */
|
2006-11-10 08:52:04 +01:00
|
|
|
|
num = num * base + c;
|
2015-04-19 20:13:31 +02:00
|
|
|
|
} while ((c = (unsigned char)*s++));
|
|
|
|
|
|
2013-04-01 04:37:53 +02:00
|
|
|
|
if (neg)
|
|
|
|
|
num = -num;
|
|
|
|
|
nump->u = num;
|
2009-06-08 22:06:50 +02:00
|
|
|
|
return (base);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/*
|
|
|
|
|
* convert variable vq to integer variable, setting its value from vp
|
2005-05-23 05:06:10 +02:00
|
|
|
|
* (vq and vp may be the same)
|
|
|
|
|
*/
|
|
|
|
|
struct tbl *
|
|
|
|
|
setint_v(struct tbl *vq, struct tbl *vp, bool arith)
|
|
|
|
|
{
|
|
|
|
|
int base;
|
2013-04-01 04:37:53 +02:00
|
|
|
|
mksh_ari_u 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);
|
2013-04-01 04:37:53 +02:00
|
|
|
|
setint_n(vq, num.i, 0);
|
2011-07-02 19:57:41 +02:00
|
|
|
|
if (vq->type == 0)
|
|
|
|
|
/* default base */
|
|
|
|
|
vq->type = base;
|
|
|
|
|
return (vq);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* convert variable vq to integer variable, setting its value to num */
|
|
|
|
|
void
|
2011-11-08 23:07:15 +01:00
|
|
|
|
setint_n(struct tbl *vq, mksh_ari_t num, int newbase)
|
2011-07-02 19:57:41 +02:00
|
|
|
|
{
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
|
|
|
|
|
vq->flag &= ~ALLOC;
|
2011-05-29 04:18:57 +02:00
|
|
|
|
vq->type = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
afree(vq->val.s, vq->areap);
|
|
|
|
|
}
|
|
|
|
|
vq->val.i = num;
|
2011-11-08 23:07:15 +01:00
|
|
|
|
if (newbase != 0)
|
|
|
|
|
vq->type = newbase;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
vq->flag |= ISSET|INTEGER;
|
|
|
|
|
if (vq->flag&SPECIAL)
|
|
|
|
|
setspec(vq);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2011-08-27 20:06:52 +02:00
|
|
|
|
olen = (int)utf_mbswidth(s);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
|
|
if (vp->flag & (RJUST|LJUST)) {
|
2011-06-05 21:58:21 +02:00
|
|
|
|
if (!vp->u2.field)
|
|
|
|
|
/* default field width */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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)) {
|
2016-05-06 00:56:15 +02:00
|
|
|
|
int slen = olen;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
|
|
if (vp->flag & RJUST) {
|
2016-05-06 00:56:15 +02:00
|
|
|
|
const char *qq;
|
2008-04-19 23:04:09 +02:00
|
|
|
|
int n = 0;
|
2008-04-19 19:21:55 +02:00
|
|
|
|
|
2016-05-06 00:56:15 +02:00
|
|
|
|
qq = utf_skipcols(s, slen, &slen);
|
|
|
|
|
|
2009-06-11 14:42:21 +02:00
|
|
|
|
/* strip trailing spaces (AT&T uses qq[-1] == ' ') */
|
2017-04-27 21:33:53 +02:00
|
|
|
|
while (qq > s && ctype(qq[-1], C_SPACE)) {
|
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) {
|
2012-12-04 02:11:17 +01:00
|
|
|
|
if (!s[0] || !s[1])
|
|
|
|
|
goto uhm_no;
|
2008-04-19 23:04:09 +02:00
|
|
|
|
if (s[1] == '#')
|
|
|
|
|
n = 2;
|
|
|
|
|
else if (s[2] == '#')
|
|
|
|
|
n = 3;
|
2012-12-04 02:11:17 +01:00
|
|
|
|
uhm_no:
|
2008-04-19 23:04:09 +02:00
|
|
|
|
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 */
|
2017-04-27 21:33:53 +02:00
|
|
|
|
while (ctype(*s, C_SPACE))
|
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
|
2010-07-18 00:09:40 +02:00
|
|
|
|
exportprep(struct tbl *vp, const char *val)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
{
|
|
|
|
|
char *xp;
|
|
|
|
|
char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
|
2010-09-14 23:26:19 +02:00
|
|
|
|
size_t namelen, vallen;
|
|
|
|
|
|
|
|
|
|
namelen = strlen(vp->name);
|
|
|
|
|
vallen = strlen(val) + 1;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
|
|
vp->flag |= ALLOC;
|
2010-09-14 23:26:19 +02:00
|
|
|
|
/* since name+val are both in memory this can go unchecked */
|
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++ = '=';
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* offset to value */
|
|
|
|
|
vp->type = xp - vp->val.s;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
memcpy(xp, val, vallen);
|
2015-09-05 21:19:12 +02:00
|
|
|
|
afree(op, vp->areap);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2011-06-05 21:58:21 +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.
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
|
|
|
|
struct tbl *
|
2011-07-05 22:12:20 +02:00
|
|
|
|
typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
{
|
|
|
|
|
struct tbl *vp;
|
|
|
|
|
struct tbl *vpbase, *t;
|
|
|
|
|
char *tvar;
|
|
|
|
|
const char *val;
|
2011-08-27 20:06:52 +02:00
|
|
|
|
size_t len;
|
2011-05-05 01:16:05 +02:00
|
|
|
|
bool vappend = false;
|
2014-05-27 15:22:46 +02:00
|
|
|
|
enum namerefflag new_refflag = SRF_NOP;
|
|
|
|
|
|
|
|
|
|
if ((set & (ARRAY | ASSOC)) == ASSOC) {
|
|
|
|
|
new_refflag = SRF_ENABLE;
|
|
|
|
|
set &= ~(ARRAY | ASSOC);
|
|
|
|
|
}
|
|
|
|
|
if ((clr & (ARRAY | ASSOC)) == ASSOC) {
|
|
|
|
|
new_refflag = SRF_DISABLE;
|
|
|
|
|
clr &= ~(ARRAY | ASSOC);
|
|
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
|
|
/* check for valid variable name, search for value */
|
|
|
|
|
val = skip_varname(var, false);
|
2013-03-30 16:39:26 +01:00
|
|
|
|
if (val == var) {
|
|
|
|
|
/* no variable name given */
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if (*val == '[') {
|
2014-05-27 15:22:46 +02:00
|
|
|
|
if (new_refflag != SRF_NOP)
|
2016-07-25 02:04:48 +02:00
|
|
|
|
errorf(Tf_sD_s, var,
|
2010-08-28 22:22:24 +02:00
|
|
|
|
"reference variable can't be an array");
|
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);
|
2011-06-05 21:58:21 +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
|
2011-06-05 21:58:21 +02:00
|
|
|
|
* references at this time since parameter/command
|
2011-10-16 02:18:56 +02:00
|
|
|
|
* substitution is performed on the [expression] which
|
2011-06-05 21:58:21 +02:00
|
|
|
|
* would be a major security hole.
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
|
|
|
|
if (set & IMPORT) {
|
2011-08-27 20:06:52 +02:00
|
|
|
|
size_t i;
|
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
|
for (i = 1; i < len - 1; i++)
|
2017-04-27 21:33:53 +02:00
|
|
|
|
if (!ctype(val[i], C_DIGIT))
|
2009-06-08 22:06:50 +02:00
|
|
|
|
return (NULL);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
val += len;
|
|
|
|
|
}
|
2014-10-03 19:20:03 +02:00
|
|
|
|
if (val[0] == '=') {
|
2011-05-05 01:16:05 +02:00
|
|
|
|
strndupx(tvar, var, val - var, ATEMP);
|
2014-10-03 19:20:03 +02:00
|
|
|
|
++val;
|
|
|
|
|
} else if (set & IMPORT) {
|
|
|
|
|
/* environment invalid variable name or no assignment */
|
|
|
|
|
return (NULL);
|
|
|
|
|
} else if (val[0] == '+' && val[1] == '=') {
|
|
|
|
|
strndupx(tvar, var, val - var, ATEMP);
|
|
|
|
|
val += 2;
|
|
|
|
|
vappend = true;
|
|
|
|
|
} else if (val[0] != '\0') {
|
|
|
|
|
/* other invalid variable names (not from environment) */
|
2013-02-18 23:24:52 +01:00
|
|
|
|
return (NULL);
|
2011-05-05 01:16:05 +02:00
|
|
|
|
} else {
|
2013-02-18 23:24:52 +01:00
|
|
|
|
/* just varname with no value part nor equals sign */
|
2008-10-28 15:32:43 +01:00
|
|
|
|
strdupx(tvar, var, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
val = NULL;
|
2010-08-28 22:22:24 +02:00
|
|
|
|
/* handle foo[*] => foo (whole array) mapping for R39b */
|
2010-01-25 15:25:16 +01:00
|
|
|
|
len = strlen(tvar);
|
2011-08-27 20:06:52 +02:00
|
|
|
|
if (len > 3 && tvar[len - 3] == '[' && tvar[len - 2] == '*' &&
|
|
|
|
|
tvar[len - 1] == ']')
|
|
|
|
|
tvar[len - 3] = '\0';
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-27 15:22:46 +02:00
|
|
|
|
if (new_refflag == SRF_ENABLE) {
|
|
|
|
|
const char *qval, *ccp;
|
2011-06-21 23:50:26 +02:00
|
|
|
|
|
|
|
|
|
/* bail out on 'nameref foo+=bar' */
|
|
|
|
|
if (vappend)
|
2014-05-27 15:22:46 +02:00
|
|
|
|
errorf("appending not allowed for nameref");
|
2011-06-21 23:50:26 +02:00
|
|
|
|
/* find value if variable already exists */
|
|
|
|
|
if ((qval = val) == NULL) {
|
|
|
|
|
varsearch(e->loc, &vp, tvar, hash(tvar));
|
2014-10-04 13:47:19 +02:00
|
|
|
|
if (vp == NULL)
|
|
|
|
|
goto nameref_empty;
|
|
|
|
|
qval = str_val(vp);
|
2011-06-21 23:50:26 +02:00
|
|
|
|
}
|
2014-05-27 15:22:46 +02:00
|
|
|
|
/* check target value for being a valid variable name */
|
|
|
|
|
ccp = skip_varname(qval, false);
|
2014-09-03 21:22:51 +02:00
|
|
|
|
if (ccp == qval) {
|
2016-03-01 21:06:15 +01:00
|
|
|
|
int c;
|
2014-09-03 21:22:51 +02:00
|
|
|
|
|
2016-03-01 21:06:15 +01:00
|
|
|
|
if (!(c = (unsigned char)qval[0]))
|
|
|
|
|
goto nameref_empty;
|
2017-04-27 21:33:53 +02:00
|
|
|
|
else if (ctype(c, C_DIGIT) && getn(qval, &c))
|
2016-03-01 21:28:33 +01:00
|
|
|
|
goto nameref_rhs_checked;
|
2016-03-01 21:06:15 +01:00
|
|
|
|
else if (qval[1] == '\0') switch (c) {
|
2014-09-03 21:22:51 +02:00
|
|
|
|
case '$':
|
|
|
|
|
case '!':
|
|
|
|
|
case '?':
|
|
|
|
|
case '#':
|
|
|
|
|
case '-':
|
|
|
|
|
goto nameref_rhs_checked;
|
|
|
|
|
}
|
2014-10-04 13:47:19 +02:00
|
|
|
|
nameref_empty:
|
2016-07-25 02:04:48 +02:00
|
|
|
|
errorf(Tf_sD_s, var, "empty nameref target");
|
2014-09-03 21:22:51 +02:00
|
|
|
|
}
|
2014-05-27 15:22:46 +02:00
|
|
|
|
len = (*ccp == '[') ? array_ref_len(ccp) : 0;
|
|
|
|
|
if (ccp[len]) {
|
|
|
|
|
/*
|
|
|
|
|
* works for cases "no array", "valid array with
|
|
|
|
|
* junk after it" and "invalid array"; in the
|
|
|
|
|
* latter case, len is also 0 and points to '['
|
|
|
|
|
*/
|
2016-07-25 02:04:48 +02:00
|
|
|
|
errorf(Tf_sD_s, qval,
|
2014-05-27 15:22:46 +02:00
|
|
|
|
"nameref target not a valid parameter name");
|
|
|
|
|
}
|
2014-09-03 21:22:51 +02:00
|
|
|
|
nameref_rhs_checked:
|
2013-06-01 00:47:14 +02:00
|
|
|
|
/* prevent nameref loops */
|
|
|
|
|
while (qval) {
|
|
|
|
|
if (!strcmp(qval, tvar))
|
2016-07-25 02:04:48 +02:00
|
|
|
|
errorf(Tf_sD_s, qval,
|
2013-06-01 00:47:14 +02:00
|
|
|
|
"expression recurses on parameter");
|
|
|
|
|
varsearch(e->loc, &vp, qval, hash(qval));
|
|
|
|
|
qval = NULL;
|
2014-05-27 15:22:46 +02:00
|
|
|
|
if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC))
|
2013-06-01 00:47:14 +02:00
|
|
|
|
qval = str_val(vp);
|
2011-06-21 23:50:26 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* prevent typeset from creating a local PATH/ENV/SHELL */
|
2016-07-25 02:04:48 +02:00
|
|
|
|
if (Flag(FRESTRICTED) && (strcmp(tvar, TPATH) == 0 ||
|
|
|
|
|
strcmp(tvar, "ENV") == 0 || strcmp(tvar, TSHELL) == 0))
|
|
|
|
|
errorf(Tf_sD_s, tvar, "restricted");
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2014-05-27 15:22:46 +02:00
|
|
|
|
innermost_refflag = new_refflag;
|
|
|
|
|
vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
|
2005-05-23 05:06:10 +02:00
|
|
|
|
global(tvar);
|
2014-05-27 15:22:46 +02:00
|
|
|
|
if (new_refflag == SRF_DISABLE && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
|
2009-09-06 19:42:15 +02:00
|
|
|
|
vp->flag &= ~ASSOC;
|
2014-05-27 15:22:46 +02:00
|
|
|
|
else if (new_refflag == SRF_ENABLE) {
|
2009-09-06 19:42:15 +02:00
|
|
|
|
if (vp->flag & ARRAY) {
|
|
|
|
|
struct tbl *a, *tmp;
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* free up entire array */
|
2009-09-06 19:42:15 +02:00
|
|
|
|
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);
|
|
|
|
|
|
2014-05-27 15:22:46 +02:00
|
|
|
|
vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2011-05-05 01:16:05 +02:00
|
|
|
|
/*
|
|
|
|
|
* only allow export flag to be set; AT&T ksh allows any
|
|
|
|
|
* attribute to be changed which means it can be truncated or
|
|
|
|
|
* modified (-L/-R/-Z/-i)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
2014-05-27 15:22:46 +02:00
|
|
|
|
if ((vpbase->flag & RDONLY) &&
|
2005-05-23 05:06:10 +02:00
|
|
|
|
(val || clr || (set & ~EXPORT)))
|
|
|
|
|
/* XXX check calls - is error here ok by POSIX? */
|
2016-07-25 02:04:48 +02:00
|
|
|
|
errorfx(2, Tf_ro, 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;
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/*
|
|
|
|
|
* XXX if x[0] isn't set, there will be problems: need
|
|
|
|
|
* to have one copy of attributes for arrays...
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
|
|
|
|
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;
|
2011-05-05 01:16:05 +02:00
|
|
|
|
/*
|
|
|
|
|
* Don't change base if assignment is to be
|
|
|
|
|
* done, in case assignment fails.
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
|
|
|
|
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)) {
|
2011-05-05 01:16:05 +02:00
|
|
|
|
/*
|
|
|
|
|
* Somewhat arbitrary action
|
|
|
|
|
* here: zap contents of
|
|
|
|
|
* variable, but keep the flag
|
|
|
|
|
* settings.
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-09-05 21:19:12 +02: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) {
|
2011-05-05 01:16:05 +02:00
|
|
|
|
char *tval;
|
|
|
|
|
|
|
|
|
|
if (vappend) {
|
2016-07-25 02:04:48 +02:00
|
|
|
|
tval = shf_smprintf(Tf_ss, str_val(vp), val);
|
2011-05-05 01:16:05 +02:00
|
|
|
|
val = tval;
|
|
|
|
|
} else
|
|
|
|
|
tval = NULL;
|
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if (vp->flag&INTEGER) {
|
|
|
|
|
/* do not zero base before assignment */
|
|
|
|
|
setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* done after assignment to override default */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
if (base > 0)
|
|
|
|
|
vp->type = base;
|
|
|
|
|
} else
|
|
|
|
|
/* setstr can't fail (readonly check already done) */
|
|
|
|
|
setstr(vp, val, KSH_RETURN_ERROR | 0x4);
|
2011-05-05 01:16:05 +02:00
|
|
|
|
|
2015-09-05 21:19:12 +02:00
|
|
|
|
afree(tval, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* only x[0] is ever exported, so use vpbase */
|
|
|
|
|
if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
|
|
|
|
|
vpbase->type == 0)
|
2010-07-18 00:09:40 +02:00
|
|
|
|
exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
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;
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* free up entire array */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2011-06-05 21:58:21 +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)
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* responsible for 'unspecial'ing var */
|
|
|
|
|
unsetspec(vp);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/*
|
|
|
|
|
* Return a pointer to the first char past a legal variable name
|
|
|
|
|
* (returns the 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 *
|
2014-05-27 15:22:46 +02:00
|
|
|
|
skip_varname(const char *s, bool aok)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
{
|
2011-08-27 20:06:52 +02:00
|
|
|
|
size_t alen;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2017-04-27 21:33:53 +02:00
|
|
|
|
if (s && ctype(*s, C_ALPHX)) {
|
2017-03-26 01:10:26 +01:00
|
|
|
|
do {
|
|
|
|
|
++s;
|
2017-04-27 21:33:53 +02:00
|
|
|
|
} while (ctype(*s, C_ALNUX));
|
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,
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/* skip array de-reference? */
|
|
|
|
|
bool aok)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
{
|
2017-04-27 21:33:53 +02:00
|
|
|
|
if (s[0] == CHAR && ctype(s[1], C_ALPHX)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
|
do {
|
|
|
|
|
s += 2;
|
2017-04-27 21:33:53 +02:00
|
|
|
|
} while (s[0] == CHAR && ctype(s[1], C_ALNUX));
|
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;
|
|
|
|
|
|
2011-03-13 02:20:25 +01:00
|
|
|
|
while (/* CONSTCOND */ 1) {
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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
|
2011-06-05 21:58:21 +02:00
|
|
|
|
is_wdvarname(const char *s, bool aok)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
{
|
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
|
|
|
|
|
2011-05-05 01:16:05 +02:00
|
|
|
|
return (p != s && p[0] == CHAR &&
|
|
|
|
|
(p[1] == '=' || (p[1] == '+' && p[2] == CHAR && p[3] == '=')));
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make the exported environment from the exported names in the dictionary.
|
|
|
|
|
*/
|
|
|
|
|
char **
|
|
|
|
|
makenv(void)
|
|
|
|
|
{
|
2011-06-05 21:58:21 +02:00
|
|
|
|
ssize_t i;
|
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;
|
|
|
|
|
|
2009-04-07 21:27:49 +02:00
|
|
|
|
XPinit(denv, 64);
|
2011-06-05 21:58:21 +02:00
|
|
|
|
for (l = e->loc; l != NULL; l = l->next) {
|
|
|
|
|
vpp = l->vars.tbls;
|
|
|
|
|
i = 1 << (l->vars.tshift);
|
|
|
|
|
while (--i >= 0)
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|
2016-11-12 00:31:39 +01:00
|
|
|
|
#ifdef __OS2__
|
|
|
|
|
/* these special variables are not exported */
|
|
|
|
|
if (!strcmp(vp->name, "BEGINLIBPATH") ||
|
|
|
|
|
!strcmp(vp->name, "ENDLIBPATH") ||
|
|
|
|
|
!strcmp(vp->name, "LIBPATHSTRICT"))
|
|
|
|
|
continue;
|
|
|
|
|
#endif
|
2009-04-07 21:27:49 +02:00
|
|
|
|
XPput(denv, vp->val.s);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
2015-02-06 11:56:49 +01:00
|
|
|
|
if (l->flags & BF_STOPENV)
|
|
|
|
|
break;
|
2011-06-05 21:58:21 +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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 */
|
2015-04-19 20:50:38 +02:00
|
|
|
|
static mksh_uari_t user_lineno; /* what user set $LINENO to */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2016-07-13 01:06:26 +02:00
|
|
|
|
/* minimum values from the OS we consider sane, lowered for R53 */
|
|
|
|
|
#define MIN_COLS 4
|
|
|
|
|
#define MIN_LINS 2
|
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
|
static void
|
|
|
|
|
getspec(struct tbl *vp)
|
|
|
|
|
{
|
2013-04-01 04:37:53 +02:00
|
|
|
|
mksh_ari_u num;
|
2009-10-17 23:16:05 +02:00
|
|
|
|
int st;
|
2012-11-20 19:07:45 +01:00
|
|
|
|
struct timeval tv;
|
2008-12-29 22:34:22 +01:00
|
|
|
|
|
2009-10-17 23:16:05 +02:00
|
|
|
|
switch ((st = special(vp->name))) {
|
2012-11-20 18:34:42 +01:00
|
|
|
|
case V_COLUMNS:
|
2012-11-30 20:25:08 +01:00
|
|
|
|
case V_LINES:
|
2012-11-20 18:34:42 +01:00
|
|
|
|
/*
|
|
|
|
|
* Do NOT export COLUMNS/LINES. Many applications
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2012-11-30 20:25:08 +01:00
|
|
|
|
if (got_winch)
|
|
|
|
|
change_winsz();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
switch (st) {
|
|
|
|
|
case V_BASHPID:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
num.u = (mksh_uari_t)procpid;
|
2012-11-30 20:25:08 +01:00
|
|
|
|
break;
|
|
|
|
|
case V_COLUMNS:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
num.i = x_cols;
|
2012-11-20 18:34:42 +01:00
|
|
|
|
break;
|
|
|
|
|
case V_HISTSIZE:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
num.i = histsize;
|
2012-11-20 18:34:42 +01:00
|
|
|
|
break;
|
|
|
|
|
case V_LINENO:
|
2015-04-19 20:50:38 +02:00
|
|
|
|
num.u = (mksh_uari_t)current_lineno + user_lineno;
|
2012-11-20 18:34:42 +01:00
|
|
|
|
break;
|
|
|
|
|
case V_LINES:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
num.i = x_lins;
|
2012-11-20 18:34:42 +01:00
|
|
|
|
break;
|
2012-11-26 23:49:51 +01:00
|
|
|
|
case V_EPOCHREALTIME: {
|
2012-11-20 19:07:45 +01:00
|
|
|
|
/* 10(%u) + 1(.) + 6 + NUL */
|
|
|
|
|
char buf[18];
|
|
|
|
|
|
|
|
|
|
vp->flag &= ~SPECIAL;
|
|
|
|
|
mksh_TIME(tv);
|
|
|
|
|
shf_snprintf(buf, sizeof(buf), "%u.%06u",
|
|
|
|
|
(unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
|
|
|
|
|
setstr(vp, buf, KSH_RETURN_ERROR | 0x4);
|
|
|
|
|
vp->flag |= SPECIAL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2012-11-20 18:34:42 +01:00
|
|
|
|
case V_OPTIND:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
num.i = user_opt.uoptind;
|
2012-11-20 18:34:42 +01:00
|
|
|
|
break;
|
|
|
|
|
case V_RANDOM:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
num.i = rndget();
|
2012-11-20 18:34:42 +01:00
|
|
|
|
break;
|
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) {
|
2012-05-04 23:47:04 +02:00
|
|
|
|
mksh_TIME(tv);
|
2013-04-01 04:37:53 +02:00
|
|
|
|
num.i = tv.tv_sec - seconds;
|
2009-10-17 23:16:05 +02:00
|
|
|
|
} else
|
|
|
|
|
return;
|
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;
|
2013-04-01 04:37:53 +02:00
|
|
|
|
setint_n(vp, num.i, 0);
|
2009-10-17 23:16:05 +02:00
|
|
|
|
vp->flag |= SPECIAL;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
setspec(struct tbl *vp)
|
|
|
|
|
{
|
2013-04-01 04:37:53 +02:00
|
|
|
|
mksh_ari_u num;
|
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))) {
|
2016-11-12 00:31:39 +01:00
|
|
|
|
#ifdef __OS2__
|
|
|
|
|
case V_BEGINLIBPATH:
|
|
|
|
|
case V_ENDLIBPATH:
|
|
|
|
|
case V_LIBPATHSTRICT:
|
|
|
|
|
setextlibpath(vp->name, str_val(vp));
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
2012-11-20 18:34:42 +01:00
|
|
|
|
#if HAVE_PERSISTENT_HISTORY
|
|
|
|
|
case V_HISTFILE:
|
|
|
|
|
sethistfile(str_val(vp));
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
case V_IFS:
|
2017-04-22 02:07:10 +02:00
|
|
|
|
set_ifs(str_val(vp));
|
2012-11-20 18:34:42 +01:00
|
|
|
|
return;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
case V_PATH:
|
2015-09-05 21:19:12 +02:00
|
|
|
|
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);
|
• more comment and int→bool cleanup, add and improve some comments
• in interactive mode, always look up {LC_{ALL,CTYPE},LANG} environment
variables if setlocale/nl_langinfo(CODESET) doesn’t suffice
• add the ability to call any builtin (some don't make sense or wouldn't
work) directly by analysing argv[0]
• for direct builtin calls, the {LC_{ALL,CTYPE},LANG} environment
variables determine utf8-mode, even if MKSH_ASSUME_UTF8 was set
• when called as builtin, echo behaves POSIXish
• add domainname as alias for true on MirBSD only, to be able to link it
• sync mksh Makefiles with Build.sh output
• adjust manpage wrt release plans
• link some things to mksh now that we have callable builtins:
bin/echo bin/kill bin/pwd bin/sleep (exact matches)
bin/test bin/[ (were scripts before)
bin/domainname=usr/bin/true usr/bin/false (move to /bin/ now)
• drop linked utilities and, except for echo and kill, their manpages
• adjust instbin and link a few more there as well
2011-02-11 02:18:23 +01:00
|
|
|
|
/* clear tracked aliases */
|
|
|
|
|
flushcom(true);
|
2009-10-17 23:16:05 +02:00
|
|
|
|
return;
|
2016-07-25 23:02:13 +02:00
|
|
|
|
#ifndef MKSH_NO_CMDLINE_EDITING
|
2016-07-25 22:43:54 +02:00
|
|
|
|
case V_TERM:
|
|
|
|
|
x_initterm(str_val(vp));
|
|
|
|
|
return;
|
2016-07-25 23:02:13 +02:00
|
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
|
case V_TMPDIR:
|
2015-09-05 21:19:12 +02:00
|
|
|
|
afree(tmpdir, APERM);
|
|
|
|
|
tmpdir = NULL;
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/*
|
|
|
|
|
* Use tmpdir iff it is an absolute path, is writable
|
|
|
|
|
* and searchable and is a directory...
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
struct stat statb;
|
|
|
|
|
|
|
|
|
|
s = str_val(vp);
|
2011-09-07 17:24:22 +02:00
|
|
|
|
/* LINTED use of access */
|
2015-07-10 21:36:38 +02:00
|
|
|
|
if (mksh_abspath(s) && 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
|
|
|
|
}
|
2011-01-21 22:04:48 +01:00
|
|
|
|
return;
|
2009-10-17 23:16:05 +02:00
|
|
|
|
/* common sub-cases */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
case V_COLUMNS:
|
2012-11-30 20:25:08 +01:00
|
|
|
|
case V_LINES:
|
|
|
|
|
if (vp->flag & IMPORT) {
|
|
|
|
|
/* do not touch */
|
|
|
|
|
unspecial(vp->name);
|
|
|
|
|
vp->flag &= ~SPECIAL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* FALLTHROUGH */
|
2012-11-20 18:34:42 +01:00
|
|
|
|
case V_HISTSIZE:
|
|
|
|
|
case V_LINENO:
|
|
|
|
|
case V_OPTIND:
|
2009-10-17 23:16:05 +02:00
|
|
|
|
case V_RANDOM:
|
|
|
|
|
case V_SECONDS:
|
2011-07-02 19:57:41 +02:00
|
|
|
|
case V_TMOUT:
|
2008-12-29 22:34:22 +01:00
|
|
|
|
vp->flag &= ~SPECIAL;
|
2013-04-01 04:37:53 +02:00
|
|
|
|
if (getint(vp, &num, false) == -1) {
|
2011-07-02 19:57:41 +02:00
|
|
|
|
s = str_val(vp);
|
|
|
|
|
if (st != V_RANDOM)
|
2017-04-02 18:47:43 +02:00
|
|
|
|
errorf(Tf_sD_sD_s, vp->name, Tbadnum, s);
|
2013-04-01 04:37:53 +02:00
|
|
|
|
num.u = hash(s);
|
2011-07-02 19:57:41 +02:00
|
|
|
|
}
|
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) {
|
2012-11-20 18:34:42 +01:00
|
|
|
|
case V_COLUMNS:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
if (num.i >= MIN_COLS)
|
|
|
|
|
x_cols = num.i;
|
2009-10-17 23:16:05 +02:00
|
|
|
|
break;
|
|
|
|
|
case V_HISTSIZE:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
sethistsize(num.i);
|
2009-10-17 23:16:05 +02:00
|
|
|
|
break;
|
2012-11-20 18:34:42 +01:00
|
|
|
|
case V_LINENO:
|
|
|
|
|
/* The -1 is because line numbering starts at 1. */
|
2015-04-19 20:50:38 +02:00
|
|
|
|
user_lineno = num.u - (mksh_uari_t)current_lineno - 1;
|
2009-10-17 23:16:05 +02:00
|
|
|
|
break;
|
2008-12-29 22:34:22 +01:00
|
|
|
|
case V_LINES:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
if (num.i >= MIN_LINS)
|
|
|
|
|
x_lins = num.i;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
break;
|
2012-11-20 18:34:42 +01:00
|
|
|
|
case V_OPTIND:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
getopts_reset((int)num.i);
|
2012-11-20 18:34:42 +01:00
|
|
|
|
break;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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
|
|
|
|
|
*/
|
2013-04-01 04:37:53 +02:00
|
|
|
|
rndset((unsigned long)num.u);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
break;
|
|
|
|
|
case V_SECONDS:
|
2006-11-10 05:22:13 +01:00
|
|
|
|
{
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
2012-05-04 23:47:04 +02:00
|
|
|
|
mksh_TIME(tv);
|
2013-04-01 04:37:53 +02:00
|
|
|
|
seconds = tv.tv_sec - num.i;
|
2006-11-10 05:22:13 +01:00
|
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
|
break;
|
2011-07-02 19:57:41 +02:00
|
|
|
|
case V_TMOUT:
|
2013-04-01 04:37:53 +02:00
|
|
|
|
ksh_tmout = num.i >= 0 ? num.i : 0;
|
2011-07-02 19:57:41 +02:00
|
|
|
|
break;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
unsetspec(struct tbl *vp)
|
|
|
|
|
{
|
2012-11-20 18:34:42 +01: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
|
|
|
|
|
*/
|
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
|
switch (special(vp->name)) {
|
2016-11-12 00:31:39 +01:00
|
|
|
|
#ifdef __OS2__
|
|
|
|
|
case V_BEGINLIBPATH:
|
|
|
|
|
case V_ENDLIBPATH:
|
|
|
|
|
case V_LIBPATHSTRICT:
|
|
|
|
|
setextlibpath(vp->name, "");
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
2015-03-07 21:46:31 +01:00
|
|
|
|
#if HAVE_PERSISTENT_HISTORY
|
|
|
|
|
case V_HISTFILE:
|
|
|
|
|
sethistfile(NULL);
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
2012-11-20 18:34:42 +01:00
|
|
|
|
case V_IFS:
|
2017-04-22 02:07:10 +02:00
|
|
|
|
set_ifs(TC_IFSWS);
|
2012-11-20 18:34:42 +01:00
|
|
|
|
break;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
case V_PATH:
|
2015-09-05 21:19:12 +02:00
|
|
|
|
afree(path, APERM);
|
2008-10-28 15:32:43 +01:00
|
|
|
|
strdupx(path, def_path, APERM);
|
• more comment and int→bool cleanup, add and improve some comments
• in interactive mode, always look up {LC_{ALL,CTYPE},LANG} environment
variables if setlocale/nl_langinfo(CODESET) doesn’t suffice
• add the ability to call any builtin (some don't make sense or wouldn't
work) directly by analysing argv[0]
• for direct builtin calls, the {LC_{ALL,CTYPE},LANG} environment
variables determine utf8-mode, even if MKSH_ASSUME_UTF8 was set
• when called as builtin, echo behaves POSIXish
• add domainname as alias for true on MirBSD only, to be able to link it
• sync mksh Makefiles with Build.sh output
• adjust manpage wrt release plans
• link some things to mksh now that we have callable builtins:
bin/echo bin/kill bin/pwd bin/sleep (exact matches)
bin/test bin/[ (were scripts before)
bin/domainname=usr/bin/true usr/bin/false (move to /bin/ now)
• drop linked utilities and, except for echo and kill, their manpages
• adjust instbin and link a few more there as well
2011-02-11 02:18:23 +01:00
|
|
|
|
/* clear tracked aliases */
|
|
|
|
|
flushcom(true);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
break;
|
2016-07-25 23:02:13 +02:00
|
|
|
|
#ifndef MKSH_NO_CMDLINE_EDITING
|
2016-07-25 22:43:54 +02:00
|
|
|
|
case V_TERM:
|
|
|
|
|
x_initterm(null);
|
|
|
|
|
return;
|
2016-07-25 23:02:13 +02:00
|
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
|
case V_TMPDIR:
|
|
|
|
|
/* should not become unspecial */
|
|
|
|
|
if (tmpdir) {
|
|
|
|
|
afree(tmpdir, APERM);
|
|
|
|
|
tmpdir = NULL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case V_LINENO:
|
|
|
|
|
case V_RANDOM:
|
|
|
|
|
case V_SECONDS:
|
2011-06-05 21:58: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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Search for (and possibly create) a table entry starting with
|
|
|
|
|
* vp, indexed by val.
|
|
|
|
|
*/
|
2011-05-29 04:18:57 +02:00
|
|
|
|
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
|
|
|
|
{
|
2010-07-18 00:09:40 +02:00
|
|
|
|
struct tbl *prev, *curr, *news;
|
2009-08-01 22:32:45 +02:00
|
|
|
|
size_t len;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2014-05-27 15:22:46 +02:00
|
|
|
|
vp->flag = (vp->flag | (ARRAY | DEFINED)) & ~ASSOC;
|
2011-06-05 21:58:21 +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);
|
2010-07-18 00:09:40 +02:00
|
|
|
|
news = curr;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
} else
|
2010-07-18 00:09:40 +02:00
|
|
|
|
news = NULL;
|
|
|
|
|
if (!news) {
|
2010-09-14 23:26:19 +02:00
|
|
|
|
len = strlen(vp->name);
|
|
|
|
|
checkoktoadd(len, 1 + offsetof(struct tbl, name[0]));
|
|
|
|
|
news = alloc(offsetof(struct tbl, name[0]) + ++len, vp->areap);
|
2010-07-18 00:09:40 +02:00
|
|
|
|
memcpy(news->name, vp->name, len);
|
2009-08-28 23:01:27 +02:00
|
|
|
|
}
|
2010-07-18 00:09:40 +02:00
|
|
|
|
news->flag = (vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL)) | AINDEX;
|
|
|
|
|
news->type = vp->type;
|
|
|
|
|
news->areap = vp->areap;
|
|
|
|
|
news->u2.field = vp->u2.field;
|
|
|
|
|
news->ua.index = val;
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
if (curr != news) {
|
|
|
|
|
/* not reusing old array entry */
|
2010-07-18 00:09:40 +02:00
|
|
|
|
prev->u.array = news;
|
|
|
|
|
news->u.array = curr;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
2010-07-18 00:09:40 +02:00
|
|
|
|
return (news);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 21:58:21 +02:00
|
|
|
|
/*
|
|
|
|
|
* Return the length of an array reference (eg, [1+2]) - cp is assumed
|
|
|
|
|
* to point to the open bracket. Returns 0 if there is no matching
|
|
|
|
|
* closing bracket.
|
2014-05-27 15:22:46 +02:00
|
|
|
|
*
|
|
|
|
|
* XXX this should parse the actual arithmetic syntax
|
2005-05-23 05:06:10 +02:00
|
|
|
|
*/
|
2011-08-27 20:06:52 +02:00
|
|
|
|
size_t
|
2005-05-23 05:06:10 +02:00
|
|
|
|
array_ref_len(const char *cp)
|
|
|
|
|
{
|
|
|
|
|
const char *s = cp;
|
2011-08-27 20:06:52 +02:00
|
|
|
|
char c;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
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
|
|
|
|
|
2016-01-14 23:49:33 +01:00
|
|
|
|
if (!(p = cstrchr(str, '[')))
|
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;
|
2011-08-27 20:06:52 +02:00
|
|
|
|
mksh_uari_t i = 0, j = 0;
|
2011-11-11 23:14:19 +01:00
|
|
|
|
const char *ccp = var;
|
2011-05-05 01:16:05 +02:00
|
|
|
|
char *cp = NULL;
|
2011-08-27 20:06:52 +02:00
|
|
|
|
size_t n;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
2011-11-26 01:45:03 +01:00
|
|
|
|
/* to get local array, use "local foo; set -A foo" */
|
2011-08-27 20:06:52 +02:00
|
|
|
|
n = strlen(var);
|
|
|
|
|
if (n > 0 && var[n - 1] == '+') {
|
2011-05-05 01:16:05 +02:00
|
|
|
|
/* append mode */
|
|
|
|
|
reset = false;
|
2011-08-27 20:06:52 +02:00
|
|
|
|
strndupx(cp, var, n - 1, ATEMP);
|
2011-11-11 23:14:19 +01:00
|
|
|
|
ccp = cp;
|
2011-05-05 01:16:05 +02:00
|
|
|
|
}
|
2011-11-11 23:14:19 +01:00
|
|
|
|
vp = global(ccp);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
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))
|
2016-07-25 02:04:48 +02:00
|
|
|
|
errorfx(2, Tf_ro, ccp);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
/* This code is quite non-optimal */
|
2011-11-26 01:45:03 +01: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);
|
2011-11-26 01:45:03 +01:00
|
|
|
|
/* allocate-by-access the [0] element to keep in scope */
|
|
|
|
|
arraysearch(vp, 0);
|
|
|
|
|
}
|
2011-05-05 01:16:05 +02:00
|
|
|
|
/*
|
2011-06-05 21:58:21 +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...
|
|
|
|
|
*/
|
2011-05-05 01:16:05 +02:00
|
|
|
|
if (cp != NULL) {
|
|
|
|
|
/* find out where to set when appending */
|
|
|
|
|
for (vq = vp; vq; vq = vq->u.array) {
|
|
|
|
|
if (!(vq->flag & ISSET))
|
|
|
|
|
continue;
|
|
|
|
|
if (arrayindex(vq) >= j)
|
|
|
|
|
j = arrayindex(vq) + 1;
|
|
|
|
|
}
|
|
|
|
|
afree(cp, ATEMP);
|
|
|
|
|
}
|
2009-12-01 20:15:35 +01:00
|
|
|
|
while ((ccp = vals[i])) {
|
2014-06-09 13:13:19 +02:00
|
|
|
|
#if 0 /* temporarily taken out due to regression */
|
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];
|
|
|
|
|
}
|
2014-06-09 13:13:19 +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++;
|
|
|
|
|
j++;
|
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)
|
|
|
|
|
{
|
2014-01-11 19:09:43 +01:00
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
|
|
mksh_TIME(tv);
|
|
|
|
|
BAFHUpdateMem_mem(qh_state, &tv, sizeof(tv));
|
|
|
|
|
|
2008-12-29 22:05:15 +01:00
|
|
|
|
#ifdef TIOCGWINSZ
|
2010-07-25 13:35:43 +02:00
|
|
|
|
/* check if window size has changed */
|
2012-11-30 20:25:08 +01:00
|
|
|
|
if (tty_init_fd() < 2) {
|
2008-12-29 22:05:15 +01:00
|
|
|
|
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
|
2011-01-21 22:04:48 +01:00
|
|
|
|
hash(const void *s)
|
2010-07-04 19:33:58 +02:00
|
|
|
|
{
|
2011-01-21 22:04:48 +01:00
|
|
|
|
register uint32_t h;
|
|
|
|
|
|
2014-01-11 19:09:43 +01:00
|
|
|
|
BAFHInit(h);
|
|
|
|
|
BAFHUpdateStr_reg(h, s);
|
|
|
|
|
BAFHFinish_reg(h);
|
|
|
|
|
return (h);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
|
chvt_rndsetup(const void *bp, size_t sz)
|
|
|
|
|
{
|
|
|
|
|
register uint32_t h;
|
|
|
|
|
|
|
|
|
|
/* use LCG as seed but try to get them to deviate immediately */
|
|
|
|
|
h = lcg_state;
|
|
|
|
|
(void)rndget();
|
|
|
|
|
BAFHFinish_reg(h);
|
|
|
|
|
/* variation through pid, ppid, and the works */
|
|
|
|
|
BAFHUpdateMem_reg(h, &rndsetupstate, sizeof(rndsetupstate));
|
|
|
|
|
/* some variation, some possibly entropy, depending on OE */
|
|
|
|
|
BAFHUpdateMem_reg(h, bp, sz);
|
|
|
|
|
/* mix them all up */
|
|
|
|
|
BAFHFinish_reg(h);
|
|
|
|
|
|
2011-01-21 22:04:48 +01:00
|
|
|
|
return (h);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-14 16:35:13 +02:00
|
|
|
|
mksh_ari_t
|
|
|
|
|
rndget(void)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* this is the same Linear Congruential PRNG as Borland
|
|
|
|
|
* C/C++ allegedly uses in its built-in rand() function
|
|
|
|
|
*/
|
|
|
|
|
return (((lcg_state = 22695477 * lcg_state + 1) >> 16) & 0x7FFF);
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-21 22:04:48 +01:00
|
|
|
|
void
|
2013-04-01 04:37:53 +02:00
|
|
|
|
rndset(unsigned long v)
|
2011-01-21 22:04:48 +01:00
|
|
|
|
{
|
|
|
|
|
register uint32_t h;
|
2014-01-11 19:09:43 +01:00
|
|
|
|
#if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
|
|
|
|
|
register uint32_t t;
|
|
|
|
|
#endif
|
|
|
|
|
struct {
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
void *sp;
|
|
|
|
|
uint32_t qh;
|
|
|
|
|
pid_t pp;
|
|
|
|
|
short r;
|
|
|
|
|
} z;
|
|
|
|
|
|
2016-10-23 01:56:50 +02:00
|
|
|
|
/* clear the allocated space, for valgrind and to avoid UB */
|
2014-06-26 22:36:02 +02:00
|
|
|
|
memset(&z, 0, sizeof(z));
|
|
|
|
|
|
2014-01-11 19:09:43 +01:00
|
|
|
|
h = lcg_state;
|
|
|
|
|
BAFHFinish_reg(h);
|
|
|
|
|
BAFHUpdateMem_reg(h, &v, sizeof(v));
|
|
|
|
|
|
|
|
|
|
mksh_TIME(z.tv);
|
|
|
|
|
z.sp = &lcg_state;
|
|
|
|
|
z.pp = procpid;
|
|
|
|
|
z.r = (short)rndget();
|
2011-01-21 22:04:48 +01:00
|
|
|
|
|
|
|
|
|
#if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
|
2014-01-11 19:09:43 +01:00
|
|
|
|
t = qh_state;
|
|
|
|
|
BAFHFinish_reg(t);
|
|
|
|
|
z.qh = (t & 0xFFFF8000) | rndget();
|
|
|
|
|
lcg_state = (t << 15) | rndget();
|
2011-01-21 22:04:48 +01:00
|
|
|
|
/*
|
|
|
|
|
* either we have very chap entropy get and push available,
|
|
|
|
|
* with malloc() pulling in this code already anyway, or the
|
|
|
|
|
* user requested us to use the old functions
|
|
|
|
|
*/
|
2014-01-11 19:09:43 +01:00
|
|
|
|
t = h;
|
|
|
|
|
BAFHUpdateMem_reg(t, &lcg_state, sizeof(lcg_state));
|
|
|
|
|
BAFHFinish_reg(t);
|
|
|
|
|
lcg_state = t;
|
2011-01-21 22:04:48 +01:00
|
|
|
|
#if defined(arc4random_pushb_fast)
|
|
|
|
|
arc4random_pushb_fast(&lcg_state, sizeof(lcg_state));
|
|
|
|
|
lcg_state = arc4random();
|
|
|
|
|
#else
|
|
|
|
|
lcg_state = arc4random_pushb(&lcg_state, sizeof(lcg_state));
|
|
|
|
|
#endif
|
2014-01-11 19:09:43 +01:00
|
|
|
|
BAFHUpdateMem_reg(h, &lcg_state, sizeof(lcg_state));
|
|
|
|
|
#else
|
|
|
|
|
z.qh = qh_state;
|
2011-01-21 22:04:48 +01:00
|
|
|
|
#endif
|
2010-07-04 19:33:58 +02:00
|
|
|
|
|
2014-01-11 19:09:43 +01:00
|
|
|
|
BAFHUpdateMem_reg(h, &z, sizeof(z));
|
|
|
|
|
BAFHFinish_reg(h);
|
2011-01-21 22:04:48 +01:00
|
|
|
|
lcg_state = h;
|
2010-07-04 19:33:58 +02:00
|
|
|
|
}
|
2014-01-11 19:09:43 +01:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
rndpush(const void *s)
|
|
|
|
|
{
|
|
|
|
|
register uint32_t h = qh_state;
|
|
|
|
|
|
|
|
|
|
BAFHUpdateStr_reg(h, s);
|
|
|
|
|
BAFHUpdateOctet_reg(h, 0);
|
|
|
|
|
qh_state = h;
|
|
|
|
|
}
|
2016-08-01 23:38:07 +02:00
|
|
|
|
|
|
|
|
|
/* record last glob match */
|
|
|
|
|
void
|
|
|
|
|
record_match(const char *istr)
|
|
|
|
|
{
|
|
|
|
|
struct tbl *vp;
|
|
|
|
|
|
|
|
|
|
vp = local("KSH_MATCH", false);
|
|
|
|
|
unset(vp, 1);
|
|
|
|
|
vp->flag = DEFINED | RDONLY;
|
|
|
|
|
setstr(vp, istr, 0x4);
|
|
|
|
|
}
|
2017-04-02 17:51:20 +02:00
|
|
|
|
|
|
|
|
|
/* typeset, global(deprecated), export, and readonly */
|
|
|
|
|
int
|
|
|
|
|
c_typeset(const char **wp)
|
|
|
|
|
{
|
|
|
|
|
struct tbl *vp, **p;
|
|
|
|
|
uint32_t fset = 0, fclr = 0, flag;
|
|
|
|
|
int thing = 0, field = 0, base = 0, i;
|
|
|
|
|
struct block *l;
|
|
|
|
|
const char *opts;
|
|
|
|
|
const char *fieldstr = NULL, *basestr = NULL;
|
|
|
|
|
bool localv = false, func = false, pflag = false, istset = true;
|
|
|
|
|
enum namerefflag new_refflag = SRF_NOP;
|
|
|
|
|
|
|
|
|
|
switch (**wp) {
|
|
|
|
|
|
|
|
|
|
/* export */
|
|
|
|
|
case 'e':
|
|
|
|
|
fset |= EXPORT;
|
|
|
|
|
istset = false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* readonly */
|
|
|
|
|
case 'r':
|
|
|
|
|
fset |= RDONLY;
|
|
|
|
|
istset = false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* set */
|
|
|
|
|
case 's':
|
|
|
|
|
/* called with 'typeset -' */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* typeset */
|
|
|
|
|
case 't':
|
|
|
|
|
localv = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* see comment below regarding possible opions */
|
|
|
|
|
opts = istset ? "L#R#UZ#afgi#lnprtux" : "p";
|
|
|
|
|
|
|
|
|
|
builtin_opt.flags |= GF_PLUSOPT;
|
|
|
|
|
/*
|
|
|
|
|
* AT&T ksh seems to have 0-9 as options which are multiplied
|
|
|
|
|
* to get a number that is used with -L, -R, -Z or -i (eg, -1R2
|
|
|
|
|
* sets right justify in a field of 12). This allows options
|
|
|
|
|
* to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
|
|
|
|
|
* does not allow the number to be specified as a separate argument
|
|
|
|
|
* Here, the number must follow the RLZi option, but is optional
|
|
|
|
|
* (see the # kludge in ksh_getopt()).
|
|
|
|
|
*/
|
|
|
|
|
while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
|
|
|
|
|
flag = 0;
|
|
|
|
|
switch (i) {
|
|
|
|
|
case 'L':
|
|
|
|
|
flag = LJUST;
|
|
|
|
|
fieldstr = builtin_opt.optarg;
|
|
|
|
|
break;
|
|
|
|
|
case 'R':
|
|
|
|
|
flag = RJUST;
|
|
|
|
|
fieldstr = builtin_opt.optarg;
|
|
|
|
|
break;
|
|
|
|
|
case 'U':
|
|
|
|
|
/*
|
|
|
|
|
* AT&T ksh uses u, but this conflicts with
|
|
|
|
|
* upper/lower case. If this option is changed,
|
|
|
|
|
* need to change the -U below as well
|
|
|
|
|
*/
|
|
|
|
|
flag = INT_U;
|
|
|
|
|
break;
|
|
|
|
|
case 'Z':
|
|
|
|
|
flag = ZEROFIL;
|
|
|
|
|
fieldstr = builtin_opt.optarg;
|
|
|
|
|
break;
|
|
|
|
|
case 'a':
|
|
|
|
|
/*
|
|
|
|
|
* this is supposed to set (-a) or unset (+a) the
|
|
|
|
|
* indexed array attribute; it does nothing on an
|
|
|
|
|
* existing regular string or indexed array though
|
|
|
|
|
*/
|
|
|
|
|
break;
|
|
|
|
|
case 'f':
|
|
|
|
|
func = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'g':
|
|
|
|
|
localv = (builtin_opt.info & GI_PLUS) ? true : false;
|
|
|
|
|
break;
|
|
|
|
|
case 'i':
|
|
|
|
|
flag = INTEGER;
|
|
|
|
|
basestr = builtin_opt.optarg;
|
|
|
|
|
break;
|
|
|
|
|
case 'l':
|
|
|
|
|
flag = LCASEV;
|
|
|
|
|
break;
|
|
|
|
|
case 'n':
|
|
|
|
|
new_refflag = (builtin_opt.info & GI_PLUS) ?
|
|
|
|
|
SRF_DISABLE : SRF_ENABLE;
|
|
|
|
|
break;
|
|
|
|
|
/* export, readonly: POSIX -p flag */
|
|
|
|
|
case 'p':
|
|
|
|
|
/* typeset: show values as well */
|
|
|
|
|
pflag = true;
|
|
|
|
|
if (istset)
|
|
|
|
|
continue;
|
|
|
|
|
break;
|
|
|
|
|
case 'r':
|
|
|
|
|
flag = RDONLY;
|
|
|
|
|
break;
|
|
|
|
|
case 't':
|
|
|
|
|
flag = TRACE;
|
|
|
|
|
break;
|
|
|
|
|
case 'u':
|
|
|
|
|
/* upper case / autoload */
|
|
|
|
|
flag = UCASEV_AL;
|
|
|
|
|
break;
|
|
|
|
|
case 'x':
|
|
|
|
|
flag = EXPORT;
|
|
|
|
|
break;
|
|
|
|
|
case '?':
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
if (builtin_opt.info & GI_PLUS) {
|
|
|
|
|
fclr |= flag;
|
|
|
|
|
fset &= ~flag;
|
|
|
|
|
thing = '+';
|
|
|
|
|
} else {
|
|
|
|
|
fset |= flag;
|
|
|
|
|
fclr &= ~flag;
|
|
|
|
|
thing = '-';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fieldstr && !getn(fieldstr, &field)) {
|
2017-04-02 18:47:43 +02:00
|
|
|
|
bi_errorf(Tf_sD_s, Tbadnum, fieldstr);
|
2017-04-02 17:51:20 +02:00
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
if (basestr) {
|
|
|
|
|
if (!getn(basestr, &base)) {
|
|
|
|
|
bi_errorf(Tf_sD_s, "bad integer base", basestr);
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
if (base < 1 || base > 36)
|
|
|
|
|
base = 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
|
|
|
|
|
(wp[builtin_opt.optind][0] == '-' ||
|
|
|
|
|
wp[builtin_opt.optind][0] == '+') &&
|
|
|
|
|
wp[builtin_opt.optind][1] == '\0') {
|
|
|
|
|
thing = wp[builtin_opt.optind][0];
|
|
|
|
|
builtin_opt.optind++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
|
|
|
|
|
new_refflag != SRF_NOP)) {
|
|
|
|
|
bi_errorf("only -t, -u and -x options may be used with -f");
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
if (wp[builtin_opt.optind]) {
|
|
|
|
|
/*
|
|
|
|
|
* Take care of exclusions.
|
|
|
|
|
* At this point, flags in fset are cleared in fclr and vice
|
|
|
|
|
* versa. This property should be preserved.
|
|
|
|
|
*/
|
|
|
|
|
if (fset & LCASEV)
|
|
|
|
|
/* LCASEV has priority over UCASEV_AL */
|
|
|
|
|
fset &= ~UCASEV_AL;
|
|
|
|
|
if (fset & LJUST)
|
|
|
|
|
/* LJUST has priority over RJUST */
|
|
|
|
|
fset &= ~RJUST;
|
|
|
|
|
if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
|
|
|
|
|
/* -Z implies -ZR */
|
|
|
|
|
fset |= RJUST;
|
|
|
|
|
fclr &= ~RJUST;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Setting these attributes clears the others, unless they
|
|
|
|
|
* are also set in this command
|
|
|
|
|
*/
|
|
|
|
|
if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
|
|
|
|
|
INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
|
|
|
|
|
fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
|
|
|
|
|
LCASEV | INTEGER | INT_U | INT_L);
|
|
|
|
|
}
|
|
|
|
|
if (new_refflag != SRF_NOP) {
|
|
|
|
|
fclr &= ~(ARRAY | ASSOC);
|
|
|
|
|
fset &= ~(ARRAY | ASSOC);
|
|
|
|
|
fclr |= EXPORT;
|
|
|
|
|
fset |= ASSOC;
|
|
|
|
|
if (new_refflag == SRF_DISABLE)
|
|
|
|
|
fclr |= ASSOC;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set variables and attributes */
|
|
|
|
|
if (wp[builtin_opt.optind] &&
|
|
|
|
|
/* not "typeset -p varname" */
|
|
|
|
|
!(!func && pflag && !(fset | fclr))) {
|
|
|
|
|
int rv = 0;
|
|
|
|
|
struct tbl *f;
|
|
|
|
|
|
|
|
|
|
if (localv && !func)
|
|
|
|
|
fset |= LOCAL;
|
|
|
|
|
for (i = builtin_opt.optind; wp[i]; i++) {
|
|
|
|
|
if (func) {
|
|
|
|
|
f = findfunc(wp[i], hash(wp[i]),
|
|
|
|
|
tobool(fset & UCASEV_AL));
|
|
|
|
|
if (!f) {
|
|
|
|
|
/* AT&T ksh does ++rv: bogus */
|
|
|
|
|
rv = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (fset | fclr) {
|
|
|
|
|
f->flag |= fset;
|
|
|
|
|
f->flag &= ~fclr;
|
|
|
|
|
} else {
|
|
|
|
|
fpFUNCTf(shl_stdout, 0,
|
|
|
|
|
tobool(f->flag & FKSH),
|
|
|
|
|
wp[i], f->val.t);
|
|
|
|
|
shf_putc('\n', shl_stdout);
|
|
|
|
|
}
|
|
|
|
|
} else if (!typeset(wp[i], fset, fclr, field, base)) {
|
|
|
|
|
bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (rv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* list variables and attributes */
|
|
|
|
|
|
|
|
|
|
/* no difference at this point.. */
|
|
|
|
|
flag = fset | fclr;
|
|
|
|
|
if (func) {
|
|
|
|
|
for (l = e->loc; l; l = l->next) {
|
|
|
|
|
for (p = ktsort(&l->funs); (vp = *p++); ) {
|
|
|
|
|
if (flag && (vp->flag & flag) == 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (thing == '-')
|
|
|
|
|
fpFUNCTf(shl_stdout, 0,
|
|
|
|
|
tobool(vp->flag & FKSH),
|
|
|
|
|
vp->name, vp->val.t);
|
|
|
|
|
else
|
|
|
|
|
shf_puts(vp->name, shl_stdout);
|
|
|
|
|
shf_putc('\n', shl_stdout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (wp[builtin_opt.optind]) {
|
|
|
|
|
for (i = builtin_opt.optind; wp[i]; i++) {
|
2017-04-02 18:25:23 +02:00
|
|
|
|
vp = isglobal(wp[i], false);
|
|
|
|
|
c_typeset_vardump(vp, flag, thing,
|
|
|
|
|
last_lookup_was_array ? 4 : 0, pflag, istset);
|
2017-04-02 17:51:20 +02:00
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
|
|
|
|
|
bool pflag, bool istset)
|
|
|
|
|
{
|
|
|
|
|
struct tbl **blockvars, *vp;
|
|
|
|
|
|
|
|
|
|
if (l->next)
|
|
|
|
|
c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
|
|
|
|
|
blockvars = ktsort(&l->vars);
|
|
|
|
|
while ((vp = *blockvars++))
|
2017-04-02 18:25:23 +02:00
|
|
|
|
c_typeset_vardump(vp, flag, thing, 0, pflag, istset);
|
2017-04-02 17:51:20 +02:00
|
|
|
|
/*XXX doesn’t this leak? */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-04-02 18:25:23 +02:00
|
|
|
|
c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, int any_set,
|
|
|
|
|
bool pflag, bool istset)
|
2017-04-02 17:51:20 +02:00
|
|
|
|
{
|
|
|
|
|
struct tbl *tvp;
|
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
|
|
if (!vp)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* See if the parameter is set (for arrays, if any
|
|
|
|
|
* element is set).
|
|
|
|
|
*/
|
|
|
|
|
for (tvp = vp; tvp; tvp = tvp->u.array)
|
|
|
|
|
if (tvp->flag & ISSET) {
|
2017-04-02 18:25:23 +02:00
|
|
|
|
any_set |= 1;
|
2017-04-02 17:51:20 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check attributes - note that all array elements
|
|
|
|
|
* have (should have?) the same attributes, so checking
|
|
|
|
|
* the first is sufficient.
|
|
|
|
|
*
|
|
|
|
|
* Report an unset param only if the user has
|
|
|
|
|
* explicitly given it some attribute (like export);
|
|
|
|
|
* otherwise, after "echo $FOO", we would report FOO...
|
|
|
|
|
*/
|
|
|
|
|
if (!any_set && !(vp->flag & USERATTRIB))
|
|
|
|
|
return;
|
|
|
|
|
if (flag && (vp->flag & flag) == 0)
|
|
|
|
|
return;
|
|
|
|
|
if (!(vp->flag & ARRAY))
|
|
|
|
|
/* optimise later conditionals */
|
|
|
|
|
any_set = 0;
|
|
|
|
|
do {
|
|
|
|
|
/*
|
|
|
|
|
* Ignore array elements that aren't set unless there
|
|
|
|
|
* are no set elements, in which case the first is
|
|
|
|
|
* reported on
|
|
|
|
|
*/
|
|
|
|
|
if (any_set && !(vp->flag & ISSET))
|
|
|
|
|
continue;
|
|
|
|
|
/* no arguments */
|
|
|
|
|
if (!thing && !flag) {
|
|
|
|
|
if (any_set == 1) {
|
|
|
|
|
shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
|
|
|
|
|
any_set = 2;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* AT&T ksh prints things like export, integer,
|
|
|
|
|
* leftadj, zerofill, etc., but POSIX says must
|
|
|
|
|
* be suitable for re-entry...
|
|
|
|
|
*/
|
|
|
|
|
shprintf(Tf_s_s, Ttypeset, "");
|
|
|
|
|
if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
|
|
|
|
|
shprintf(Tf__c_, 'n');
|
|
|
|
|
if ((vp->flag & INTEGER))
|
|
|
|
|
shprintf(Tf__c_, 'i');
|
|
|
|
|
if ((vp->flag & EXPORT))
|
|
|
|
|
shprintf(Tf__c_, 'x');
|
|
|
|
|
if ((vp->flag & RDONLY))
|
|
|
|
|
shprintf(Tf__c_, 'r');
|
|
|
|
|
if ((vp->flag & TRACE))
|
|
|
|
|
shprintf(Tf__c_, 't');
|
|
|
|
|
if ((vp->flag & LJUST))
|
|
|
|
|
shprintf("-L%d ", vp->u2.field);
|
|
|
|
|
if ((vp->flag & RJUST))
|
|
|
|
|
shprintf("-R%d ", vp->u2.field);
|
|
|
|
|
if ((vp->flag & ZEROFIL))
|
|
|
|
|
shprintf(Tf__c_, 'Z');
|
|
|
|
|
if ((vp->flag & LCASEV))
|
|
|
|
|
shprintf(Tf__c_, 'l');
|
|
|
|
|
if ((vp->flag & UCASEV_AL))
|
|
|
|
|
shprintf(Tf__c_, 'u');
|
|
|
|
|
if ((vp->flag & INT_U))
|
|
|
|
|
shprintf(Tf__c_, 'U');
|
|
|
|
|
} else if (pflag) {
|
|
|
|
|
shprintf(Tf_s_s, istset ? Ttypeset :
|
|
|
|
|
(flag & EXPORT) ? Texport : Treadonly, "");
|
|
|
|
|
}
|
|
|
|
|
if (any_set)
|
|
|
|
|
shprintf("%s[%lu]", vp->name, arrayindex(vp));
|
|
|
|
|
else
|
|
|
|
|
shf_puts(vp->name, shl_stdout);
|
|
|
|
|
if ((!thing && !flag && pflag) ||
|
|
|
|
|
(thing == '-' && (vp->flag & ISSET))) {
|
|
|
|
|
s = str_val(vp);
|
|
|
|
|
shf_putc('=', shl_stdout);
|
|
|
|
|
/* AT&T ksh can't have justified integers... */
|
|
|
|
|
if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
|
|
|
|
|
shf_puts(s, shl_stdout);
|
|
|
|
|
else
|
|
|
|
|
print_value_quoted(shl_stdout, s);
|
|
|
|
|
}
|
|
|
|
|
shf_putc('\n', shl_stdout);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Only report first 'element' of an array with
|
|
|
|
|
* no set elements.
|
|
|
|
|
*/
|
|
|
|
|
if (!any_set)
|
|
|
|
|
return;
|
2017-04-02 18:25:23 +02:00
|
|
|
|
} while (!(any_set & 4) && (vp = vp->u.array));
|
2017-04-02 17:51:20 +02:00
|
|
|
|
}
|