Fix typeset issues (LP: #993847)

This was actually more evil:
• use a recursive function to display blocks in reverse order,
  so that local variable values overwrite global ones
• add array support to typeset -p (from typeset -p -)
• display 'set -A varname' line before setting values, for -p
• if -p got arguments, only display those (from the innermost scope)

Also, the usual amount of code cleanup…
This commit is contained in:
tg 2012-05-09 23:21:00 +00:00
parent b89b96a4ab
commit 3125146b43
5 changed files with 169 additions and 152 deletions

14
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.536 2012/05/04 22:44:31 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.537 2012/05/09 23:20:55 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -29,7 +29,7 @@
# http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
expected-stdout:
@(#)MIRBSD KSH R40 2012/05/04
@(#)MIRBSD KSH R40 2012/05/09
description:
Check version of shell.
stdin:
@ -4821,7 +4821,8 @@ expected-stdout:
M
xxx[1]=7
N
typeset -i xxx
set -A xxx
typeset -i xxx[1]
---
name: regression-58
description:
@ -4922,6 +4923,9 @@ stdin:
typeset -
echo FNORD-8
} | fgrep FNORD
fnord=(42 23)
typeset -p fnord
echo FNORD-9
expected-stdout:
FNORD-0
FNORD-1
@ -4972,6 +4976,10 @@ expected-stdout:
FNORD_G=7
FNORD_H=8
FNORD-8
set -A fnord
typeset fnord[0]=42
typeset fnord[1]=23
FNORD-9
---
name: regression-64
description:

280
funcs.c
View File

@ -38,7 +38,7 @@
#endif
#endif
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.218 2012/05/04 22:18:24 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.219 2012/05/09 23:20:56 tg Exp $");
#if HAVE_KILLPG
/*
@ -111,7 +111,7 @@ const struct builtin mkshbuiltins[] = {
{"+true", c_true},
{"ulimit", c_ulimit},
{"+umask", c_umask},
{"*=unset", c_unset},
{Tsgunset, c_unset},
/* no =: AT&T manual wrong */
{Tpalias, c_alias},
{"+cd", c_cd},
@ -119,7 +119,7 @@ const struct builtin mkshbuiltins[] = {
{"chdir", c_cd},
{"+command", c_command},
{"echo", c_print},
{"*=export", c_typeset},
{Tsgexport, c_typeset},
{"+fc", c_fc},
{"+getopts", c_getopts},
{"=global", c_typeset},
@ -131,7 +131,7 @@ const struct builtin mkshbuiltins[] = {
{"printf", c_printf},
#endif
{"pwd", c_pwd},
{"*=readonly", c_typeset},
{Tsgreadonly, c_typeset},
{T_typeset, c_typeset},
{Tpunalias, c_unalias},
{"whence", c_whence},
@ -616,13 +616,16 @@ c_command(const char **wp)
}
/* typeset, global, export, and readonly */
static void c_typeset_vardump(struct tbl *, uint32_t, int, bool, bool);
static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
bool);
int
c_typeset(const char **wp)
{
struct block *l;
struct tbl *vp, **p;
uint32_t fset = 0, fclr = 0, flag;
int thing = 0, field = 0, base = 0, optc;
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;
@ -665,9 +668,9 @@ c_typeset(const char **wp)
* Here, the number must follow the RLZi option, but is optional
* (see the # kludge in ksh_getopt()).
*/
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
flag = 0;
switch (optc) {
switch (i) {
case 'L':
flag = LJUST;
fieldstr = builtin_opt.optarg;
@ -793,8 +796,10 @@ c_typeset(const char **wp)
}
/* set variables and attributes */
if (wp[builtin_opt.optind]) {
int i, rv = 0;
if (wp[builtin_opt.optind] &&
/* not "typeset -p varname" */
!(!func && pflag && !(fset | fclr))) {
int rv = 0;
struct tbl *f;
if (localv && !func)
@ -826,6 +831,7 @@ c_typeset(const char **wp)
return (rv);
}
set_refflag = SRF_NOP;
/* list variables and attributes */
/* no difference at this point.. */
@ -844,139 +850,135 @@ c_typeset(const char **wp)
shf_putc('\n', shl_stdout);
}
}
} else {
for (l = e->loc; l; l = l->next) {
for (p = ktsort(&l->vars); (vp = *p++); ) {
struct tbl *tvp;
bool any_set = false;
/*
* 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) {
any_set = true;
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))
continue;
if (flag && (vp->flag & flag) == 0)
continue;
for (; vp; vp = vp->u.array) {
/*
* Ignore array elements that aren't
* set unless there are no set elements,
* in which case the first is reported on
*/
if ((vp->flag&ARRAY) && any_set &&
!(vp->flag & ISSET))
continue;
/* no arguments */
if (thing == 0 && flag == 0) {
/*
* AT&T ksh prints things
* like export, integer,
* leftadj, zerofill, etc.,
* but POSIX says must
* be suitable for re-entry...
*/
shf_puts("typeset ", shl_stdout);
if (((vp->flag&(ARRAY|ASSOC))==ASSOC))
shprintf("%s ", "-n");
if ((vp->flag&INTEGER))
shprintf("%s ", "-i");
if ((vp->flag&EXPORT))
shprintf("%s ", "-x");
if ((vp->flag&RDONLY))
shprintf("%s ", "-r");
if ((vp->flag&TRACE))
shprintf("%s ", "-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("%s ", "-Z");
if ((vp->flag&LCASEV))
shprintf("%s ", "-l");
if ((vp->flag&UCASEV_AL))
shprintf("%s ", "-u");
if ((vp->flag&INT_U))
shprintf("%s ", "-U");
shf_puts(vp->name, shl_stdout);
if (pflag) {
char *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);
if (vp->flag & ARRAY)
break;
} else {
if (pflag)
shf_puts(istset ?
"typeset " :
(flag & EXPORT) ?
"export " :
"readonly ",
shl_stdout);
if ((vp->flag&ARRAY) && any_set)
shprintf("%s[%lu]",
vp->name,
arrayindex(vp));
else
shf_puts(vp->name, shl_stdout);
if (thing == '-' && (vp->flag&ISSET)) {
char *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)
break;
}
}
} else if (wp[builtin_opt.optind]) {
for (i = builtin_opt.optind; wp[i]; i++) {
varsearch(e->loc, &vp, wp[i], hash(wp[i]));
if (!vp)
continue;
c_typeset_vardump(vp, flag, thing, pflag, istset);
}
}
set_refflag = SRF_NOP;
} 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++))
c_typeset_vardump(vp, flag, thing, pflag, istset);
/*XXX doesnt this leak? */
}
static void
c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
bool istset)
{
struct tbl *tvp;
int any_set = 0;
char *s;
/*
* 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) {
any_set = 1;
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("%s %s %s\n", 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("%s %s", Ttypeset, "");
if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
shprintf("%s ", "-n");
if ((vp->flag & INTEGER))
shprintf("%s ", "-i");
if ((vp->flag & EXPORT))
shprintf("%s ", "-x");
if ((vp->flag & RDONLY))
shprintf("%s ", "-r");
if ((vp->flag & TRACE))
shprintf("%s ", "-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("%s ", "-Z");
if ((vp->flag & LCASEV))
shprintf("%s ", "-l");
if ((vp->flag & UCASEV_AL))
shprintf("%s ", "-u");
if ((vp->flag & INT_U))
shprintf("%s ", "-U");
} else if (pflag) {
shprintf("%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.
*/
} while (any_set && (vp = vp->u.array));
}
int
c_alias(const char **wp)
{

11
sh.h
View File

@ -157,9 +157,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.559 2012/05/05 17:37:44 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.560 2012/05/09 23:20:58 tg Exp $");
#endif
#define MKSH_VERSION "R40 2012/05/04"
#define MKSH_VERSION "R40 2012/05/09"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@ -730,6 +730,12 @@ EXTERN const char Tpunalias[] E_INIT("+unalias");
#define Tunalias (Tpunalias + 1) /* "unalias" */
EXTERN const char Tsgset[] E_INIT("*=set");
#define Tset (Tsgset + 2) /* "set" */
EXTERN const char Tsgunset[] E_INIT("*=unset");
#define Tunset (Tsgunset + 2) /* "unset" */
EXTERN const char Tsgexport[] E_INIT("*=export");
#define Texport (Tsgexport + 2) /* "export" */
EXTERN const char Tsgreadonly[] E_INIT("*=readonly");
#define Treadonly (Tsgreadonly + 2) /* "readonly" */
EXTERN const char Tgbuiltin[] E_INIT("=builtin");
#define Tbuiltin (Tgbuiltin + 1) /* "builtin" */
EXTERN const char T_function[] E_INIT(" function");
@ -1895,6 +1901,7 @@ void fpFUNCTf(struct shf *, int, bool, const char *, struct op *);
void newblock(void);
void popblock(void);
void initvar(void);
struct block *varsearch(struct block *, struct tbl **, const char *, uint32_t);
struct tbl *global(const char *);
struct tbl *local(const char *, bool);
char *str_val(struct tbl *);

10
syn.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.74 2012/03/03 21:30:58 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.75 2012/05/09 23:21:00 tg Exp $");
extern short subshell_nesting_level;
extern void yyskiputf8bom(void);
@ -52,7 +52,7 @@ static struct op *newtp(int);
static void syntaxerr(const char *) MKSH_A_NORETURN;
static void nesting_push(struct nesting_state *, int);
static void nesting_pop(struct nesting_state *);
static int assign_command(char *);
static int assign_command(const char *);
static int inalias(struct source *);
static Test_op dbtestp_isa(Test_env *, Test_meta);
static const char *dbtestp_getopnd(Test_env *, Test_op, bool);
@ -914,13 +914,13 @@ compile(Source *s, bool skiputf8bom)
* $
*/
static int
assign_command(char *s)
assign_command(const char *s)
{
if (!*s)
return (0);
return ((strcmp(s, Talias) == 0) ||
(strcmp(s, "export") == 0) ||
(strcmp(s, "readonly") == 0) ||
(strcmp(s, Texport) == 0) ||
(strcmp(s, Treadonly) == 0) ||
(strcmp(s, Ttypeset) == 0));
}

6
var.c
View File

@ -27,7 +27,7 @@
#include <sys/sysctl.h>
#endif
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.148 2012/05/04 21:47:04 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.149 2012/05/09 23:21:00 tg Exp $");
/*-
* Variables
@ -141,8 +141,8 @@ initvar(void)
}
}
/* common code for several functions below */
static struct block *
/* common code for several functions below and c_typeset() */
struct block *
varsearch(struct block *l, struct tbl **vpp, const char *vn, uint32_t h)
{
register struct tbl *vp;