OSDN Git Service

Update mksh to R43 (formal release, from tarball)
[android-x86/external-mksh.git] / src / histrap.c
index 4a4a275..3b28f23 100644 (file)
@@ -2,7 +2,8 @@
 /*     $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $        */
 
 /*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *              2011, 2012
  *     Thorsten Glaser <tg@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
 #include <sys/file.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.111 2011/09/07 15:24:16 tg Exp $");
-
-/*-
- * MirOS: This is the default mapping type, and need not be specified.
- * IRIX doesn't have this constant.
- */
-#ifndef MAP_FILE
-#define MAP_FILE       0
-#endif
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.131 2012/12/28 02:28:35 tg Exp $");
 
 Trap sigtraps[NSIG + 1];
 static struct sigaction Sigact_ign;
 
 #if HAVE_PERSISTENT_HISTORY
-static int hist_count_lines(unsigned char *, int);
-static int hist_shrink(unsigned char *, int);
-static unsigned char *hist_skip_back(unsigned char *,int *,int);
-static void histload(Source *, unsigned char *, int);
-static void histinsert(Source *, int, const char *);
-static void writehistfile(int, char *);
-static int sprinkle(int);
+static int histload(Source *, unsigned char *, size_t);
+static int writehistline(int, int, const char *);
+static void writehistfile(int, const char *);
 #endif
 
 static int hist_execute(char *);
-static int hist_replace(char **, const char *, const char *, bool);
 static char **hist_get(const char *, bool, bool);
 static char **hist_get_oldest(void);
-static void histbackup(void);
 
-static char **current;         /* current position in history[] */
-static int hstarted;           /* set after hist_init() called */
+static bool hstarted;          /* set after hist_init() called */
 static Source *hist_source;
 
 #if HAVE_PERSISTENT_HISTORY
+/*XXX imake style */
+#if defined(__linux)
+#define caddr_cast(x)  ((void *)(x))
+#else
+#define caddr_cast(x)  ((caddr_t)(x))
+#endif
+
+/* several OEs do not have these constants */
+#ifndef MAP_FAILED
+#define MAP_FAILED     caddr_cast(-1)
+#endif
+
+/* some OEs need the default mapping type specified */
+#ifndef MAP_FILE
+#define MAP_FILE       0
+#endif
+
 /* current history file: name, fd, size */
 static char *hname;
-static int histfd;
-static size_t hsize;
+static int histfd = -1;
+static off_t histfsize;
 #endif
 
 static const char Tnot_in_history[] = "not in history";
 #define Thistory (Tnot_in_history + 7)
 
