-/* $OpenBSD: main.c,v 1.46 2010/05/19 17:36:08 jasper Exp $ */
+/* $OpenBSD: main.c,v 1.47 2011/09/07 11:33:25 otto Exp $ */
/* $OpenBSD: tty.c,v 1.9 2006/03/14 22:08:01 deraadt Exp $ */
/* $OpenBSD: io.c,v 1.22 2006/03/17 16:30:13 millert Exp $ */
/* $OpenBSD: table.c,v 1.13 2009/01/17 22:06:44 millert Exp $ */
/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* of said person's immediate fault when using the work as intended.
*/
-#define EXTERN
+#define EXTERN
#include "sh.h"
#if HAVE_LANGINFO_CODESET
#include <locale.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.167 2010/07/04 17:45:15 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.200 2011/10/07 19:51:28 tg Exp $");
extern char **environ;
-#if !HAVE_SETRESUGID
-extern uid_t kshuid;
-extern gid_t kshgid, kshegid;
-#endif
-
#ifndef MKSHRC_PATH
#define MKSHRC_PATH "~/.mkshrc"
#endif
#define MKSH_DEFAULT_TMPDIR "/tmp"
#endif
+void chvt_reinit(void);
static void reclaim(void);
static void remove_temps(struct temp *);
-void chvt_reinit(void);
-Source *mksh_init(int, const char *[]);
+static mksh_uari_t rndsetup(void);
#ifdef SIGWINCH
static void x_sigwinch(int);
#endif
"${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0}";
static const char *initcoms[] = {
- T_typeset, "-r", initvsn, NULL,
- T_typeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
- T_typeset, "-i10", "COLUMNS", "LINES", "OPTIND", "PGRP", "PPID",
- "RANDOM", "SECONDS", "TMOUT", "USER_ID", NULL,
- "alias",
+ Ttypeset, "-r", initvsn, NULL,
+ Ttypeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
+ Ttypeset, "-i10", "SECONDS", "TMOUT", NULL,
+ Talias,
"integer=typeset -i",
- T_local_typeset,
- "hash=alias -t", /* not "alias -t --": hash -r needs to work */
+ Tlocal_typeset,
+ /* not "alias -t --": hash -r needs to work */
+ "hash=alias -t",
"type=whence -v",
-#ifndef MKSH_UNEMPLOYED
+#if !defined(ANDROID) && !defined(MKSH_UNEMPLOYED)
+ /* not in Android for political reasons */
+ /* not in ARGE mksh due to no job control */
+ "stop=kill -STOP",
"suspend=kill -STOP $$",
#endif
"autoload=typeset -fu",
"history=fc -l",
"nameref=typeset -n",
"nohup=nohup ",
- r_fc_e_,
+ Tr_fc_e_dash,
"source=PATH=$PATH:. command .",
"login=exec login",
NULL,
/* this is what AT&T ksh seems to track, with the addition of emacs */
- "alias", "-tU",
+ Talias, "-tU",
"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
"make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL,
NULL
};
+static const char *restr_com[] = {
+ Ttypeset, "-r", "PATH", "ENV", "SHELL", NULL
+};
+
static int initio_done;
-struct env *e = &kshstate_v.env_;
+/* top-level parsing and execution environment */
+static struct env env;
+struct env *e = &env;
+
+static mksh_uari_t
+rndsetup(void)
+{
+ register uint32_t h;
+ struct {
+ ALLOC_ITEM alloc_INT;
+ void *dataptr, *stkptr, *mallocptr;
+ sigjmp_buf jbuf;
+ struct timeval tv;
+ struct timezone tz;
+ } *bufptr;
+ char *cp;
+
+ cp = alloc(sizeof(*bufptr) - ALLOC_SIZE, APERM);
+#ifdef DEBUG
+ /* clear the allocated space, for valgrind */
+ memset(cp, 0, sizeof(*bufptr) - ALLOC_SIZE);
+#endif
+ /* undo what alloc() did to the malloc result address */
+ bufptr = (void *)(cp - ALLOC_SIZE);
+ /* PIE or something similar provides us with deltas here */
+ bufptr->dataptr = &rndsetupstate;
+ /* ASLR in at least Windows, Linux, some BSDs */
+ bufptr->stkptr = &bufptr;
+ /* randomised malloc in BSD (and possibly others) */
+ bufptr->mallocptr = bufptr;
+ /* glibc pointer guard */
+ sigsetjmp(bufptr->jbuf, 1);
+ /* introduce variation */
+ gettimeofday(&bufptr->tv, &bufptr->tz);
+
+ NZATInit(h);
+ /* variation through pid, ppid, and the works */
+ NZATUpdateMem(h, &rndsetupstate, sizeof(rndsetupstate));
+ /* some variation, some possibly entropy, depending on OE */
+ NZATUpdateMem(h, bufptr, sizeof(*bufptr));
+ NZAATFinish(h);
+
+ afree(cp, APERM);
+ return ((mksh_uari_t)h);
+}
void
chvt_reinit(void)
kshppid = getppid();
}
-Source *
-mksh_init(int argc, const char *argv[])
+static const char *empty_argv[] = {
+ "mksh", NULL
+};
+
+int
+main(int argc, const char *argv[])
{
int argi, i;
- Source *s;
+ Source *s = NULL;
struct block *l;
unsigned char restricted, errexit, utf_flag;
- const char **wp;
+ char *cp;
+ const char *ccp, **wp;
struct tbl *vp;
struct stat s_stdin;
#if !defined(_PATH_DEFPATH) && defined(_CS_PATH)
- size_t k;
- char *cp;
+ ssize_t k;
#endif
/* do things like getpgrp() et al. */
/* make sure argv[] is sane */
if (!*argv) {
- static const char *empty_argv[] = {
- "mksh", NULL
- };
-
argv = empty_argv;
argc = 1;
}
- kshname = *argv;
+ kshname = argv[0];
- ainit(&aperm); /* initialise permanent Area */
+ /* initialise permanent Area */
+ ainit(&aperm);
/* set up base environment */
- kshstate_v.env_.type = E_NONE;
- ainit(&kshstate_v.env_.area);
- newblock(); /* set up global l->vars and l->funs */
+ env.type = E_NONE;
+ ainit(&env.area);
+ /* set up global l->vars and l->funs */
+ newblock();
/* Do this first so output routines (eg, errorf, shellf) can work */
initio();
- argi = parse_args(argv, OF_FIRSTTIME, NULL);
- if (argi < 0)
- return (NULL);
+ /* determine the basename (without '-' or path) of the executable */
+ ccp = kshname;
+ goto begin_parse_kshname;
+ while ((i = ccp[argi++])) {
+ if (i == '/') {
+ ccp += argi;
+ begin_parse_kshname:
+ argi = 0;
+ if (*ccp == '-')
+ ++ccp;
+ }
+ }
+ if (!*ccp)
+ ccp = empty_argv[0];
+
+ /* define built-in commands and see if we were called as one */
+ ktinit(APERM, &builtins,
+ /* currently 50 builtins -> 80% of 64 (2^6) */
+ 6);
+ for (i = 0; mkshbuiltins[i].name != NULL; i++)
+ if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
+ mkshbuiltins[i].func)))
+ Flag(FAS_BUILTIN) = 1;
+
+ if (!Flag(FAS_BUILTIN)) {
+ /* check for -T option early */
+ argi = parse_args(argv, OF_FIRSTTIME, NULL);
+ if (argi < 0)
+ return (1);
+
+#ifdef MKSH_BINSHREDUCED
+ /* set FSH if we're called as -sh or /bin/sh or so */
+ if (!strcmp(ccp, "sh"))
+ change_flag(FSH, OF_FIRSTTIME, 1);
+#endif
+ }
initvar();
coproc_init();
/* set up variable and command dictionaries */
- ktinit(&taliases, APERM, 0);
- ktinit(&aliases, APERM, 0);
+ ktinit(APERM, &taliases, 0);
+ ktinit(APERM, &aliases, 0);
#ifndef MKSH_NOPWNAM
- ktinit(&homedirs, APERM, 0);
+ ktinit(APERM, &homedirs, 0);
#endif
/* define shell keywords */
initkeywords();
- /* define built-in commands */
- ktinit(&builtins, APERM,
- /* must be 80% of 2^n (currently 44 builtins) */ 64);
- for (i = 0; mkshbuiltins[i].name != NULL; i++)
- builtin(mkshbuiltins[i].name, mkshbuiltins[i].func);
-
init_histvec();
#ifdef _PATH_DEFPATH
def_path = _PATH_DEFPATH;
#else
#ifdef _CS_PATH
- if ((k = confstr(_CS_PATH, NULL, 0)) != (size_t)-1 && k > 0 &&
+ if ((k = confstr(_CS_PATH, NULL, 0)) > 0 &&
confstr(_CS_PATH, cp = alloc(k + 1, APERM), k + 1) == k + 1)
def_path = cp;
else
def_path = "/bin:/usr/bin:/sbin:/usr/sbin";
#endif
- /* Set PATH to def_path (will set the path global variable).
+ /*
+ * Set PATH to def_path (will set the path global variable).
* (import of environment below will probably change this setting).
*/
vp = global("PATH");
/* setstr can't fail here */
setstr(vp, def_path, KSH_RETURN_ERROR);
- /* Turn on nohup by default for now - will change to off
+ /*
+ * Turn on nohup by default for now - will change to off
* by default once people are aware of its existence
* (AT&T ksh does not have a nohup option - it always sends
* the hup).
*/
Flag(FNOHUP) = 1;
- /* Turn on brace expansion by default. AT&T kshs that have
+ /*
+ * Turn on brace expansion by default. AT&T kshs that have
* alternation always have it on.
*/
Flag(FBRACEEXPAND) = 1;
- /* Set edit mode to emacs by default, may be overridden
+ /*
+ * Set edit mode to emacs by default, may be overridden
* by the environment or the user. Also, we want tab completion
- * on in vi by default. */
+ * on in vi by default.
+ */
change_flag(FEMACS, OF_SPECIAL, 1);
#if !MKSH_S_NOVI
Flag(FVITABCOMPLETE) = 1;
#endif
-#ifdef MKSH_BINSHREDUCED
- /* set FSH if we're called as -sh or /bin/sh or so */
- {
- const char *cc;
-
- cc = kshname;
- i = 0; argi = 0;
- while (cc[i] != '\0')
- /* the following line matches '-' and '/' ;-) */
- if ((cc[i++] | 2) == '/')
- argi = i;
- if (((cc[argi] | 0x20) == 's') && ((cc[argi + 1] | 0x20) == 'h'))
- change_flag(FSH, OF_FIRSTTIME, 1);
- }
-#endif
-
/* import environment */
if (environ != NULL)
for (wp = (const char **)environ; *wp != NULL; wp++)
typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
- typeset(initifs, 0, 0, 0, 0); /* for security */
+ /* for security */
+ typeset(initifs, 0, 0, 0, 0);
/* assign default shell variable values */
substitute(initsubs, 0);
/* Figure out the current working directory and set $PWD */
- {
- struct stat s_pwd, s_dot;
- struct tbl *pwd_v = global("PWD");
- char *pwd = str_val(pwd_v);
- char *pwdx = pwd;
-
- /* Try to use existing $PWD if it is valid */
- if (pwd[0] != '/' ||
- stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0 ||
- s_pwd.st_dev != s_dot.st_dev ||
- s_pwd.st_ino != s_dot.st_ino)
- pwdx = NULL;
- set_current_wd(pwdx);
- if (current_wd[0])
- simplify_path(current_wd);
- /* Only set pwd if we know where we are or if it had a
- * bogus value
- */
- if (current_wd[0] || pwd != null)
- /* setstr can't fail here */
- setstr(pwd_v, current_wd, KSH_RETURN_ERROR);
- }
+ vp = global("PWD");
+ cp = str_val(vp);
+ /* Try to use existing $PWD if it is valid */
+ set_current_wd((cp[0] == '/' && test_eval(NULL, TO_FILEQ, cp, ".",
+ true)) ? cp : NULL);
+ if (current_wd[0])
+ simplify_path(current_wd);
+ /* Only set pwd if we know where we are or if it had a bogus value */
+ if (current_wd[0] || *cp)
+ /* setstr can't fail here */
+ setstr(vp, current_wd, KSH_RETURN_ERROR);
for (wp = initcoms; *wp != NULL; wp++) {
shcomexec(wp);
while (*wp != NULL)
wp++;
}
- setint(global("COLUMNS"), 0);
- setint(global("LINES"), 0);
- setint(global("OPTIND"), 1);
+ setint_n(global("COLUMNS"), 0);
+ setint_n(global("LINES"), 0);
+ setint_n(global("OPTIND"), 1);
+
+ kshuid = getuid();
+ kshgid = getgid();
+ kshegid = getegid();
safe_prompt = ksheuid ? "$ " : "# ";
vp = global("PS1");
(!ksheuid && !strchr(str_val(vp), '#')))
/* setstr can't fail here */
setstr(vp, safe_prompt, KSH_RETURN_ERROR);
- setint((vp = global("PGRP")), (mksh_uari_t)kshpgrp);
+ setint_n((vp = global("PGRP")), (mksh_uari_t)kshpgrp);
vp->flag |= INT_U;
- setint((vp = global("PPID")), (mksh_uari_t)kshppid);
+ setint_n((vp = global("PPID")), (mksh_uari_t)kshppid);
vp->flag |= INT_U;
- setint((vp = global("RANDOM")), (mksh_uari_t)evilhash(kshname));
+ setint_n((vp = global("USER_ID")), (mksh_uari_t)ksheuid);
vp->flag |= INT_U;
- setint((vp = global("USER_ID")), (mksh_uari_t)ksheuid);
+ setint_n((vp = global("KSHUID")), (mksh_uari_t)kshuid);
vp->flag |= INT_U;
+ setint_n((vp = global("KSHEGID")), (mksh_uari_t)kshegid);
+ vp->flag |= INT_U;
+ setint_n((vp = global("KSHGID")), (mksh_uari_t)kshgid);
+ vp->flag |= INT_U;
+ setint_n((vp = global("RANDOM")), rndsetup());
+ vp->flag |= INT_U;
+ setint_n((vp_pipest = global("PIPESTATUS")), 0);
/* Set this before parsing arguments */
-#if HAVE_SETRESUGID
- Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid();
-#else
- Flag(FPRIVILEGED) = (kshuid = getuid()) != ksheuid ||
- (kshgid = getgid()) != (kshegid = getegid());
-#endif
+ Flag(FPRIVILEGED) = kshuid != ksheuid || kshgid != kshegid;
/* this to note if monitor is set on command line (see below) */
#ifndef MKSH_UNEMPLOYED
/* this to note if utf-8 mode is set on command line (see below) */
UTFMODE = 2;
- argi = parse_args(argv, OF_CMDLINE, NULL);
- if (argi < 0)
- return (NULL);
+ if (!Flag(FAS_BUILTIN)) {
+ argi = parse_args(argv, OF_CMDLINE, NULL);
+ if (argi < 0)
+ return (1);
+ }
+
+#ifdef DEBUG
+ /* test wraparound of arithmetic types */
+ {
+ volatile long xl;
+ volatile unsigned long xul;
+ volatile int xi;
+ volatile unsigned int xui;
+ volatile mksh_ari_t xa;
+ volatile mksh_uari_t xua, xua2;
+ volatile uint8_t xc;
+
+ xa = 2147483647;
+ xua = 2147483647;
+ ++xa;
+ ++xua;
+ xua2 = xa;
+ xl = xa;
+ xul = xua;
+ xa = 0;
+ xua = 0;
+ --xa;
+ --xua;
+ xi = xa;
+ xui = xua;
+ xa = -1;
+ xua = xa;
+ ++xa;
+ ++xua;
+ xc = 0;
+ --xc;
+ if ((xua2 != 2147483648UL) ||
+ (xl != -2147483648L) || (xul != 2147483648UL) ||
+ (xi != -1) || (xui != 4294967295U) ||
+ (xa != 0) || (xua != 0) || (xc != 255))
+ errorf("integer wraparound test failed");
+ }
+#endif
/* process this later only, default to off (hysterical raisins) */
utf_flag = UTFMODE;
UTFMODE = 0;
- if (Flag(FCOMMAND)) {
+ if (Flag(FAS_BUILTIN)) {
+ /* auto-detect from environment variables, always */
+ utf_flag = 3;
+ } else if (Flag(FCOMMAND)) {
s = pushs(SSTRING, ATEMP);
if (!(s->start = s->str = argv[argi++]))
- errorf("-c requires an argument");
+ errorf("%s %s", "-c", "requires an argument");
#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
/* compatibility to MidnightBSD 0.1 /bin/sh (kludge) */
if (Flag(FSH) && argv[argi] && !strcmp(argv[argi], "--"))
s->u.shf = shf_open(s->file, O_RDONLY, 0,
SHF_MAPHI | SHF_CLEXEC);
if (s->u.shf == NULL) {
- shl_stdout_ok = 0;
+ shl_stdout_ok = false;
warningf(true, "%s: %s", s->file, strerror(errno));
/* mandated by SUSv4 */
exstat = 127;
/* initialise job control */
j_init();
- /* set: 0/1; unset: 2->0 */
- UTFMODE = utf_flag & 1;
/* Do this after j_init(), as tty_fd is not initialised until then */
if (Flag(FTALKING)) {
if (utf_flag == 2) {
#ifndef MKSH_ASSUME_UTF8
-#define isuc(x) (((x) != NULL) && \
- (stristr((x), "UTF-8") || stristr((x), "utf8")))
- /* Check if we're in a UTF-8 locale */
- const char *ccp;
-
-#if HAVE_SETLOCALE_CTYPE
- ccp = setlocale(LC_CTYPE, "");
-#if HAVE_LANGINFO_CODESET
- if (!isuc(ccp))
- ccp = nl_langinfo(CODESET);
-#endif
-#else
- /* these were imported from environ earlier */
- ccp = str_val(global("LC_ALL"));
- if (ccp == null)
- ccp = str_val(global("LC_CTYPE"));
- if (ccp == null)
- ccp = str_val(global("LANG"));
-#endif
- UTFMODE = isuc(ccp);
-#undef isuc
+ /* auto-detect from locale or environment */
+ utf_flag = 4;
#elif MKSH_ASSUME_UTF8
- UTFMODE = 1;
+ utf_flag = 1;
#else
- UTFMODE = 0;
+ /* always disable UTF-8 (for interactive) */
+ utf_flag = 0;
#endif
}
x_init();
#endif
l = e->loc;
- l->argv = &argv[argi - 1];
- l->argc = argc - argi;
- l->argv[0] = kshname;
- getopts_reset(1);
+ if (Flag(FAS_BUILTIN)) {
+ l->argc = argc;
+ l->argv = argv;
+ l->argv[0] = ccp;
+ } else {
+ l->argc = argc - argi;
+ l->argv = &argv[argi - 1];
+ l->argv[0] = kshname;
+ getopts_reset(1);
+ }
+
+ /* divine the initial state of the utf8-mode Flag */
+#define isuc(x) (((x) != NULL) && \
+ (stristr((x), "UTF-8") || stristr((x), "utf8")))
+ ccp = null;
+ switch (utf_flag) {
+
+ /* auto-detect from locale or environment */
+ case 4:
+#if HAVE_SETLOCALE_CTYPE
+ ccp = setlocale(LC_CTYPE, "");
+#if HAVE_LANGINFO_CODESET
+ if (!isuc(ccp))
+ ccp = nl_langinfo(CODESET);
+#endif
+ if (!isuc(ccp))
+ ccp = null;
+ /* FALLTHROUGH */
+#endif
+
+ /* auto-detect from environment */
+ case 3:
+ /* these were imported from environ earlier */
+ if (ccp == null)
+ ccp = str_val(global("LC_ALL"));
+ if (ccp == null)
+ ccp = str_val(global("LC_CTYPE"));
+ if (ccp == null)
+ ccp = str_val(global("LANG"));
+ UTFMODE = isuc(ccp);
+ break;
+
+ /* not set on command line, not FTALKING */
+ case 2:
+ /* unknown values */
+ default:
+ utf_flag = 0;
+ /* FALLTHROUGH */
+
+ /* known values */
+ case 1:
+ case 0:
+ UTFMODE = utf_flag;
+ break;
+ }
+#undef isuc
/* Disable during .profile/ENV reading */
restricted = Flag(FRESTRICTED);
errexit = Flag(FERREXIT);
Flag(FERREXIT) = 0;
- /* Do this before profile/$ENV so that if it causes problems in them,
+ /*
+ * Do this before profile/$ENV so that if it causes problems in them,
* user will know why things broke.
*/
if (!current_wd[0] && Flag(FTALKING))
- warningf(false, "Cannot determine current working directory");
+ warningf(false, "can't determine current directory");
if (Flag(FLOGIN)) {
- include(KSH_SYSTEM_PROFILE, 0, NULL, 1);
+ include(MKSH_SYSTEM_PROFILE, 0, NULL, 1);
if (!Flag(FPRIVILEGED))
include(substitute("$HOME/.profile", 0), 0,
NULL, 1);
}
if (Flag(FPRIVILEGED))
- include("/etc/suid_profile", 0, NULL, 1);
+ include(MKSH_SUID_PROFILE, 0, NULL, 1);
else if (Flag(FTALKING)) {
char *env_file;
}
if (restricted) {
- static const char *restr_com[] = {
- T_typeset, "-r", "PATH",
- "ENV", "SHELL",
- NULL
- };
shcomexec(restr_com);
/* After typeset command... */
Flag(FRESTRICTED) = 1;
}
Flag(FERREXIT) = errexit;
- if (Flag(FTALKING)) {
+ if (Flag(FTALKING))
hist_init(s);
- alarm_init();
- } else
- Flag(FTRACKALL) = 1; /* set after ENV */
-
- return (s);
-}
+ else
+ /* set after ENV */
+ Flag(FTRACKALL) = 1;
-int
-main(int argc, const char *argv[])
-{
- Source *s;
+ alarm_init();
- kshstate_v.lcg_state_ = 5381;
+ if (Flag(FAS_BUILTIN))
+ return (shcomexec(l->argv));
- if ((s = mksh_init(argc, argv))) {
- /* put more entropy into the LCG */
- change_random(s, sizeof(*s));
- /* doesn’t return */
- shell(s, true);
- }
- return (1);
+ /* doesn't return */
+ shell(s, true);
+ /* NOTREACHED */
+ return (0);
}
int
switch (i) {
case LRETURN:
case LERROR:
- return (exstat & 0xff); /* see below */
+ /* see below */
+ return (exstat & 0xFF);
case LINTR:
- /* intr_ok is set if we are including .profile or $ENV.
+ /*
+ * intr_ok is set if we are including .profile or $ENV.
* If user ^Cs out, we don't want to kill the shell...
*/
if (intr_ok && (exstat - 128) != SIGTERM)
unwind(i);
/* NOTREACHED */
default:
- internal_errorf("include: %d", i);
+ internal_errorf("%s %d", "include", i);
/* NOTREACHED */
}
}
e->loc->argv = old_argv;
e->loc->argc = old_argc;
}
- return (i & 0xff); /* & 0xff to ensure value not -1 */
+ /* & 0xff to ensure value not -1 */
+ return (i & 0xFF);
}
/* spawn a command into a shell optionally keeping track of the line number */
volatile int wastty = s->flags & SF_TTY;
volatile int attempts = 13;
volatile int interactive = Flag(FTALKING) && toplevel;
+ volatile bool sfirst = true;
Source *volatile old_source = source;
int i;
- s->flags |= SF_FIRST; /* enable UTF-8 BOM check */
-
newenv(E_PARSE);
if (interactive)
really_exit = 0;
i = sigsetjmp(e->jbuf, 0);
if (i) {
switch (i) {
- case LINTR: /* we get here if SIGINT not caught or ignored */
+ case LINTR:
+ /* we get here if SIGINT not caught or ignored */
case LERROR:
case LSHELL:
if (interactive) {
if (i == LINTR)
shellf("\n");
- /* Reset any eof that was read as part of a
+ /*
+ * Reset any eof that was read as part of a
* multiline command.
*/
if (Flag(FIGNOREEOF) && s->type == SEOF &&
wastty)
s->type = SSTDIN;
- /* Used by exit command to get back to
+ /*
+ * Used by exit command to get back to
* top level shell. Kind of strange since
* interactive is set if we are reading from
* a tty, but to have stopped jobs, one only
case LRETURN:
source = old_source;
quitenv(NULL);
- unwind(i); /* keep on going */
+ /* keep on going */
+ unwind(i);
/* NOTREACHED */
default:
source = old_source;
quitenv(NULL);
- internal_errorf("shell: %d", i);
+ internal_errorf("%s %d", "shell", i);
/* NOTREACHED */
}
}
- while (1) {
+ while (/* CONSTCOND */ 1) {
if (trap)
runtraps(0);
j_notify();
set_prompt(PS1, s);
}
- t = compile(s);
+ t = compile(s, sfirst);
+ sfirst = false;
if (t != NULL && t->type == TEOF) {
if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
- shellf("Use 'exit' to leave ksh\n");
+ shellf("Use 'exit' to leave mksh\n");
s->type = SSTDIN;
} else if (wastty && !really_exit &&
j_stopped_running()) {
really_exit = 1;
s->type = SSTDIN;
} else {
- /* this for POSIX which says EXIT traps
+ /*
+ * this for POSIX which says EXIT traps
* shall be taken in the environment
* immediately after the last command
* executed.
{
/* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) &&
- sigtraps[SIGEXIT_].trap)) {
- runtrap(&sigtraps[SIGEXIT_]);
+ sigtraps[ksh_SIGEXIT].trap)) {
+ ++trap_nested;
+ runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1);
+ --trap_nested;
i = LLEAVE;
} else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
- runtrap(&sigtraps[SIGERR_]);
+ ++trap_nested;
+ runtrap(&sigtraps[ksh_SIGERR], trap_nested == 1);
+ --trap_nested;
i = LLEAVE;
}
- while (1) {
+ while (/* CONSTCOND */ 1) {
switch (e->type) {
case E_PARSE:
case E_FUNC:
* so first get the actually used memory, then assign it
*/
cp = alloc(sizeof(struct env) - ALLOC_SIZE, ATEMP);
- ep = (void *)(cp - ALLOC_SIZE); /* undo what alloc() did */
+ /* undo what alloc() did to the malloc result address */
+ ep = (void *)(cp - ALLOC_SIZE);
/* initialise public members of struct env (not the ALLOC_ITEM) */
ainit(&ep->area);
ep->oenv = e;
/* if ep->savefd[fd] < 0, means fd was closed */
if (ep->savefd[fd])
restfd(fd, ep->savefd[fd]);
- if (ep->savefd[2]) /* Clear any write errors */
+ if (ep->savefd[2])
+ /* Clear any write errors */
shf_reopen(2, SHF_WR, shl_out);
}
- /* Bottom of the stack.
+ /*
+ * Bottom of the stack.
* Either main shell is exiting or cleanup_parents_env() was called.
*/
if (ep->oenv == NULL) {
- if (ep->type == E_NONE) { /* Main shell exiting? */
+ if (ep->type == E_NONE) {
+ /* Main shell exiting? */
#if HAVE_PERSISTENT_HISTORY
if (Flag(FTALKING))
hist_finish();
if (ep->flags & EF_FAKE_SIGDIE) {
int sig = exstat - 128;
- /* ham up our death a bit (AT&T ksh
+ /*
+ * ham up our death a bit (AT&T ksh
* only seems to do this for SIGTERM)
* Don't do it for SIGQUIT, since we'd
* dump a core..
unlink(tp->name);
}
-/* Initialise tty_fd. Used for saving/reseting tty modes upon
+/*
+ * Initialise tty_fd. Used for saving/reseting tty modes upon
* foreground job completion and for setting up tty process group.
*/
void
close(tty_fd);
tty_fd = -1;
}
- tty_devtty = 1;
+ tty_devtty = true;
#ifdef _UWIN
- /* XXX imake style */
- if (isatty(3))
+ /*XXX imake style */
+ if (isatty(3)) {
+ /* fd 3 on UWIN _is_ /dev/tty (or our controlling tty) */
tfd = 3;
- else
+ do_close = false;
+ } else
#endif
- if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
- tty_devtty = 0;
+ if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
+ tty_devtty = false;
if (need_tty)
- warningf(false,
- "No controlling tty (open /dev/tty: %s)",
+ warningf(false, "%s: %s %s: %s",
+ "No controlling tty", "open", "/dev/tty",
strerror(errno));
}
if (tfd < 0) {
tfd = 2;
else {
if (need_tty)
- warningf(false,
- "Can't find tty file descriptor");
+ warningf(false, "can't find tty fd");
return;
}
}
if ((tty_fd = fcntl(tfd, F_DUPFD, FDBASE)) < 0) {
if (need_tty)
- warningf(false, "j_ttyinit: dup of tty fd failed: %s",
- strerror(errno));
+ warningf(false, "%s: %s %s: %s", "j_ttyinit",
+ "dup of tty fd", "failed", strerror(errno));
} else if (fcntl(tty_fd, F_SETFD, FD_CLOEXEC) < 0) {
if (need_tty)
- warningf(false,
- "j_ttyinit: can't set close-on-exec flag: %s",
- strerror(errno));
+ warningf(false, "%s: %s: %s", "j_ttyinit",
+ "can't set close-on-exec flag", strerror(errno));
close(tty_fd);
tty_fd = -1;
} else if (init_ttystate)
}
/* A shell error occurred (eg, syntax error, etc.) */
+
+#define VWARNINGF_ERRORPREFIX 1
+#define VWARNINGF_FILELINE 2
+#define VWARNINGF_BUILTIN 4
+#define VWARNINGF_INTERNAL 8
+
+static void vwarningf(unsigned int, const char *, va_list)
+ MKSH_A_FORMAT(__printf__, 2, 0);
+
+static void
+vwarningf(unsigned int flags, const char *fmt, va_list ap)
+{
+ if (*fmt != 1) {
+ if (flags & VWARNINGF_INTERNAL)
+ shf_fprintf(shl_out, "internal error: ");
+ if (flags & VWARNINGF_ERRORPREFIX)
+ error_prefix(tobool(flags & VWARNINGF_FILELINE));
+ if ((flags & VWARNINGF_BUILTIN) &&
+ /* not set when main() calls parse_args() */
+ builtin_argv0 && builtin_argv0 != kshname)
+ shf_fprintf(shl_out, "%s: ", builtin_argv0);
+ shf_vfprintf(shl_out, fmt, ap);
+ shf_putchar('\n', shl_out);
+ }
+ shf_flush(shl_out);
+}
+
+void
+errorfx(int rc, const char *fmt, ...)
+{
+ va_list va;
+
+ exstat = rc;
+
+ /* debugging: note that stdout not valid */
+ shl_stdout_ok = false;
+
+ va_start(va, fmt);
+ vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE, fmt, va);
+ va_end(va);
+ unwind(LERROR);
+}
+
void
errorf(const char *fmt, ...)
{
va_list va;
- shl_stdout_ok = 0; /* debugging: note that stdout not valid */
exstat = 1;
- if (*fmt != 1) {
- error_prefix(true);
- va_start(va, fmt);
- shf_vfprintf(shl_out, fmt, va);
- va_end(va);
- shf_putchar('\n', shl_out);
- }
- shf_flush(shl_out);
+
+ /* debugging: note that stdout not valid */
+ shl_stdout_ok = false;
+
+ va_start(va, fmt);
+ vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE, fmt, va);
+ va_end(va);
unwind(LERROR);
}
{
va_list va;
- error_prefix(fileline);
va_start(va, fmt);
- shf_vfprintf(shl_out, fmt, va);
+ vwarningf(VWARNINGF_ERRORPREFIX | (fileline ? VWARNINGF_FILELINE : 0),
+ fmt, va);
va_end(va);
- shf_putchar('\n', shl_out);
- shf_flush(shl_out);
}
-/* Used by built-in utilities to prefix shell and utility name to message
+/*
+ * Used by built-in utilities to prefix shell and utility name to message
* (also unwinds environments for special builtins).
*/
void
{
va_list va;
- shl_stdout_ok = 0; /* debugging: note that stdout not valid */
+ /* debugging: note that stdout not valid */
+ shl_stdout_ok = false;
+
exstat = 1;
- if (*fmt != 1) {
- error_prefix(true);
- /* not set when main() calls parse_args() */
- if (builtin_argv0)
- shf_fprintf(shl_out, "%s: ", builtin_argv0);
- va_start(va, fmt);
- shf_vfprintf(shl_out, fmt, va);
- va_end(va);
- shf_putchar('\n', shl_out);
- }
- shf_flush(shl_out);
- /* POSIX special builtins and ksh special builtins cause
+
+ va_start(va, fmt);
+ vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE |
+ VWARNINGF_BUILTIN, fmt, va);
+ va_end(va);
+
+ /*
+ * POSIX special builtins and ksh special builtins cause
* non-interactive shells to exit.
* XXX odd use of KEEPASN; also may not want LERROR here
*/
/* Called when something that shouldn't happen does */
void
-internal_verrorf(const char *fmt, va_list ap)
-{
- shf_fprintf(shl_out, "internal error: ");
- shf_vfprintf(shl_out, fmt, ap);
- shf_putchar('\n', shl_out);
- shf_flush(shl_out);
-}
-
-void
internal_errorf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
- internal_verrorf(fmt, va);
+ vwarningf(VWARNINGF_INTERNAL, fmt, va);
va_end(va);
unwind(LERROR);
}
va_list va;
va_start(va, fmt);
- internal_verrorf(fmt, va);
+ vwarningf(VWARNINGF_INTERNAL, fmt, va);
va_end(va);
}
{
va_list va;
- if (!initio_done) /* shl_out may not be set up yet... */
+ if (!initio_done)
+ /* shl_out may not be set up yet... */
return;
va_start(va, fmt);
shf_vfprintf(shl_out, fmt, va);
void
initio(void)
{
- shf_fdopen(1, SHF_WR, shl_stdout); /* force buffer allocation */
+ /* force buffer allocation */
+ shf_fdopen(1, SHF_WR, shl_stdout);
shf_fdopen(2, SHF_WR, shl_out);
- shf_fdopen(2, SHF_WR, shl_spare); /* force buffer allocation */
+ /* force buffer allocation */
+ shf_fdopen(2, SHF_WR, shl_spare);
initio_done = 1;
}
errorf("too many files open in shell");
#ifdef __ultrix
- /* XXX imake style */
+ /*XXX imake style */
if (rv >= 0)
fcntl(nfd, F_SETFD, 0);
#endif
}
/*
- * move fd from user space (0<=fd<10) to shell space (fd>=10),
- * set close-on-exec flag.
+ * Move fd from user space (0 <= fd < 10) to shell space (fd >= 10),
+ * set close-on-exec flag. See FDBASE in sh.h, maybe 24 not 10 here.
*/
short
savefd(int fd)
{
if (fd == 2)
shf_flush(&shf_iob[fd]);
- if (ofd < 0) /* original fd closed */
+ if (ofd < 0)
+ /* original fd closed */
close(fd);
else if (fd != ofd) {
- ksh_dup2(ofd, fd, true); /* XXX: what to do if this fails? */
+ /*XXX: what to do if this dup fails? */
+ ksh_dup2(ofd, fd, true);
close(ofd);
}
}
close(pv[1]);
}
-/* Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
+/*
+ * Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
* a string (the X in 2>&X, read -uX, print -uX) into a file descriptor.
*/
int
return (-1);
}
fl &= O_ACCMODE;
- /* X_OK is a kludge to disable this check for dups (x<&1):
+ /*
+ * X_OK is a kludge to disable this check for dups (x<&1):
* historical shells never did this check (XXX don't know what
* POSIX has to say).
*/
}
}
-/* Called by c_read() and by iosetup() to close the other side of the
+/*
+ * Called by c_read() and by iosetup() to close the other side of the
* read pipe, so reads will actually terminate.
*/
void
}
}
-/* Called by c_print when a write to a fd fails with EPIPE and by iosetup
+/*
+ * Called by c_print when a write to a fd fails with EPIPE and by iosetup
* when co-process input is dup'd
*/
void
}
}
-/* Called to check for existence of/value of the co-process file descriptor.
+/*
+ * Called to check for existence of/value of the co-process file descriptor.
* (Used by check_fd() and by c_read/c_print to deal with -p option).
*/
int
return (-1);
}
-/* called to close file descriptors related to the coprocess (if any)
+/*
+ * called to close file descriptors related to the coprocess (if any)
* Should be called with SIGCHLD blocked.
*/
void
maketemp(Area *ap, Temp_type type, struct temp **tlist)
{
struct temp *tp;
- int len;
+ size_t len;
int fd;
char *pathname;
const char *dir;
pathname = tempnam(dir, "mksh.");
len = ((pathname == NULL) ? 0 : strlen(pathname)) + 1;
#endif
+ /* reasonably sure that this will not overflow */
tp = alloc(sizeof(struct temp) + len, ap);
tp->name = (char *)&tp[1];
#if !HAVE_MKSTEMP
tp->name[0] = '\0';
else {
memcpy(tp->name, pathname, len);
- free(pathname);
+ free_ostempnam(pathname);
}
#endif
pathname = tp->name;
tp->shf = NULL;
tp->type = type;
#if HAVE_MKSTEMP
- shf_snprintf(pathname, len, "%s/mksh.XXXXXXXXXX", dir);
+ shf_snprintf(pathname, len, "%s%s", dir, "/mksh.XXXXXXXXXX");
if ((fd = mkstemp(pathname)) >= 0)
#else
if (tp->name[0] && (fd = open(tp->name, O_CREAT | O_RDWR, 0600)) >= 0)
* but with a slightly tweaked implementation written from scratch.
*/
-#define INIT_TBLS 8 /* initial table size (power of 2) */
+#define INIT_TBLSHIFT 3 /* initial table shift (2^3 = 8) */
#define PERTURB_SHIFT 5 /* see Python 2.5.4 Objects/dictobject.c */
-static void texpand(struct table *, size_t);
+static void tgrow(struct table *);
static int tnamecmp(const void *, const void *);
-static struct tbl *ktscan(struct table *, const char *, uint32_t,
- struct tbl ***);
static void
-texpand(struct table *tp, size_t nsize)
+tgrow(struct table *tp)
{
- size_t i, j, osize = tp->size, perturb;
+ size_t i, j, osize, mask, perturb;
struct tbl *tblp, **pp;
struct tbl **ntblp, **otblp = tp->tbls;
- ntblp = alloc(nsize * sizeof(struct tbl *), tp->areap);
- for (i = 0; i < nsize; i++)
- ntblp[i] = NULL;
- tp->size = nsize;
- tp->nfree = (nsize * 4) / 5; /* table can get 80% full */
+ if (tp->tshift > 29)
+ internal_errorf("hash table size limit reached");
+
+ /* calculate old size, new shift and new size */
+ osize = (size_t)1 << (tp->tshift++);
+ i = osize << 1;
+
+ ntblp = alloc2(i, sizeof(struct tbl *), tp->areap);
+ /* multiplication cannot overflow: alloc2 checked that */
+ memset(ntblp, 0, i * sizeof(struct tbl *));
+
+ /* table can get 80% full except when reaching its limit */
+ tp->nfree = (tp->tshift == 30) ? 0x3FFF0000UL : ((i * 4) / 5);
tp->tbls = ntblp;
if (otblp == NULL)
return;
- nsize--; /* from here on nsize := mask */
+
+ mask = i - 1;
for (i = 0; i < osize; i++)
if ((tblp = otblp[i]) != NULL) {
if ((tblp->flag & DEFINED)) {
/* search for free hash table slot */
- j = (perturb = tblp->ua.hval) & nsize;
+ j = (perturb = tblp->ua.hval) & mask;
goto find_first_empty_slot;
find_next_empty_slot:
j = (j << 2) + j + perturb + 1;
perturb >>= PERTURB_SHIFT;
find_first_empty_slot:
- pp = &ntblp[j & nsize];
+ pp = &ntblp[j & mask];
if (*pp != NULL)
goto find_next_empty_slot;
/* found an empty hash table slot */
}
void
-ktinit(struct table *tp, Area *ap, size_t tsize)
+ktinit(Area *ap, struct table *tp, uint8_t initshift)
{
tp->areap = ap;
tp->tbls = NULL;
- tp->size = tp->nfree = 0;
- if (tsize)
- texpand(tp, tsize);
+ tp->tshift = ((initshift > INIT_TBLSHIFT) ?
+ initshift : INIT_TBLSHIFT) - 1;
+ tgrow(tp);
}
/* table, name (key) to search for, hash(name), rv pointer to tbl ptr */
-static struct tbl *
+struct tbl *
ktscan(struct table *tp, const char *name, uint32_t h, struct tbl ***ppp)
{
size_t j, perturb, mask;
struct tbl **pp, *p;
- mask = tp->size - 1;
+ mask = ((size_t)1 << (tp->tshift)) - 1;
/* search for hash table slot matching name */
j = (perturb = h) & mask;
goto find_first_slot;
return (p);
}
-/* table, name (key) to search for, hash(n) */
-struct tbl *
-ktsearch(struct table *tp, const char *n, uint32_t h)
-{
- return (tp->size ? ktscan(tp, n, h, NULL) : NULL);
-}
-
/* table, name (key) to enter, hash(n) */
struct tbl *
ktenter(struct table *tp, const char *n, uint32_t h)
{
struct tbl **pp, *p;
- int len;
+ size_t len;
- if (tp->size == 0)
- texpand(tp, INIT_TBLS);
Search:
if ((p = ktscan(tp, n, h, &pp)))
return (p);
- if (tp->nfree <= 0) {
+ if (tp->nfree == 0) {
/* too full */
- texpand(tp, 2 * tp->size);
+ tgrow(tp);
goto Search;
}
/* create new tbl entry */
- len = strlen(n) + 1;
- p = alloc(offsetof(struct tbl, name[0]) + len, tp->areap);
+ len = strlen(n);
+ checkoktoadd(len, offsetof(struct tbl, name[0]) + 1);
+ p = alloc(offsetof(struct tbl, name[0]) + ++len, tp->areap);
p->flag = 0;
p->type = 0;
p->areap = tp->areap;
void
ktwalk(struct tstate *ts, struct table *tp)
{
- ts->left = tp->size;
+ ts->left = (size_t)1 << (tp->tshift);
ts->next = tp->tbls;
}
size_t i;
struct tbl **p, **sp, **dp;
- p = alloc((tp->size + 1) * sizeof(struct tbl *), ATEMP);
+ /*
+ * since the table is never entirely full, no need to reserve
+ * additional space for the trailing NULL appended below
+ */
+ i = (size_t)1 << (tp->tshift);
+ p = alloc2(i, sizeof(struct tbl *), ATEMP);
sp = tp->tbls; /* source */
dp = p; /* dest */
- i = (size_t)tp->size;
while (i--)
if ((*dp = *sp++) != NULL && (((*dp)->flag & DEFINED) ||
((*dp)->flag & ARRAY)))
dp++;
- qsort(p, (i = dp - p), sizeof(void *), tnamecmp);
+ qsort(p, (i = dp - p), sizeof(struct tbl *), tnamecmp);
p[i] = NULL;
return (p);
}