/*-
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- * 2010, 2011
+ * 2010, 2011, 2012, 2013
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
#endif
#endif
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.197 2011/09/07 15:24:15 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.238 2013/02/18 22:47:32 tg Exp $");
#if HAVE_KILLPG
/*
#define c_ulimit c_true
#endif
-#if defined(ANDROID)
-static int c_android_lsmod(const char **);
-#endif
+/* getn() that prints error */
+static int
+bi_getn(const char *as, int *ai)
+{
+ int rv;
+
+ if (!(rv = getn(as, ai)))
+ bi_errorf("%s: %s", as, "bad number");
+ return (rv);
+}
static int
c_true(const char **wp MKSH_A_UNUSED)
{"+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},
{"chdir", c_cd},
{"+command", c_command},
{"echo", c_print},
- {"*=export", c_typeset},
+ {Tsgexport, c_typeset},
{"+fc", c_fc},
{"+getopts", c_getopts},
{"=global", c_typeset},
{"printf", c_printf},
#endif
{"pwd", c_pwd},
- {"*=readonly", c_typeset},
+ {Tsgreadonly, c_typeset},
{T_typeset, c_typeset},
{Tpunalias, c_unalias},
{"whence", c_whence},
{"+bg", c_fgbg},
{"+fg", c_fgbg},
#endif
+#ifndef MKSH_NO_CMDLINE_EDITING
{"bind", c_bind},
+#endif
{"cat", c_cat},
#if HAVE_MKNOD
{"mknod", c_mknod},
/* alias to "true" for historical reasons */
{"domainname", c_true},
#endif
-#if defined(ANDROID)
- {"lsmod", c_android_lsmod},
-#endif
{NULL, (int (*)(const char **))NULL}
};
static Test_op ptest_isa(Test_env *, Test_meta);
static const char *ptest_getopnd(Test_env *, Test_op, bool);
static void ptest_error(Test_env *, int, const char *);
-static char *kill_fmt_entry(char *, size_t, int, const void *);
+static char *kill_fmt_entry(char *, size_t, unsigned int, const void *);
static void p_time(struct shf *, bool, long, int, int,
- const char *, const char *)
- MKSH_A_NONNULL((__nonnull__ (6, 7)));
+ const char *, const char *);
int
c_pwd(const char **wp)
p = NULL;
if (!p && !(p = allocd = ksh_get_wd())) {
bi_errorf("%s: %s", "can't determine current directory",
- strerror(errno));
+ cstrerror(errno));
return (1);
}
shprintf("%s\n", p);
#define PO_HIST BIT(3) /* print to history instead of stdout */
#define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */
int fd = 1, c;
- int flags = PO_EXPAND|PO_NL;
+ int flags = PO_EXPAND | PO_NL;
const char *s, *emsg;
XString xs;
char *xp;
if (wp[0][0] == 'e') {
/* echo builtin */
wp++;
- if (Flag(FPOSIX) || Flag(FSH) || Flag(FAS_BUILTIN)) {
+#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
+ if (Flag(FSH)) {
+ /*
+ * MidnightBSD /bin/sh needs a BSD echo, that is,
+ * one that supports -e but does not enable it by
+ * default
+ */
+ flags = PO_NL;
+ }
+#endif
+ if (Flag(FPOSIX) ||
+#ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT
+ Flag(FSH) ||
+#endif
+ Flag(FAS_BUILTIN)) {
/* Debian Policy 10.4 compliant "echo" builtin */
if (*wp && !strcmp(*wp, "-n")) {
/* we recognise "-n" only as the first arg */
Talias);
if (!iam_whence && !vflag)
shprintf("%s %s=", Talias, id);
- print_value_quoted(tp->val.s);
+ print_value_quoted(shl_stdout, tp->val.s);
break;
case CFUNC:
if (vflag) {
}
/* 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, base, optc;
+ int thing = 0, field = 0, base = 0, i;
+ struct block *l;
const char *opts;
- const char *fieldstr, *basestr;
+ const char *fieldstr = NULL, *basestr = NULL;
bool localv = false, func = false, pflag = false, istset = true;
switch (**wp) {
/* see comment below regarding possible opions */
opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
- fieldstr = basestr = NULL;
builtin_opt.flags |= GF_PLUSOPT;
/*
* AT&T ksh seems to have 0-9 as options which are multiplied
* 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;
flag = EXPORT;
break;
case '?':
+ errout:
+ set_refflag = SRF_NOP;
return (1);
}
if (builtin_opt.info & GI_PLUS) {
}
}
- field = 0;
if (fieldstr && !bi_getn(fieldstr, &field))
- return (1);
- base = 0;
- if (basestr && !bi_getn(basestr, &base))
- return (1);
+ goto errout;
+ if (basestr && (!bi_getn(basestr, &base) || base < 1 || base > 36)) {
+ bi_errorf("%s: %s", "bad integer base", basestr);
+ goto errout;
+ }
if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
(wp[builtin_opt.optind][0] == '-' ||
if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
set_refflag != SRF_NOP)) {
bi_errorf("only -t, -u and -x options may be used with -f");
- set_refflag = SRF_NOP;
- return (1);
+ goto errout;
}
if (wp[builtin_opt.optind]) {
/*
}
/* 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)
}
} else if (!typeset(wp[i], fset, fclr, field, base)) {
bi_errorf("%s: %s", wp[i], "not identifier");
- set_refflag = SRF_NOP;
- return (1);
+ goto errout;
}
}
set_refflag = SRF_NOP;
return (rv);
}
+ set_refflag = SRF_NOP;
/* list variables and attributes */
/* no difference at this point.. */
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(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(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]));
+ c_typeset_vardump(vp, flag, thing, pflag, istset);
}
- }
+ } 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 doesn’t 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;
+
+ 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) {
+ 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.
+ */
+ if (!any_set)
+ return;
+ } while ((vp = vp->u.array));
+}
+
int
c_alias(const char **wp)
{
shf_puts(ap->name, shl_stdout);
if (prefix != '+') {
shf_putc('=', shl_stdout);
- print_value_quoted(ap->val.s);
+ print_value_quoted(shl_stdout, ap->val.s);
}
shf_putc('\n', shl_stdout);
}
shf_puts(ap->name, shl_stdout);
if (prefix != '+') {
shf_putc('=', shl_stdout);
- print_value_quoted(ap->val.s);
+ print_value_quoted(shl_stdout, ap->val.s);
}
shf_putc('\n', shl_stdout);
} else {
/* format a single kill item */
static char *
-kill_fmt_entry(char *buf, size_t buflen, int i, const void *arg)
+kill_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
{
const struct kill_info *ki = (const struct kill_info *)arg;
i++;
- shf_snprintf(buf, buflen, "%*d %*s %s",
+ shf_snprintf(buf, buflen, "%*u %*s %s",
ki->num_width, i,
ki->name_width, sigtraps[i].name,
sigtraps[i].mess);
/* assume old style options if -digits or -UPPERCASE */
if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
ksh_isupper(p[1]))) {
- if (!(t = gettrap(p + 1, true))) {
+ if (!(t = gettrap(p + 1, false))) {
bi_errorf("bad signal '%s'", p + 1);
return (1);
}
mess_cols = w;
}
- print_columns(shl_stdout, NSIG - 1,
+ print_columns(shl_stdout, (unsigned int)(NSIG - 1),
kill_fmt_entry, (void *)&ki,
ki.num_width + 1 + ki.name_width + 1 + mess_octs,
ki.num_width + 1 + ki.name_width + 1 + mess_cols,
rv = 1;
} else {
if (mksh_kill(n, sig) < 0) {
- bi_errorf("%s: %s", p, strerror(errno));
+ bi_errorf("%s: %s", p, cstrerror(errno));
rv = 1;
}
}
return (optc < 0 ? 1 : rv);
}
+#ifndef MKSH_NO_CMDLINE_EDITING
int
c_bind(const char **wp)
{
return (rv);
}
+#endif
int
c_shift(const char **wp)
return (1);
}
if ((file = search_path(cp, path, R_OK, &errcode)) == NULL) {
- bi_errorf("%s: %s", cp, strerror(errcode));
+ bi_errorf("%s: %s", cp, cstrerror(errcode));
return (1);
}
argc = 0;
argv = NULL;
}
- if ((i = include(file, argc, argv, 0)) < 0) {
+ if ((i = include(file, argc, argv, false)) < 0) {
/* should not happen */
- bi_errorf("%s: %s", cp, strerror(errno));
+ bi_errorf("%s: %s", cp, cstrerror(errno));
return (1);
}
return (i);
return (rv);
}
+static char REPLY[] = "REPLY";
int
c_read(const char **wp)
{
#define is_ifsws(c) (ctype((c), C_IFS) && ctype((c), C_IFSWS))
- static char REPLY[] = "REPLY";
int c, fd = 0, rv = 0, lastparm = 0;
bool savehist = false, intoarray = false, aschars = false;
bool rawmode = false, expanding = false;
const char *ccp;
XString xs;
ptrdiff_t xsave = 0;
- struct termios tios;
+ mksh_ttyst tios;
bool restore_tios = false;
#if HAVE_SELECT
bool hastimeout = false;
#if HAVE_SELECT
case 't':
if (parse_usec(builtin_opt.optarg, &tv)) {
- bi_errorf("%s: %s '%s'", Tsynerr, strerror(errno),
+ bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno),
builtin_opt.optarg);
return (2);
}
#if HAVE_SELECT
if (hastimeout) {
- gettimeofday(&tvlim, NULL);
+ mksh_TIME(tvlim);
timeradd(&tvlim, &tv, &tvlim);
}
#endif
fd_set fdset;
FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
- gettimeofday(&tv, NULL);
+ FD_SET((unsigned int)fd, &fdset);
+ mksh_TIME(tv);
timersub(&tvlim, &tv, &tv);
if (tv.tv_sec < 0) {
/* timeout expired globally */
rv = 1;
goto c_read_out;
default:
- bi_errorf("%s: %s", Tselect, strerror(errno));
+ bi_errorf("%s: %s", Tselect, cstrerror(errno));
rv = 2;
goto c_read_out;
}
vp = global(*wp);
if (vp->flag & RDONLY) {
c_read_splitro:
- bi_errorf("%s: %s", *wp, "is read only");
+ bi_errorf("read-only: %s", *wp);
c_read_spliterr:
rv = 2;
afree(cp, ATEMP);
afree(allocd, ATEMP);
Xfree(xs, xp);
if (restore_tios)
- tcsetattr(fd, TCSADRAIN, &tios);
+ mksh_tcset(fd, &tios);
return (rv);
#undef is_ifsws
}
* If there are no arguments, or only null arguments,
* eval shall return a zero exit status; ...
*/
- /* exstat = subst_exstat; */ /* AT&T ksh88 */
- exstat = 0; /* SUSv4 */
+ /* AT&T ksh88: use subst_exstat */
+ /* exstat = subst_exstat; */
+ /* SUSv4: OR with a high value never written otherwise */
+ exstat |= 0x4000;
savef = Flag(FERREXIT);
- Flag(FERREXIT) = 0;
+ Flag(FERREXIT) |= 0x80;
rv = shell(s, false);
Flag(FERREXIT) = savef;
source = saves;
afree(s, ATEMP);
+ if (exstat & 0x4000)
+ /* detect old exstat, use 0 in that case */
+ rv = 0;
return (rv);
}
for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
if (p->trap != NULL) {
shf_puts("trap -- ", shl_stdout);
- print_value_quoted(p->trap);
+ print_value_quoted(shl_stdout, p->trap);
shprintf(" %s\n", p->name);
}
return (0);
const char *arg;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
+ goto c_exitreturn_err;
arg = wp[builtin_opt.optind];
if (arg) {
exstat = 1;
warningf(true, "%s: %s", arg, "bad number");
} else
- exstat = n;
+ exstat = n & 0xFF;
} else if (trap_exstat != -1)
exstat = trap_exstat;
if (wp[0][0] == 'r') {
}
if (how == LEXIT && !really_exit && j_stopped_running()) {
- really_exit = 1;
+ really_exit = true;
how = LSHELL;
}
quitenv(NULL);
unwind(how);
/* NOTREACHED */
+
+ c_exitreturn_err:
+ return (1);
}
int
c_brkcont(const char **wp)
{
- int n, quit;
+ unsigned int quit;
+ int n;
struct env *ep, *last_ep = NULL;
const char *arg;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
+ goto c_brkcont_err;
arg = wp[builtin_opt.optind];
if (!arg)
n = 1;
else if (!bi_getn(arg, &n))
- return (1);
- quit = n;
- if (quit <= 0) {
+ goto c_brkcont_err;
+ if (n <= 0) {
/* AT&T ksh does this for non-interactive shells only - weird */
bi_errorf("%s: %s", arg, "bad value");
- return (1);
+ goto c_brkcont_err;
}
+ quit = (unsigned int)n;
/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
* can. We print a message 'cause it helps in debugging
* scripts, but don't generate an error (ie, keep going).
*/
- if (n == quit) {
+ if ((unsigned int)n == quit) {
warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
return (0);
}
*/
if (last_ep)
last_ep->flags &= ~EF_BRKCONT_PASS;
- warningf(true, "%s: can only %s %d level(s)",
- wp[0], wp[0], n - quit);
+ warningf(true, "%s: can only %s %u level(s)",
+ wp[0], wp[0], (unsigned int)n - quit);
}
unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
/* NOTREACHED */
+
+ c_brkcont_err:
+ return (1);
}
int
* if there are no command substitutions).
* Switched ksh (!posix !sh) to POSIX in mksh R39b.
*/
+#ifdef MKSH_LEGACY_MODE
+ return (subst_exstat);
+#else
return (Flag(FSH) ? subst_exstat : 0);
+#endif
}
int
afree(cp, ATEMP);
if ((vp->flag&RDONLY)) {
- warningf(true, "%s: %s", vp->name,
- "is read only");
+ warningf(true, "read-only: %s", vp->name);
rv = 1;
} else
unset(vp, optc);
struct rusage ru0, ru1, cru0, cru1;
struct timeval usrtime, systime, tv0, tv1;
- gettimeofday(&tv0, NULL);
+ mksh_TIME(tv0);
getrusage(RUSAGE_SELF, &ru0);
getrusage(RUSAGE_CHILDREN, &cru0);
if (t->left) {
rv = execute(t->left, f | XTIME, xerrok);
if (t->left->type == TCOM)
tf |= t->left->str[0];
- gettimeofday(&tv1, NULL);
+ mksh_TIME(tv1);
getrusage(RUSAGE_SELF, &ru1);
getrusage(RUSAGE_CHILDREN, &cru1);
} else
for (i = 0; i < NUFILE; i++) {
if (e->savefd[i] > 0)
close(e->savefd[i]);
+#ifndef MKSH_LEGACY_MODE
/*
* keep all file descriptors > 2 private for ksh,
* but not for POSIX or legacy/kludge sh
if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
e->savefd[i])
fcntl(i, F_SETFD, FD_CLOEXEC);
+#endif
}
e->savefd = NULL;
}
goto c_mknod_failed;
} else if (mkfifo(argv[0], mode)) {
c_mknod_failed:
- bi_errorf("%s: %s", argv[0], strerror(errno));
+ bi_errorf("%s: %s", argv[0], cstrerror(errno));
c_mknod_err:
rv = 1;
}
#endif
/*-
- test(1) accepts the following grammar:
+ test(1) roughly accepts the following grammar:
oexpr ::= aexpr | aexpr "-o" oexpr ;
aexpr ::= nexpr | nexpr "-a" aexpr ;
nexpr ::= primary | "!" nexpr ;
binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
"-nt"|"-ot"|"-ef"|
- "<"|">" # rules used for [[ .. ]] expressions
+ "<"|">" # rules used for [[ ... ]] expressions
;
- operand ::= <any thing>
+ operand ::= <anything>
*/
/* POSIX says > 1 for errors */
-#define T_ERR_EXIT 2
+#define T_ERR_EXIT 2
int
c_test(const char **wp)
{
- int argc, res;
+ int argc, rv, invert = 0;
Test_env te;
+ Test_op op;
+ const char *lhs, **swp;
te.flags = 0;
te.isa = ptest_isa;
for (argc = 0; wp[argc]; argc++)
;
+ mkssert(argc > 0);
+ mkssert(wp[0] != NULL);
if (strcmp(wp[0], "[") == 0) {
if (strcmp(wp[--argc], "]") != 0) {
te.wp_end = wp + argc;
/*
- * Handle the special cases from POSIX.2, section 4.62.4.
- * Implementation of all the rules isn't necessary since
- * our parser does the right thing for the omitted steps.
+ * Attempt to conform to POSIX special cases. This is pretty
+ * dumb code straight-forward from the 2008 spec, but unless
+ * the old pdksh code doesn't live from so many assumptions.
+ * It does, though, inline some calls to '(*te.funcname)()'.
*/
- if (argc <= 5) {
- const char **owp = wp, **owpend = te.wp_end;
- int invert = 0;
- Test_op op;
- const char *opnd1, *opnd2;
-
- if (argc >= 2 && ((*te.isa)(&te, TM_OPAREN))) {
- te.pos.wp = te.wp_end - 1;
- if ((*te.isa)(&te, TM_CPAREN)) {
- argc -= 2;
- te.wp_end--;
- te.pos.wp = owp + 2;
- } else {
- te.pos.wp = owp + 1;
- te.wp_end = owpend;
- }
- }
-
- while (--argc >= 0) {
- if ((*te.isa)(&te, TM_END))
- return (!0);
- if (argc == 3) {
- opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
- if ((op = (*te.isa)(&te, TM_BINOP))) {
- opnd2 = (*te.getopnd)(&te, op, 1);
- res = (*te.eval)(&te, op, opnd1,
- opnd2, 1);
- if (te.flags & TEF_ERROR)
- return (T_ERR_EXIT);
- if (invert & 1)
- res = !res;
- return (!res);
- }
- /* back up to opnd1 */
- te.pos.wp--;
- }
- if (argc == 1) {
- opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
- res = (*te.eval)(&te, TO_STNZE, opnd1,
- NULL, 1);
- if (invert & 1)
- res = !res;
- return (!res);
- }
- if ((*te.isa)(&te, TM_NOT)) {
- invert++;
- } else
- break;
- }
- te.pos.wp = owp + 1;
- te.wp_end = owpend;
+ switch (argc - 1) {
+ case 0:
+ return (1);
+ case 1:
+ ptest_one:
+ op = TO_STNZE;
+ goto ptest_unary;
+ case 2:
+ ptest_two:
+ if (ptest_isa(&te, TM_NOT)) {
+ ++invert;
+ goto ptest_one;
+ }
+ if ((op = ptest_isa(&te, TM_UNOP))) {
+ ptest_unary:
+ rv = test_eval(&te, op, *te.pos.wp++, NULL, true);
+ ptest_out:
+ return ((invert & 1) ? rv : !rv);
+ }
+ /* let the parser deal with anything else */
+ break;
+ case 3:
+ ptest_three:
+ swp = te.pos.wp;
+ /* use inside knowledge of ptest_getopnd inlined below */
+ lhs = *te.pos.wp++;
+ if ((op = ptest_isa(&te, TM_BINOP))) {
+ /* test lhs op rhs */
+ rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
+ goto ptest_out;
+ }
+ /* back up to lhs */
+ te.pos.wp = swp;
+ if (ptest_isa(&te, TM_NOT)) {
+ ++invert;
+ goto ptest_two;
+ }
+ if (ptest_isa(&te, TM_OPAREN)) {
+ swp = te.pos.wp;
+ /* skip operand, without evaluation */
+ te.pos.wp++;
+ /* check for closing parenthesis */
+ op = ptest_isa(&te, TM_CPAREN);
+ /* back up to operand */
+ te.pos.wp = swp;
+ /* if there was a closing paren, handle it */
+ if (op)
+ goto ptest_one;
+ /* backing up is done before calling the parser */
+ }
+ /* let the parser deal with it */
+ break;
+ case 4:
+ if (ptest_isa(&te, TM_NOT)) {
+ ++invert;
+ goto ptest_three;
+ }
+ if (ptest_isa(&te, TM_OPAREN)) {
+ swp = te.pos.wp;
+ /* skip two operands, without evaluation */
+ te.pos.wp++;
+ te.pos.wp++;
+ /* check for closing parenthesis */
+ op = ptest_isa(&te, TM_CPAREN);
+ /* back up to first operand */
+ te.pos.wp = swp;
+ /* if there was a closing paren, handle it */
+ if (op)
+ goto ptest_two;
+ /* backing up is done before calling the parser */
+ }
+ /* defer this to the parser */
+ break;
}
+ /* "The results are unspecified." */
+ te.pos.wp = wp + 1;
return (test_parse(&te));
}
if (!do_eval)
return (0);
+#ifdef DEBUG
+ switch (op) {
+ /* Binary operators */
+ case TO_STEQL:
+ case TO_STNEQ:
+ case TO_STLT:
+ case TO_STGT:
+ case TO_INTEQ:
+ case TO_INTNE:
+ case TO_INTGT:
+ case TO_INTGE:
+ case TO_INTLT:
+ case TO_INTLE:
+ case TO_FILEQ:
+ case TO_FILNT:
+ case TO_FILOT:
+ /* consistency check, but does not happen in practice */
+ if (!opnd2) {
+ te->flags |= TEF_ERROR;
+ return (1);
+ }
+ break;
+ default:
+ /* for completeness of switch */
+ break;
+ }
+#endif
+
switch (op) {
/*
/* -h or -L */
case TO_FILSYM:
+#ifdef MKSH__NO_SYMLINK
+ return (0);
+#else
return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
+#endif
/* -S */
case TO_FILSOCK:
/* -s */
case TO_FILGZ:
- return (stat(opnd1, &b1) == 0 && b1.st_size > 0L);
+ return (stat(opnd1, &b1) == 0 && (off_t)b1.st_size > (off_t)0);
/* -t */
case TO_FILTT:
/* -O */
case TO_FILUID:
- return (stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid);
+ return (stat(opnd1, &b1) == 0 && (uid_t)b1.st_uid == ksheuid);
/* -G */
case TO_FILGID:
- return (stat(opnd1, &b1) == 0 && b1.st_gid == getegid());
+ return (stat(opnd1, &b1) == 0 && (gid_t)b1.st_gid == getegid());
/*
* Binary Operators
ptest_isa(Test_env *te, Test_meta meta)
{
/* Order important - indexed by Test_meta values */
- static const char *const tokens[] = {
+ static const char * const tokens[] = {
"-o", "-a", "!", "(", ")"
};
Test_op rv;
struct limits {
const char *name;
int resource; /* resource to get/set */
- int factor; /* multiply by to get rlim_{cur,max} values */
+ unsigned int factor; /* multiply by to get rlim_{cur,max} values */
char option;
};
#endif
{ NULL, 0, 0, 0 }
};
- static char opts[3 + NELEM(limits)];
+ static const char opts[] = "a"
+#ifdef RLIMIT_SBSIZE
+ "B"
+#endif
+#ifdef RLIMIT_TCACHE
+ "C"
+#endif
+#ifdef RLIMIT_CORE
+ "c"
+#endif
+#ifdef RLIMIT_DATA
+ "d"
+#endif
+#ifdef RLIMIT_NICE
+ "e"
+#endif
+#ifdef RLIMIT_FSIZE
+ "f"
+#endif
+ "H"
+#ifdef RLIMIT_SIGPENDING
+ "i"
+#endif
+#ifdef RLIMIT_LOCKS
+ "L"
+#endif
+#ifdef RLIMIT_MEMLOCK
+ "l"
+#endif
+#ifdef RLIMIT_AIO_MEM
+ "M"
+#endif
+#if defined(ULIMIT_M_IS_RSS) || defined(ULIMIT_M_IS_VMEM)
+ "m"
+#endif
+#ifdef RLIMIT_NOFILE
+ "n"
+#endif
+#ifdef RLIMIT_AIO_OPS
+ "O"
+#endif
+#ifdef RLIMIT_PTHREAD
+ "P"
+#endif
+#ifdef RLIMIT_NPROC
+ "p"
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ "q"
+#endif
+#ifdef RLIMIT_RTPRIO
+ "r"
+#endif
+ "S"
+#ifdef RLIMIT_STACK
+ "s"
+#endif
+#ifdef RLIMIT_TIME
+ "T"
+#endif
+#ifdef RLIMIT_CPU
+ "t"
+#endif
+#ifdef RLIMIT_NOVMON
+ "V"
+#endif
+#if defined(ULIMIT_V_IS_VMEM) || defined(ULIMIT_V_IS_AS)
+ "v"
+#endif
+#ifdef RLIMIT_SWAP
+ "w"
+#endif
+ ;
int how = SOFT | HARD, optc, what = 'f';
bool all = false;
const struct limits *l;
- if (!opts[0]) {
- /* build options string on first call - yuck */
- char *p = opts;
-
- *p++ = 'H'; *p++ = 'S'; *p++ = 'a';
- for (l = limits; l->name; l++)
- *p++ = l->option;
- *p = '\0';
- }
-
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
switch (optc) {
case 'H':
all = true;
break;
case '?':
- bi_errorf("%s: %s", "usage",
- "ulimit [-acdfHLlmnpSsTtvw] [value]");
+ bi_errorf("usage: ulimit [-%s] [value]", opts);
return (1);
default:
what = optc;
if (strcmp(v, "unlimited") == 0)
val = (rlim_t)RLIM_INFINITY;
else {
- mksh_ari_t rval;
+ mksh_uari_t rval;
- if (!evaluate(v, &rval, KSH_RETURN_ERROR, false))
+ if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false))
return (1);
/*
* Avoid problems caused by typos that evaluate misses due
if (errno == EPERM)
bi_errorf("%s exceeds allowable %s limit", v, l->name);
else
- bi_errorf("bad %s limit: %s", l->name, strerror(errno));
+ bi_errorf("bad %s limit: %s", l->name, cstrerror(errno));
return (1);
}
if (val == (rlim_t)RLIM_INFINITY)
shf_puts("unlimited\n", shl_stdout);
else
- shprintf("%ld\n", (long)(val / l->factor));
+ shprintf("%lu\n", (unsigned long)(val / l->factor));
}
#endif
bi_errorf(Tsynerr);
else if ((rv = rename(wp[0], wp[1])) != 0) {
rv = errno;
- bi_errorf("%s: %s", "failed", strerror(rv));
+ bi_errorf("%s: %s", "failed", cstrerror(rv));
}
return (rv);
bi_errorf(Tsynerr);
else if ((buf = do_realpath(wp[0])) == NULL) {
rv = errno;
- bi_errorf("%s: %s", wp[0], strerror(rv));
+ bi_errorf("%s: %s", wp[0], cstrerror(rv));
if ((unsigned int)rv > 255)
rv = 255;
} else {
int
c_cat(const char **wp)
{
- int fd = STDIN_FILENO, rv;
+ int fd = STDIN_FILENO, rv, eno;
ssize_t n, w;
const char *fn = "<stdin>";
char *buf, *cp;
#define MKSH_CAT_BUFSIZ 4096
- if ((buf = malloc_osfunc(MKSH_CAT_BUFSIZ)) == NULL) {
- bi_errorf(Toomem, (unsigned long)MKSH_CAT_BUFSIZ);
- return (1);
- }
-
/* parse options: POSIX demands we support "-u" as no-op */
while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
switch (rv) {
wp += builtin_opt.optind;
rv = 0;
+ if ((buf = malloc_osfunc(MKSH_CAT_BUFSIZ)) == NULL) {
+ bi_errorf(Toomem, (unsigned long)MKSH_CAT_BUFSIZ);
+ return (1);
+ }
+
do {
if (*wp) {
fn = *wp++;
if (fn[0] == '-' && fn[1] == '\0')
fd = STDIN_FILENO;
else if ((fd = open(fn, O_RDONLY)) < 0) {
- rv = errno;
- bi_errorf("%s: %s", fn, strerror(rv));
+ eno = errno;
+ bi_errorf("%s: %s", fn, cstrerror(eno));
rv = 1;
continue;
}
}
while (/* CONSTCOND */ 1) {
n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ);
+ eno = errno;
+ /* give the user a chance to ^C out */
+ intrcheck();
if (n == -1) {
- if (errno == EINTR) {
- /* give the user a chance to ^C out */
- intrcheck();
+ if (eno == EINTR) {
/* interrupted, try again */
continue;
}
/* an error occured during reading */
- rv = errno;
- bi_errorf("%s: %s", fn, strerror(rv));
+ bi_errorf("%s: %s", fn, cstrerror(eno));
rv = 1;
break;
} else if (n == 0)
/* interrupted, try again */
continue;
/* an error occured during writing */
- rv = errno;
+ eno = errno;
bi_errorf("%s: %s", "<stdout>",
- strerror(rv));
+ cstrerror(eno));
rv = 1;
if (fd != STDIN_FILENO)
close(fd);
if (!wp[0] || wp[1])
bi_errorf(Tsynerr);
else if (parse_usec(wp[0], &tv))
- bi_errorf("%s: %s '%s'", Tsynerr, strerror(errno), wp[0]);
+ bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno), wp[0]);
else {
#ifndef MKSH_NOPROSPECTOFWORK
- sigset_t omask;
+ sigset_t omask, bmask;
- /* block SIGCHLD from interrupting us, though */
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+ /* block a number of signals from interrupting us, though */
+ (void)sigemptyset(&bmask);
+ (void)sigaddset(&bmask, SIGPIPE);
+ (void)sigaddset(&bmask, SIGCHLD);
+#ifdef SIGWINCH
+ (void)sigaddset(&bmask, SIGWINCH);
+#endif
+#ifdef SIGINFO
+ (void)sigaddset(&bmask, SIGINFO);
+#endif
+#ifdef SIGUSR1
+ (void)sigaddset(&bmask, SIGUSR1);
+#endif
+#ifdef SIGUSR2
+ (void)sigaddset(&bmask, SIGUSR2);
+#endif
+ sigprocmask(SIG_BLOCK, &bmask, &omask);
#endif
- if (select(0, NULL, NULL, NULL, &tv) == 0 || errno == EINTR)
+ if (select(1, NULL, NULL, NULL, &tv) == 0 || errno == EINTR)
/*
* strictly speaking only for SIGALRM, but the
* execution may be interrupted by other signals
*/
rv = 0;
else
- bi_errorf("%s: %s", Tselect, strerror(errno));
+ bi_errorf("%s: %s", Tselect, cstrerror(errno));
#ifndef MKSH_NOPROSPECTOFWORK
+ /* this will re-schedule signal delivery */
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
}
return (rv);
}
#endif
-
-#if defined(ANDROID)
-static int
-c_android_lsmod(const char **wp MKSH_A_UNUSED)
-{
- const char *cwp[3] = { "cat", "/proc/modules", NULL };
-
- builtin_argv0 = cwp[0];
- return (c_cat(cwp));
-}
-#endif