+static const char TFCEDIT_dollaru[] = "${FCEDIT:-/bin/ed} $_";
+#define Tspdollaru (TFCEDIT_dollaru + 18)
+
+/* HISTSIZE default: size of saved history, persistent or standard */
+#ifdef MKSH_SMALL
+#define MKSH_DEFHISTSIZE       255
+#else
+#define MKSH_DEFHISTSIZE       2047
+#endif
+/* maximum considered size of persistent history file */
+#define MKSH_MAXHISTFSIZE      ((off_t)1048576 * 96)
+
 int
 c_fc(const char **wp)
 {
        struct shf *shf;
        struct temp *tf;
-       const char *p;
-       char *editor = NULL;
        bool gflag = false, lflag = false, nflag = false, rflag = false,
            sflag = false;
        int optc;
-       const char *first = NULL, *last = NULL;
-       char **hfirst, **hlast, **hp;
+       const char *p, *first = NULL, *last = NULL;
+       char **hfirst, **hlast, **hp, *editor = NULL;
 
        if (!Flag(FTALKING_I)) {
                bi_errorf("history %ss not available", Tfunction);
@@ -101,7 +114,7 @@ c_fc(const char **wp)
                                /* almost certainly not overflowing */
                                editor = alloc(len + 4, ATEMP);
                                memcpy(editor, p, len);
-                               memcpy(editor + len, " $_", 4);
+                               memcpy(editor + len, Tspdollaru, 4);
                        }
                        break;
 
@@ -149,7 +162,7 @@ c_fc(const char **wp)
 
        /* Substitute and execute command */
        if (sflag) {
-               char *pat = NULL, *rep = NULL;
+               char *pat = NULL, *rep = NULL, *line;
 
                if (editor || lflag || nflag || rflag) {
                        bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
@@ -175,7 +188,42 @@ c_fc(const char **wp)
                    hist_get_newest(false);
                if (!hp)
                        return (1);
-               return (hist_replace(hp, pat, rep, gflag));
+               /* hist_replace */
+               if (!pat)
+                       strdupx(line, *hp, ATEMP);
+               else {
+                       char *s, *s1;
+                       size_t len, pat_len, rep_len;
+                       XString xs;
+                       char *xp;
+                       bool any_subst = false;
+
+                       pat_len = strlen(pat);
+                       rep_len = strlen(rep);
+                       Xinit(xs, xp, 128, ATEMP);
+                       for (s = *hp; (s1 = strstr(s, pat)) &&
+                           (!any_subst || gflag); s = s1 + pat_len) {
+                               any_subst = true;
+                               len = s1 - s;
+                               XcheckN(xs, xp, len + rep_len);
+                               /*; first part */
+                               memcpy(xp, s, len);
+                               xp += len;
+                               /* replacement */
+                               memcpy(xp, rep, rep_len);
+                               xp += rep_len;
+                       }
+                       if (!any_subst) {
+                               bi_errorf("bad substitution");
+                               return (1);
+                       }
+                       len = strlen(s) + 1;
+                       XcheckN(xs, xp, len);
+                       memcpy(xp, s, len);
+                       xp += len;
+                       line = Xclose(xs, xp);
+               }
+               return (hist_execute(line));
        }
 
        if (editor && (lflag || nflag)) {
@@ -249,7 +297,7 @@ c_fc(const char **wp)
        tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
        if (!(shf = tf->shf)) {
                bi_errorf("can't %s temporary file %s: %s",
-                   "create", tf->name, strerror(errno));
+                   "create", tf->tffn, cstrerror(errno));
                return (1);
        }
        for (hp = rflag ? hlast : hfirst;
@@ -257,19 +305,19 @@ c_fc(const char **wp)
                shf_fprintf(shf, "%s\n", *hp);
        if (shf_close(shf) == EOF) {
                bi_errorf("can't %s temporary file %s: %s",
-                   "write", tf->name, strerror(errno));
+                   "write", tf->tffn, cstrerror(errno));
                return (1);
        }
 
        /* Ignore setstr errors here (arbitrary) */
-       setstr(local("_", false), tf->name, KSH_RETURN_ERROR);
+       setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR);
 
        /* XXX: source should not get trashed by this.. */
        {
                Source *sold = source;
                int ret;
 
-               ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_", 0);
+               ret = command(editor ? editor : TFCEDIT_dollaru, 0);
                source = sold;
                if (ret)
                        return (ret);
@@ -279,22 +327,22 @@ c_fc(const char **wp)
                struct stat statb;
                XString xs;
                char *xp;
-               int n;
+               ssize_t n;
 
-               if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) {
+               if (!(shf = shf_open(tf->tffn, O_RDONLY, 0, 0))) {
                        bi_errorf("can't %s temporary file %s: %s",
-                           "open", tf->name, strerror(errno));
+                           "open", tf->tffn, cstrerror(errno));
                        return (1);
                }
 
-               if (stat(tf->name, &statb) < 0)
+               if (stat(tf->tffn, &statb) < 0)
                        n = 128;
-               else if (statb.st_size > (1024 * 1048576)) {
+               else if ((off_t)statb.st_size > MKSH_MAXHISTFSIZE) {
                        bi_errorf("%s %s too large: %lu", Thistory,
                            "file", (unsigned long)statb.st_size);
                        goto errout;
                } else
-                       n = statb.st_size + 1;
+                       n = (size_t)statb.st_size + 1;
                Xinit(xs, xp, n, hist_source->areap);
                while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
                        xp += n;
@@ -303,7 +351,7 @@ c_fc(const char **wp)
                }
                if (n < 0) {
                        bi_errorf("can't %s temporary file %s: %s",
-                           "read", tf->name, strerror(shf_errno(shf)));
+                           "read", tf->tffn, cstrerror(shf_errno(shf)));
  errout:
                        shf_close(shf);
                        return (1);
@@ -319,11 +367,18 @@ c_fc(const char **wp)
 static int
 hist_execute(char *cmd)
 {
+       static int last_line = -1;
        Source *sold;
        int ret;
        char *p, *q;
 
-       histbackup();
+       /* Back up over last histsave */
+       if (histptr >= history && last_line != hist_source->line) {
+               hist_source->line--;
+               afree(*histptr, APERM);
+               histptr--;
+               last_line = hist_source->line;
+       }
 
        for (p = cmd; p; p = q) {
                if ((q = strchr(p, '\n'))) {
@@ -356,48 +411,6 @@ hist_execute(char *cmd)
        return (ret);
 }
 
-static int
-hist_replace(char **hp, const char *pat, const char *rep, bool globr)
-{
-       char *line;
-
-       if (!pat)
-               strdupx(line, *hp, ATEMP);
-       else {
-               char *s, *s1;
-               size_t pat_len = strlen(pat);
-               size_t rep_len = strlen(rep);
-               size_t len;
-               XString xs;
-               char *xp;
-               bool any_subst = false;
-
-               Xinit(xs, xp, 128, ATEMP);
-               for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || globr);
-                   s = s1 + pat_len) {
-                       any_subst = true;
-                       len = s1 - s;
-                       XcheckN(xs, xp, len + rep_len);
-                       /*; first part */
-                       memcpy(xp, s, len);
-                       xp += len;
-                       /* replacement */
-                       memcpy(xp, rep, rep_len);
-                       xp += rep_len;
-               }
-               if (!any_subst) {
-                       bi_errorf("bad substitution");
-                       return (1);
-               }
-               len = strlen(s) + 1;
-               XcheckN(xs, xp, len);
-               memcpy(xp, s, len);
-               xp += len;
-               line = Xclose(xs, xp);
-       }
-       return (hist_execute(line));
-}
-
 /*
  * get pointer to history given pattern
  * pattern is a number or string
@@ -462,21 +475,9 @@ hist_get_oldest(void)
        return (history);
 }
 
-/*
- * Back up over last histsave
- */
-static void
-histbackup(void)
-{
-       static int last_line = -1;
-
-       if (histptr >= history && last_line != hist_source->line) {
-               hist_source->line--;
-               afree(*histptr, APERM);
-               histptr--;
-               last_line = hist_source->line;
-       }
-}
+#if !defined(MKSH_NO_CMDLINE_EDITING) && !MKSH_S_NOVI
+/* current position in history[] */
+static char **current;
 
 /*
  * Return the current position.
@@ -500,6 +501,7 @@ histnum(int n)
                return (n);
        }
 }
+#endif
 
 /*
  * This will become unnecessary if hist_get is modified to allow
@@ -527,19 +529,18 @@ findhist(int start, int fwd, const char *str, int anchored)
 }
 
 /*
- *     set history
- *     this means reallocating the dataspace
+ * set history; this means reallocating the dataspace
  */
 void
-sethistsize(int n)
+sethistsize(mksh_ari_t n)
 {
        if (n > 0 && n != histsize) {
                int cursize = histptr - history;
 
                /* save most recent history */
                if (n < cursize) {
-                       memmove(history, histptr - n, n * sizeof(char *));
-                       cursize = n;
+                       memmove(history, histptr - n + 1, n * sizeof(char *));
+                       cursize = n - 1;
                }
 
                history = aresize2(history, n, sizeof(char *), APERM);
@@ -551,15 +552,14 @@ sethistsize(int n)
 
 #if HAVE_PERSISTENT_HISTORY
 /*
- *     set history file
- *     This can mean reloading/resetting/starting history file
- *     maintenance
+ * set history file; this can mean reloading/resetting/starting
+ * history file maintenance
  */
 void
 sethistfile(const char *name)
 {
        /* if not started then nothing to do */
-       if (hstarted == 0)
+       if (hstarted == false)
                return;
 
        /* if the name is the same as the name we have */
@@ -567,13 +567,13 @@ sethistfile(const char *name)
                return;
 
        /*
-        * its a new name - possibly
+        * it's a new name - possibly
         */
-       if (histfd) {
+       if (histfd != -1) {
                /* yes the file is open */
                (void)close(histfd);
-               histfd = 0;
-               hsize = 0;
+               histfd = -1;
+               histfsize = 0;
                afree(hname, APERM);
                hname = NULL;
                /* let's reset the history */
@@ -586,13 +586,13 @@ sethistfile(const char *name)
 #endif
 
 /*
- *     initialise the history vector
+ * initialise the history vector
  */
 void
 init_histvec(void)
 {
        if (history == (char **)NULL) {
-               histsize = HISTORYSIZE;
+               histsize = MKSH_DEFHISTSIZE;
                history = alloc2(histsize, sizeof(char *), APERM);
                histptr = history - 1;
        }
@@ -600,11 +600,7 @@ init_histvec(void)
 
 
 /*
- *     Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
- *     a) permit HISTSIZE to control number of lines of history stored
- *     b) maintain a physical history file
- *
- *     It turns out that there is a lot of ghastly hackery here
+ * It turns out that there is a lot of ghastly hackery here
  */
 
 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
@@ -614,7 +610,7 @@ histsync(void)
 {
        bool changed = false;
 
-       if (histfd) {
+       if (histfd != -1) {
                int lno = hist_source->line;
 
                hist_source->line++;
@@ -638,6 +634,7 @@ histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
        char **hp;
        char *c, *cp;
 
+       mkssert(cmd != NULL);
        strdupx(c, cmd, APERM);
        if ((cp = strchr(c, '\n')) != NULL)
                *cp = '\0';
@@ -653,7 +650,7 @@ histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
        ++*lnp;
 
 #if HAVE_PERSISTENT_HISTORY
-       if (histfd && dowrite)
+       if (dowrite && histfd != -1)
                writehistfile(*lnp, c);
 #endif
 
@@ -670,395 +667,293 @@ histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
 }
 
 /*
- *     Write history data to a file nominated by HISTFILE
- *     if HISTFILE is unset then history still happens, but
- *     the data is not written to a file
- *     All copies of ksh looking at the file will maintain the
- *     same history. This is ksh behaviour.
+ * Write history data to a file nominated by HISTFILE;
+ * if HISTFILE is unset then history still happens, but
+ * the data is not written to a file. All copies of ksh
+ * looking at the file will maintain the same history.
+ * This is ksh behaviour.
+ *
+ * This stuff uses mmap()
  *
- *     This stuff uses mmap()
- *     if your system ain't got it - then you'll have to undef HISTORYFILE
+ * This stuff is so totally broken it must eventually be
+ * redesigned, without mmap, better checks, support for
+ * larger files, etc. and handle partially corrupted files
  */
 
 /*-
- *     Open a history file
- *     Format is:
- *     Bytes 1, 2:
- *             HMAGIC - just to check that we are dealing with
- *             the correct object
- *     Then follows a number of stored commands
- *     Each command is
- *     <command byte><command number(4 bytes)><bytes><null>
+ * Open a history file
+ * Format is:
+ * Bytes 1, 2:
+ *     HMAGIC - just to check that we are dealing with the correct object
+ * Then follows a number of stored commands
+ * Each command is
+ *     <command byte><command number(4 octets, big endian)><bytes><NUL>
  */
-#define HMAGIC1                0xab
-#define HMAGIC2                0xcd
-#define COMMAND                0xff
+#define HMAGIC1                0xAB
+#define HMAGIC2                0xCD
+#define COMMAND                0xFF
+
+#if HAVE_PERSISTENT_HISTORY
+static const unsigned char sprinkle[2] = { HMAGIC1, HMAGIC2 };
+#endif
 
 void
 hist_init(Source *s)
 {
 #if HAVE_PERSISTENT_HISTORY
        unsigned char *base;
-       int lines, fd, rv = 0;
-       off_t hfsize;
+       int lines, fd;
+       enum { hist_init_first, hist_init_retry, hist_init_restore } hs;
 #endif
 
        if (Flag(FTALKING) == 0)
                return;
 
-       hstarted = 1;
-
+       hstarted = true;
        hist_source = s;
 
 #if HAVE_PERSISTENT_HISTORY
        if ((hname = str_val(global("HISTFILE"))) == NULL)
                return;
        strdupx(hname, hname, APERM);
+       hs = hist_init_first;
 
  retry:
        /* we have a file and are interactive */
-       if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0)
+       if ((fd = open(hname, O_RDWR | O_CREAT | O_APPEND, 0600)) < 0)
                return;
 
        histfd = savefd(fd);
        if (histfd != fd)
                close(fd);
 
-       (void)flock(histfd, LOCK_EX);
+       mksh_lockfd(histfd);
 
-       hfsize = lseek(histfd, (off_t)0, SEEK_END);
-       hsize = 1024 * 1048576;
-       if (hfsize < (off_t)hsize)
-               hsize = (size_t)hfsize;
-
-       if (hsize == 0) {
-               /* add magic */
-               if (sprinkle(histfd)) {
-                       hist_finish();
-                       return;
-               }
-       } else if (hsize > 0) {
-               /*
-                * we have some data
-                */
-               base = (void *)mmap(NULL, hsize, PROT_READ,
+       histfsize = lseek(histfd, (off_t)0, SEEK_END);
+       if (histfsize > MKSH_MAXHISTFSIZE || hs == hist_init_restore) {
+               /* we ignore too large files but still append to them */
+               /* we also don't need to re-read after truncation */
+               goto hist_init_tail;
+       } else if (histfsize > 2) {
+               /* we have some data, check its validity */
+               base = (void *)mmap(NULL, (size_t)histfsize, PROT_READ,
                    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
-               /*
-                * check on its validity
-                */
-               if (base == (unsigned char *)MAP_FAILED ||
-                   *base != HMAGIC1 || base[1] != HMAGIC2) {
-                       if (base != (unsigned char *)MAP_FAILED)
-                               munmap((caddr_t)base, hsize);
+               if (base == (unsigned char *)MAP_FAILED)
+                       goto hist_init_fail;
+               if (base[0] != HMAGIC1 || base[1] != HMAGIC2) {
+                       munmap(caddr_cast(base), (size_t)histfsize);
+                       goto hist_init_fail;
+               }
+               /* load _all_ data */
+               lines = histload(hist_source, base + 2, (size_t)histfsize - 2);
+               munmap(caddr_cast(base), (size_t)histfsize);
+               /* check if the file needs to be truncated */
+               if (lines > histsize && histptr >= history) {
+                       /* you're fucked up with the current code, trust me */
+                       char *nhname, **hp;
+                       struct stat sb;
+
+                       /* create temporary file */
+                       nhname = shf_smprintf("%s.%d", hname, (int)procpid);
+                       if ((fd = open(nhname, O_RDWR | O_CREAT | O_TRUNC |
+                           O_EXCL, 0600)) < 0) {
+                               /* just don't truncate then, meh. */
+                               goto hist_trunc_dont;
+                       }
+                       if (fstat(histfd, &sb) >= 0 &&
+                           chown(nhname, sb.st_uid, sb.st_gid)) {
+                               /* abort the truncation then, meh. */
+                               goto hist_trunc_abort;
+                       }
+                       /* we definitively want some magic in that file */
+                       if (write(fd, sprinkle, 2) != 2)
+                               goto hist_trunc_abort;
+                       /* and of course the entries */
+                       hp = history;
+                       while (hp < histptr) {
+                               if (!writehistline(fd,
+                                   s->line - (histptr - hp), *hp))
+                                       goto hist_trunc_abort;
+                               ++hp;
+                       }
+                       /* now unlock, close both, rename, rinse, repeat */
+                       close(fd);
+                       fd = -1;
                        hist_finish();
-                       if (unlink(hname) /* fails */)
-                               goto hiniterr;
+                       if (rename(nhname, hname) < 0) {
+ hist_trunc_abort:
+                               if (fd != -1)
+                                       close(fd);
+                               unlink(nhname);
+                               if (fd != -1)
+                                       goto hist_trunc_dont;
+                               /* darn! restore histfd and pray */
+                       }
+                       hs = hist_init_restore;
+ hist_trunc_dont:
+                       afree(nhname, ATEMP);
+                       if (hs == hist_init_restore)
+                               goto retry;
+               }
+       } else if (histfsize != 0) {
+               /* negative or too small... */
+ hist_init_fail:
+               /* ... or mmap failed or illegal */
+               hist_finish();
+               /* nuke the bogus file then retry, at most once */
+               if (!unlink(hname) && hs != hist_init_retry) {
+                       hs = hist_init_retry;
                        goto retry;
                }
-               if (hsize > 2) {
-                       lines = hist_count_lines(base+2, hsize-2);
-                       if (lines > histsize) {
-                               /* we need to make the file smaller */
-                               if (hist_shrink(base, hsize))
-                                       rv = unlink(hname);
-                               munmap((caddr_t)base, hsize);
-                               hist_finish();
-                               if (rv) {
- hiniterr:
-                                       bi_errorf("can't %s %s: %s",
-                                           "unlink HISTFILE", hname,
-                                           strerror(errno));
-                                       hsize = 0;
-                                       return;
-                               }
-                               goto retry;
-                       }
+               if (hs != hist_init_retry)
+                       bi_errorf("can't %s %s: %s",
+                           "unlink HISTFILE", hname, cstrerror(errno));
+               histfsize = 0;
+               return;
+       } else {
+               /* size 0, add magic to the history file */
+               if (write(histfd, sprinkle, 2) != 2) {
+                       hist_finish();
+                       return;
                }
-               histload(hist_source, base+2, hsize-2);
-               munmap((caddr_t)base, hsize);
        }
-       (void)flock(histfd, LOCK_UN);
-       hfsize = lseek(histfd, (off_t)0, SEEK_END);
-       hsize = 1024 * 1048576;
-       if (hfsize < (off_t)hsize)
-               hsize = hfsize;
+       histfsize = lseek(histfd, (off_t)0, SEEK_END);
+ hist_init_tail:
+       mksh_unlkfd(histfd);
 #endif
 }
 
 #if HAVE_PERSISTENT_HISTORY
-typedef enum state {
-       shdr,           /* expecting a header */
-       sline,          /* looking for a null byte to end the line */
-       sn1,            /* bytes 1 to 4 of a line no */
-       sn2, sn3, sn4
-} State;
-
-static int
-hist_count_lines(unsigned char *base, int bytes)
-{
-       State state = shdr;
-       int lines = 0;
-
-       while (bytes--) {
-               switch (state) {
-               case shdr:
-                       if (*base == COMMAND)
-                               state = sn1;
-                       break;
-               case sn1:
-                       state = sn2; break;
-               case sn2:
-                       state = sn3; break;
-               case sn3:
-                       state = sn4; break;
-               case sn4:
-                       state = sline; break;
-               case sline:
-                       if (*base == '\0') {
-                               lines++;
-                               state = shdr;
-                       }
-               }
-               base++;
-       }
-       return (lines);
-}
-
 /*
- *     Shrink the history file to histsize lines
+ * load the history structure from the stored data
  */
 static int
-hist_shrink(unsigned char *oldbase, int oldbytes)
+histload(Source *s, unsigned char *base, size_t bytes)
 {
-       int fd, rv = 0;
-       char *nfile = NULL;
-       struct  stat statb;
-       unsigned char *nbase = oldbase;
-       int nbytes = oldbytes;
-
-       nbase = hist_skip_back(nbase, &nbytes, histsize);
-       if (nbase == NULL)
-               return (1);
-       if (nbase == oldbase)
-               return (0);
-
-       /*
-        *      create temp file
-        */
-       nfile = shf_smprintf("%s.%d", hname, (int)procpid);
-       if ((fd = open(nfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0)
-               goto errout;
-       if (fstat(histfd, &statb) >= 0 &&
-           chown(nfile, statb.st_uid, statb.st_gid))
-               goto errout;
-
-       if (sprinkle(fd) || write(fd, nbase, nbytes) != nbytes)
-               goto errout;
-       close(fd);
-       fd = -1;
-
-       /*
-        *      rename
-        */
-       if (rename(nfile, hname) < 0) {
- errout:
-               if (fd >= 0) {
-                       close(fd);
-                       if (nfile)
-                               unlink(nfile);
-               }
-               rv = 1;
-       }
-       afree(nfile, ATEMP);
-       return (rv);
-}
-
-/*
- *     find a pointer to the data 'no' back from the end of the file
- *     return the pointer and the number of bytes left
- */
-static unsigned char *
-hist_skip_back(unsigned char *base, int *bytes, int no)
-{
-       int lines = 0;
-       unsigned char *ep;
-
-       for (ep = base + *bytes; --ep > base; ) {
-               /*
-                * this doesn't really work: the 4 byte line number that
-                * is encoded after the COMMAND byte can itself contain
-                * the COMMAND byte....
-                */
-               for (; ep > base && *ep != COMMAND; ep--)
-                       ;
-               if (ep == base)
-                       break;
-               if (++lines == no) {
-                       *bytes = *bytes - ((char *)ep - (char *)base);
-                       return (ep);
+       int lno = 0, lines = 0;
+       unsigned char *cp;
+
+ histload_loop:
+       /* !bytes check as some systems (older FreeBSDs) have buggy memchr */
+       if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL)
+               return (lines);
+       /* advance base pointer past COMMAND byte */
+       bytes -= ++cp - base;
+       base = cp;
+       /* if there is no full string left, don't bother with the rest */
+       if (bytes < 5 || (cp = memchr(base + 4, '\0', bytes - 4)) == NULL)
+               return (lines);
+       /* load the stored line number */
+       lno = ((base[0] & 0xFF) << 24) | ((base[1] & 0xFF) << 16) |
+           ((base[2] & 0xFF) << 8) | (base[3] & 0xFF);
+       /* store away the found line (@base[4]) */
+       ++lines;
+       if (histptr >= history && lno - 1 != s->line) {
+               /* a replacement? */
+               char **hp;
+
+               if (lno >= s->line - (histptr - history) && lno <= s->line) {
+                       hp = &histptr[lno - s->line];
+                       if (*hp)
+                               afree(*hp, APERM);
+                       strdupx(*hp, (char *)(base + 4), APERM);
                }
+       } else {
+               s->line = lno--;
+               histsave(&lno, (char *)(base + 4), false, false);
        }
-       return (NULL);
-}
-
-/*
- *     load the history structure from the stored data
- */
-static void
-histload(Source *s, unsigned char *base, int bytes)
-{
-       State state;
-       int lno = 0;
-       unsigned char *line = NULL;
-
-       for (state = shdr; bytes-- > 0; base++) {
-               switch (state) {
-               case shdr:
-                       if (*base == COMMAND)
-                               state = sn1;
-                       break;
-               case sn1:
-                       lno = (((*base)&0xff)<<24);
-                       state = sn2;
-                       break;
-               case sn2:
-                       lno |= (((*base)&0xff)<<16);
-                       state = sn3;
-                       break;
-               case sn3:
-                       lno |= (((*base)&0xff)<<8);
-                       state = sn4;
-                       break;
-               case sn4:
-                       lno |= (*base)&0xff;
-                       line = base+1;
-                       state = sline;
-                       break;
-               case sline:
-                       if (*base == '\0') {
-                               /* worry about line numbers */
-                               if (histptr >= history && lno-1 != s->line) {
-                                       /* a replacement ? */
-                                       histinsert(s, lno, (char *)line);
-                               } else {
-                                       s->line = lno--;
-                                       histsave(&lno, (char *)line, false,
-                                           false);
-                               }
-                               state = shdr;
-                       }
-               }
-       }
-}
-
-/*
- *     Insert a line into the history at a specified number
- */
-static void
-histinsert(Source *s, int lno, const char *line)
-{
-       char **hp;
-
-       if (lno >= s->line - (histptr - history) && lno <= s->line) {
-               hp = &histptr[lno - s->line];
-               if (*hp)
-                       afree(*hp, APERM);
-               strdupx(*hp, line, APERM);
-       }
+       /* advance base pointer past NUL */
+       bytes -= ++cp - base;
+       base = cp;
+       /* repeat until no more */
+       goto histload_loop;
 }
 
 /*
- *     write a command to the end of the history file
- *     This *MAY* seem easy but it's also necessary to check
- *     that the history file has not changed in size.
- *     If it has - then some other shell has written to it
- *     and we should read those commands to update our history
+ * write a command to the end of the history file
+ *
+ * This *MAY* seem easy but it's also necessary to check
+ * that the history file has not changed in size.
+ * If it has - then some other shell has written to it and
+ * we should (re)read those commands to update our history
  */
 static void
-writehistfile(int lno, char *cmd)
+writehistfile(int lno, const char *cmd)
 {
        off_t sizenow;
-       ssize_t bytes;
-       unsigned char *base, *news, hdr[5];
+       size_t bytes;
+       unsigned char *base, *news;
 
-       (void)flock(histfd, LOCK_EX);
+       mksh_lockfd(histfd);
        sizenow = lseek(histfd, (off_t)0, SEEK_END);
-       if ((sizenow <= (1024 * 1048576)) && ((size_t)sizenow != hsize)) {
-               /*
-                *      Things have changed
-                */
-               if ((size_t)sizenow > hsize) {
-                       /* someone has added some lines */
-                       bytes = (size_t)sizenow - hsize;
-                       base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ,
-                           MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
-                       if (base == (unsigned char *)MAP_FAILED)
-                               goto bad;
-                       news = base + hsize;
-                       if (*news != COMMAND) {
-                               munmap((caddr_t)base, (size_t)sizenow);
-                               goto bad;
-                       }
+       if (sizenow < histfsize) {
+               /* the file has shrunk; give up */
+               goto bad;
+       }
+       if (
+               /* ignore changes when the file is too large */
+               sizenow <= MKSH_MAXHISTFSIZE
+           &&
+               /* the size has changed, we need to do read updates */
+               sizenow > histfsize
+           ) {
+               /* both sizenow and histfsize are <= MKSH_MAXHISTFSIZE */
+               bytes = (size_t)(sizenow - histfsize);
+               base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ,
+                   MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
+               if (base == (unsigned char *)MAP_FAILED)
+                       goto bad;
+               news = base + (size_t)histfsize;
+               if (*news == COMMAND) {
                        hist_source->line--;
                        histload(hist_source, news, bytes);
                        hist_source->line++;
                        lno = hist_source->line;
-                       munmap((caddr_t)base, (size_t)sizenow);
-                       hsize = (size_t)sizenow;
-               } else {
-                       /* it has shrunk */
-                       /* but to what? */
-                       /* we'll give up for now */
-                       goto bad;
-               }
-       }
-       if (cmd) {
-               /*
-                *      we can write our bit now
-                */
-               hdr[0] = COMMAND;
-               hdr[1] = (lno>>24)&0xff;
-               hdr[2] = (lno>>16)&0xff;
-               hdr[3] = (lno>>8)&0xff;
-               hdr[4] = lno&0xff;
-               bytes = strlen(cmd) + 1;
-               if ((write(histfd, hdr, 5) != 5) ||
-                   (write(histfd, cmd, bytes) != bytes))
+               } else
+                       bytes = 0;
+               munmap(caddr_cast(base), (size_t)sizenow);
+               if (!bytes)
                        goto bad;
-               sizenow = lseek(histfd, (off_t)0, SEEK_END);
-               hsize = 1024 * 1048576;
-               if (sizenow < (off_t)hsize)
-                       hsize = (size_t)sizenow;
        }
-       (void)flock(histfd, LOCK_UN);
-       return;
+       if (cmd && !writehistline(histfd, lno, cmd)) {
  bad:
-       hist_finish();
+               hist_finish();
+               return;
+       }
+       histfsize = lseek(histfd, (off_t)0, SEEK_END);
+       mksh_unlkfd(histfd);
 }
 
-void
-hist_finish(void)
+static int
+writehistline(int fd, int lno, const char *cmd)
 {
-       (void)flock(histfd, LOCK_UN);
-       (void)close(histfd);
-       histfd = 0;
+       ssize_t n;
+       unsigned char hdr[5];
+
+       hdr[0] = COMMAND;
+       hdr[1] = (lno >> 24) & 0xFF;
+       hdr[2] = (lno >> 16) & 0xFF;
+       hdr[3] = (lno >> 8) & 0xFF;
+       hdr[4] = lno & 0xFF;
+       n = strlen(cmd) + 1;
+       return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n);
 }
 
-/*
- *     add magic to the history file
- */
-static int
-sprinkle(int fd)
+void
+hist_finish(void)
 {
-       static const unsigned char mag[] = { HMAGIC1, HMAGIC2 };
-
-       return (write(fd, mag, 2) != 2);
+       if (histfd >= 0) {
+               mksh_unlkfd(histfd);
+               (void)close(histfd);
+       }
+       histfd = -1;
 }
 #endif
 
+
 #if !HAVE_SYS_SIGNAME
 static const struct mksh_sigpair {
-       const char *const name;
+       const char * const name;
        int nr;
 } mksh_sigpairs[] = {
 #include "signames.inc"
@@ -1066,6 +961,12 @@ static const struct mksh_sigpair {
 };
 #endif
 
+#if HAVE_SYS_SIGLIST
+#if !HAVE_SYS_SIGLIST_DECL
+extern const char * const sys_siglist[];
+#endif
+#endif
+
 void
 inittraps(void)
 {
@@ -1095,8 +996,14 @@ inittraps(void)
                        else {
                                char *s;
 
-                               if (!strncasecmp(cs, "SIG", 3))
+                               /* this is not optimal, what about SIGSIG1? */
+                               if ((cs[0] & 0xDF) == 'S' &&
+                                   (cs[1] & 0xDF) == 'I' &&
+                                   (cs[2] & 0xDF) == 'G' &&
+                                   cs[3] != '\0') {
+                                       /* skip leading "SIG" */
                                        cs += 3;
+                               }
                                strdupx(s, cs, APERM);
                                sigtraps[i].name = s;
                                while ((*s = ksh_toupper(*s)))
@@ -1164,27 +1071,45 @@ alarm_catcher(int sig MKSH_A_UNUSED)
 }
 
 Trap *
-gettrap(const char *name, int igncase)
+gettrap(const char *cs, bool igncase)
 {
-       int n = NSIG + 1;
+       int i;
        Trap *p;
-       const char *n2;
-       int (*cmpfunc)(const char *, const char *) = strcmp;
+       char *as;
 
-       if (ksh_isdigit(*name)) {
-               if (getn(name, &n) && 0 <= n && n < NSIG)
-                       return (&sigtraps[n]);
-               else
-                       return (NULL);
+       if (ksh_isdigit(*cs)) {
+               return ((getn(cs, &i) && 0 <= i && i < NSIG) ?
+                   (&sigtraps[i]) : NULL);
        }
 
-       n2 = strncasecmp(name, "SIG", 3) ? NULL : name + 3;
-       if (igncase)
-               cmpfunc = strcasecmp;
-       for (p = sigtraps; --n >= 0; p++)
-               if (!cmpfunc(p->name, name) || (n2 && !cmpfunc(p->name, n2)))
-                       return (p);
-       return (NULL);
+       /* this breaks SIGSIG1, but we do that above anyway */
+       if ((cs[0] & 0xDF) == 'S' &&
+           (cs[1] & 0xDF) == 'I' &&
+           (cs[2] & 0xDF) == 'G' &&
+           cs[3] != '\0') {
+               /* skip leading "SIG" */
+               cs += 3;
+       }
+       if (igncase) {
+               char *s;
+
+               strdupx(as, cs, ATEMP);
+               cs = s = as;
+               while ((*s = ksh_toupper(*s)))
+                       ++s;
+       } else
+               as = NULL;
+
+       p = sigtraps;
+       for (i = 0; i <= NSIG; i++) {
+               if (!strcmp(p->name, cs))
+                       goto found;
+               ++p;
+       }
+       p = NULL;
+ found:
+       afree(as, ATEMP);
+       return (p);
 }
 
 /*
@@ -1194,7 +1119,7 @@ void
 trapsig(int i)
 {
        Trap *p = &sigtraps[i];
-       int errno_sv = errno;
+       int eno = errno;
 
        trap = p->set = 1;
        if (p->flags & TF_DFL_INTR)
@@ -1205,7 +1130,7 @@ trapsig(int i)
        }
        if (p->shtrap)
                (*p->shtrap)(i);
-       errno = errno_sv;
+       errno = eno;
 }
 
 /*
@@ -1306,12 +1231,12 @@ runtrap(Trap *p, bool is_last)
                /* SIG_DFL */
                if (p->flags & TF_FATAL) {
                        /* eg, SIGHUP */
-                       exstat = 128 + i;
+                       exstat = (int)ksh_min(128U + (unsigned)i, 255U);
                        unwind(LLEAVE);
                }
                if (p->flags & TF_DFL_INTR) {
                        /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
-                       exstat = 128 + i;
+                       exstat = (int)ksh_min(128U + (unsigned)i, 255U);
                        unwind(LINTR);
                }
                goto donetrap;
@@ -1326,7 +1251,7 @@ runtrap(Trap *p, bool is_last)
                p->trap = NULL;
        }
        if (trap_exstat == -1)
-               trap_exstat = exstat;
+               trap_exstat = exstat & 0xFF;
        /*
         * Note: trapstr is fully parsed before anything is executed, thus
         * no problem with afree(p->trap) in settrap() while still in use.
@@ -1457,6 +1382,8 @@ setsig(Trap *p, sig_t f, int flags)
        if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR)
                return (1);
 
+       memset(&sigact, 0, sizeof(sigact));
+
        /*
         * First time setting this signal? If so, get and note the current
         * setting.
@@ -1530,3 +1457,56 @@ setexecsig(Trap *p, int restore)
                break;
        }
 }
+
+#if HAVE_PERSISTENT_HISTORY || defined(DF)
+/*
+ * File descriptor locking and unlocking functions.
+ * Could use some error handling, but hey, this is only
+ * advisory locking anyway, will often not work over NFS,
+ * and you are SOL if this fails...
+ */
+
+void
+mksh_lockfd(int fd)
+{
+#if defined(__OpenBSD__)
+       /* flock is not interrupted by signals */
+       (void)flock(fd, LOCK_EX);
+#elif HAVE_FLOCK
+       int rv;
+
+       /* e.g. on Linux */
+       do {
+               rv = flock(fd, LOCK_EX);
+       } while (rv == 1 && errno == EINTR);
+#elif HAVE_LOCK_FCNTL
+       int rv;
+       struct flock lks;
+
+       memset(&lks, 0, sizeof(lks));
+       lks.l_type = F_WRLCK;
+       do {
+               rv = fcntl(fd, F_SETLKW, &lks);
+       } while (rv == 1 && errno == EINTR);
+#endif
+}
+
+/* designed to not define mksh_unlkfd if none triggered */
+#if HAVE_FLOCK
+void
+mksh_unlkfd(int fd)
+{
+       (void)flock(fd, LOCK_UN);
+}
+#elif HAVE_LOCK_FCNTL
+void
+mksh_unlkfd(int fd)
+{
+       struct flock lks;
+
+       memset(&lks, 0, sizeof(lks));
+       lks.l_type = F_UNLCK;
+       (void)fcntl(fd, F_SETLKW, &lks);
+}
+#endif
+#endif