aboutsummaryrefslogtreecommitdiff
path: root/system/pdksh/patches/010_OpenBSD-patches.patch
diff options
context:
space:
mode:
Diffstat (limited to 'system/pdksh/patches/010_OpenBSD-patches.patch')
-rw-r--r--system/pdksh/patches/010_OpenBSD-patches.patch1112
1 files changed, 1112 insertions, 0 deletions
diff --git a/system/pdksh/patches/010_OpenBSD-patches.patch b/system/pdksh/patches/010_OpenBSD-patches.patch
new file mode 100644
index 000000000000..ebb3c9e64c0d
--- /dev/null
+++ b/system/pdksh/patches/010_OpenBSD-patches.patch
@@ -0,0 +1,1112 @@
+ * Applied some patches from OpenBSD:
+ + Use mkstemp to create temporary files
+ + Kill -s now works
+ + Escapes special characters in tab completitions
+ + Introduce FSH flag, which is set when the shell is called as `sh'.
+ + tree.c: Fix three off-by-one errors.
+ + c_sh.c: don't set close-on-exec flag on file descriptors in FSH mode
+ (closes: #154540). Documented the change in ksh(1).
+ + history.c: Compare the return from mmap with MAP_FAILED, do not cast it
+ to int and compare with -1.
+ + main.c: set edit mode to emacs by default, may be overridden by the
+ environment or the user. Also, we want tab completion in vi by default.
+ + misc.c: use strtol() in getn().
+ + emacs.c:
+ - bind TAB (^I) to complete-list by default
+ - complete-list first completes; if that does not work, it lists
+ - fix a memleak in do_complete()
+ + edit.c:
+ - completion now works after '=' (dd), and ':' (ssh) and ` (backtick)
+ - add '#' to the list of escaped characters during vi/emacs filename
+ completion
+
+ + exec.c: Found and fixed yet another problem with `set -e' scripts
+ (see a changelog entry for 5.2.14-3), which caused `dpkg-buildpackage -B'
+ to fail on systems where /bin/sh is ksh.
+
+ + c_sh.c: Make `set' command return 0 always, not only in the POSIX mode.
+ According to Jeff Sheinberg <jeffsh@localnet.com>, this new behaviour
+ is more compatible with SUSv2 standard and other shells (esp. ksh93)
+ (closes: #118476). Documented the change in ksh(1) man page.
+
+ + c_test.c: The special case code for "test -x" over NFS was
+ incorrect. The right thing to do is to try access(2) first
+ (since that occurs on the NFS server side) and only check for the
+ absence of an execute bit when access(2) succeeds.
+ + edit.c: in word location, fix forward scanning so it correctly
+ account for any escaped char and not only spaces. for "foo
+ (bar.a)" and "foo (bar a)", cd foo\ \(bar.<tab> will correctly
+ expand to foo\ \(bar.a\).
+
+ + vi.c: Buffers are not strings so use memcpy(), not strlcpy() to copy
+ them. Also add some further bounds checks in the name of paranoia.
+ + exec.c: Unbreak parameter assignment when calling bourne style
+ functions.
+ + exec.c: For the >& and <& operators, add a check for "dup from" ==
+ "dup to" and just return success if they are the same. Fixes the
+ "ls 2>&2" problem.
+ + eval.c, exec.c, io.c, jobs.c: If "from fd" == "to fd" don't call
+ dup2() or close "from fd".
+
+Index: pdksh-5.2.14/c_ksh.c
+===================================================================
+--- pdksh-5.2.14.orig/c_ksh.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/c_ksh.c 2009-09-17 00:32:08.000000000 +0200
+@@ -1208,6 +1208,7 @@
+ builtin_opt.optarg);
+ return 1;
+ }
++ break;
+ case '?':
+ return 1;
+ }
+Index: pdksh-5.2.14/edit.c
+===================================================================
+--- pdksh-5.2.14.orig/edit.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/edit.c 2009-09-17 00:32:08.000000000 +0200
+@@ -15,6 +15,9 @@
+ # include <sys/stream.h> /* needed for <sys/ptem.h> */
+ # include <sys/ptem.h> /* needed for struct winsize */
+ #endif /* OS_SCO */
++#ifdef DEBIAN
++#include <sys/ioctl.h>
++#endif /* DEBIAN */
+ #include <ctype.h>
+ #include "ksh_stat.h"
+
+@@ -552,7 +555,11 @@
+ {
+ char *toglob;
+ char **words;
++#ifndef DEBIAN
+ int nwords;
++#else /* DEBIAN */ /* patch from OpenBSD */
++ int nwords, i, idx, escaping;
++#endif /* DEBIAN */
+ XPtrV w;
+ struct source *s, *sold;
+
+@@ -561,6 +568,22 @@
+
+ toglob = add_glob(str, slen);
+
++#ifdef DEBIAN /* patch from OpenBSD */
++ /* remove all escaping backward slashes */
++ escaping = 0;
++ for(i = 0, idx = 0; toglob[i]; i++) {
++ if (toglob[i] == '\\' && !escaping) {
++ escaping = 1;
++ continue;
++ }
++
++ toglob[idx] = toglob[i];
++ idx++;
++ if (escaping) escaping = 0;
++ }
++ toglob[idx] = '\0';
++
++#endif /* DEBIAN */
+ /*
+ * Convert "foo*" (toglob) to an array of strings (words)
+ */
+@@ -722,7 +745,12 @@
+ return nwords;
+ }
+
++#ifndef DEBIAN
+ #define IS_WORDC(c) !(ctype(c, C_LEX1) || (c) == '\'' || (c) == '"')
++#else /* patch from OpenBSD */
++#define IS_WORDC(c) !( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"' \
++ || (c) == '`' || (c) == '=' || (c) == ':' )
++#endif
+
+ static int
+ x_locate_word(buf, buflen, pos, startp, is_commandp)
+@@ -747,11 +775,23 @@
+ /* Keep going backwards to start of word (has effect of allowing
+ * one blank after the end of a word)
+ */
++#ifndef DEBIAN
+ for (; start > 0 && IS_WORDC(buf[start - 1]); start--)
++#else /* DEBIAN */ /* patch from OpenBSD */
++ for (; (start > 0 && IS_WORDC(buf[start - 1]))
++ || (start > 1 && buf[start-2] == '\\'); start--)
++#endif /* DEBIAN */
+ ;
+ /* Go forwards to end of word */
++#ifndef DEBIAN
+ for (end = start; end < buflen && IS_WORDC(buf[end]); end++)
+ ;
++#else /* DEBIAN */ /* patch from OpenBSD */
++ for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
++ if (buf[end] == '\\' && (end+1) < buflen)
++ end++;
++ }
++#endif /* DEBIAN */
+
+ if (is_commandp) {
+ int iscmd;
+@@ -759,7 +799,11 @@
+ /* Figure out if this is a command */
+ for (p = start - 1; p >= 0 && isspace(buf[p]); p--)
+ ;
++#ifndef DEBIAN
+ iscmd = p < 0 || strchr(";|&()", buf[p]);
++#else /* DEBIAN */ /* patch from OpenBSD */
++ iscmd = p < 0 || strchr(";|&()`", buf[p]);
++#endif
+ if (iscmd) {
+ /* If command has a /, path, etc. is not searched;
+ * only current directory is searched, which is just
+@@ -961,6 +1005,9 @@
+ {
+ const char *sp, *p;
+ char *xp;
++#ifdef DEBIAN /* patch from OpenBSD */
++ int staterr;
++#endif /* DEBIAN */
+ int pathlen;
+ int patlen;
+ int oldsize, newsize, i, j;
+@@ -995,13 +1042,23 @@
+ memcpy(xp, pat, patlen);
+
+ oldsize = XPsize(*wp);
++#ifndef DEBIAN
+ glob_str(Xstring(xs, xp), wp, 0);
++#else /* DEBIAN */ /* patch from OpenBSD */
++ glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
++#endif
+ newsize = XPsize(*wp);
+
+ /* Check that each match is executable... */
+ words = (char **) XPptrv(*wp);
+ for (i = j = oldsize; i < newsize; i++) {
++#ifndef DEBIAN
+ if (search_access(words[i], X_OK, (int *) 0) >= 0) {
++#else /* DEBIAN */ /* patch from OpenBSD */
++ staterr = 0;
++ if ((search_access(words[i], X_OK, &staterr) >= 0)
++ || (staterr == EISDIR)) {
++#endif
+ words[j] = words[i];
+ if (!(flags & XCF_FULLPATH))
+ memmove(words[j], words[j] + pathlen,
+@@ -1018,4 +1075,42 @@
+ Xfree(xs, xp);
+ }
+
++#ifdef DEBIAN /* patch from OpenBSD */
++/*
++ * if argument string contains any special characters, they will
++ * be escaped and the result will be put into edit buffer by
++ * keybinding-specific function
++ */
++int
++x_escape(s, len, putbuf_func)
++ const char *s;
++ size_t len;
++ int putbuf_func ARGS((const char *s, size_t len));
++{
++ size_t add, wlen;
++ const char *ifs = str_val(local("IFS", 0));
++ int rval=0;
++
++ for (add = 0, wlen = len; wlen - add > 0; add++) {
++ if (strchr("\\$(){}*&;#|<>\"'`", s[add]) || strchr(ifs, s[add])) {
++ if (putbuf_func(s, add) != 0) {
++ rval = -1;
++ break;
++ }
++
++ putbuf_func("\\", 1);
++ putbuf_func(&s[add], 1);
++
++ add++;
++ wlen -= add;
++ s += add;
++ add = -1; /* after the increment it will go to 0 */
++ }
++ }
++ if (wlen > 0 && rval == 0)
++ rval = putbuf_func(s, wlen);
++
++ return (rval);
++}
++#endif /* DEBIAN */
+ #endif /* EDIT */
+Index: pdksh-5.2.14/edit.h
+===================================================================
+--- pdksh-5.2.14.orig/edit.h 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/edit.h 2009-09-17 00:32:08.000000000 +0200
+@@ -55,6 +55,9 @@
+ int x_longest_prefix ARGS((int nwords, char *const *words));
+ int x_basename ARGS((const char *s, const char *se));
+ void x_free_words ARGS((int nwords, char **words));
++#ifdef DEBIAN /* patch from OpenBSD */
++int x_escape ARGS((const char *, size_t, int (*)(const char *s, size_t len)));
++#endif /* DEBIAN */
+ /* emacs.c */
+ int x_emacs ARGS((char *buf, size_t len));
+ void x_init_emacs ARGS((void));
+Index: pdksh-5.2.14/emacs.c
+===================================================================
+--- pdksh-5.2.14.orig/emacs.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/emacs.c 2009-09-17 00:32:08.000000000 +0200
+@@ -138,6 +138,10 @@
+ static int x_e_getc ARGS((void));
+ static void x_e_putc ARGS((int c));
+ static void x_e_puts ARGS((const char *s));
++#ifdef DEBIAN /* patch from OpenBSD */
++static int x_comment ARGS((int c));
++static int x_emacs_putbuf ARGS((const char *s, size_t len));
++#endif /* DEBIAN */
+ static int x_fold_case ARGS((int c));
+ static char *x_lastcp ARGS((void));
+ static void do_complete ARGS((int flags, Comp_type type));
+@@ -269,6 +273,9 @@
+ { XFUNC_transpose, 0, CTRL('T') },
+ #endif
+ { XFUNC_complete, 1, CTRL('[') },
++#ifdef DEBIAN /* patch from OpenBSD */
++ { XFUNC_comp_list, 0, CTRL('I') },
++#endif /* DEBIAN */
+ { XFUNC_comp_list, 1, '=' },
+ { XFUNC_enumerate, 1, '?' },
+ { XFUNC_expand, 1, '*' },
+@@ -313,6 +320,9 @@
+ * entries.
+ */
+ { XFUNC_meta2, 1, '[' },
++#ifdef DEBIAN /* patch from OpenBSD */
++ { XFUNC_meta2, 1, 'O' },
++#endif /* DEBIAN */
+ { XFUNC_prev_com, 2, 'A' },
+ { XFUNC_next_com, 2, 'B' },
+ { XFUNC_mv_forw, 2, 'C' },
+@@ -468,6 +478,23 @@
+ return 0;
+ }
+
++#ifdef DEBIAN /* patch from OpenBSD */
++/*
++ * this is used for x_escape() in do_complete()
++ */
++static int
++x_emacs_putbuf(s, len)
++ const char *s;
++ size_t len;
++{
++ int rval;
++
++ if ((rval = x_do_ins(s, len)) != 0)
++ return (rval);
++ return (rval);
++}
++
++#endif /* DEBIAN */
+ static int
+ x_del_back(c)
+ int c;
+@@ -1485,7 +1512,11 @@
+ for (j = 0; j < X_TABSZ; j++)
+ x_tab[i][j] = XFUNC_error;
+ for (i = 0; i < NELEM(x_defbindings); i++)
++#ifndef DEBIAN
+ x_tab[x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char]
++#else /* DEBIAN */ /* patch from OpenBSD */
++ x_tab[(unsigned char)x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char]
++#endif /* DEBIAN */
+ = x_defbindings[i].xdb_func;
+
+ x_atab = (char *(*)[X_TABSZ]) alloc(sizeofN(*x_atab, X_NTABS), AEDIT);
+@@ -1754,6 +1785,7 @@
+ int flags; /* XCF_{COMMAND,FILE,COMMAND_FILE} */
+ Comp_type type;
+ {
++#ifndef DEBIAN
+ char **words;
+ int nwords = 0;
+ int start, end;
+@@ -1828,8 +1860,13 @@
+ if (nlen > 0) {
+ x_goto(xbuf + start);
+ x_delete(end - start, FALSE);
++#ifndef DEBIAN
+ words[0][nlen] = '\0';
+ x_ins(words[0]);
++#else /* DEBIAN */ /* patch from OpenBSD */
++ x_escape(words[0], nlen, x_emacs_putbuf);
++ x_adjust();
++#endif /* DEBIAN */
+ /* If single match is not a directory, add a
+ * space to the end...
+ */
+@@ -1841,6 +1878,54 @@
+ }
+ break;
+ }
++#else /* patch from OpenBSD */
++ char **words;
++ int nwords;
++ int start, end, nlen, olen;
++ int is_command;
++ int completed = 0;
++
++ nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf,
++ &start, &end, &words, &is_command);
++ /* no match */
++ if (nwords == 0) {
++ x_e_putc(BEL);
++ return;
++ }
++
++ if (type == CT_LIST) {
++ x_print_expansions(nwords, words, is_command);
++ x_redraw(0);
++ x_free_words(nwords, words);
++ return;
++ }
++
++ olen = end - start;
++ nlen = x_longest_prefix(nwords, words);
++ /* complete */
++ if (nlen > olen) {
++ x_goto(xbuf + start);
++ x_delete(olen, FALSE);
++ x_escape(words[0], nlen, x_emacs_putbuf);
++ x_adjust();
++ completed = 1;
++ }
++ /* add space if single non-dir match */
++ if ((nwords == 1) && (!ISDIRSEP(words[0][nlen - 1]))) {
++ x_ins(space);
++ completed = 1;
++ }
++
++ if (type == CT_COMPLIST && !completed) {
++ x_print_expansions(nwords, words, is_command);
++ completed = 1;
++ }
++
++ if (completed)
++ x_redraw(0);
++
++ x_free_words(nwords, words);
++#endif /* DEBIAN */
+ }
+
+ /* NAME:
+Index: pdksh-5.2.14/io.c
+===================================================================
+--- pdksh-5.2.14.orig/io.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/io.c 2009-09-17 00:32:08.000000000 +0200
+@@ -318,7 +318,7 @@
+ shf_flush(&shf_iob[fd]);
+ if (ofd < 0) /* original fd closed */
+ close(fd);
+- else {
++ else if (fd != ofd) {
+ ksh_dup2(ofd, fd, TRUE); /* XXX: what to do if this fails? */
+ close(ofd);
+ }
+@@ -502,7 +502,9 @@
+ Temp_type type;
+ struct temp **tlist;
+ {
++#ifndef DEBIAN
+ static unsigned int inc;
++#endif
+ struct temp *tp;
+ int len;
+ int fd;
+@@ -516,6 +518,14 @@
+ tp->name = path = (char *) &tp[1];
+ tp->shf = (struct shf *) 0;
+ tp->type = type;
++#ifdef DEBIAN /* based on patch from OpenBSD */
++ shf_snprintf(path, len, "%s/kshXXXXXX", dir);
++ fd = mkstemp(path);
++ if (fd >= 0)
++ tp->shf = shf_fdopen(fd, SHF_WR, (struct shf *) 0);
++ if (fd >= 0)
++ fchmod(fd, 0600);
++#else /* DEBIAN */
+ while (1) {
+ /* Note that temp files need to fit 8.3 DOS limits */
+ shf_snprintf(path, len, "%s/sh%05u.%03x",
+@@ -542,6 +552,7 @@
+ break;
+ }
+ tp->next = NULL;
++#endif /* DEBIAN */
+ tp->pid = procpid;
+
+ tp->next = *tlist;
+Index: pdksh-5.2.14/vi.c
+===================================================================
+--- pdksh-5.2.14.orig/vi.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/vi.c 2009-09-17 00:32:08.000000000 +0200
+@@ -63,6 +63,9 @@
+ static void vi_pprompt ARGS((int full));
+ static void vi_error ARGS((void));
+ static void vi_macro_reset ARGS((void));
++#ifdef DEBIAN /* patch from OpenBSD */
++static int x_vi_putbuf ARGS((const char *s, size_t len));
++#endif /* DEBIAN */
+
+ #define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */
+ #define M_ 0x2 /* movement command (h, l, etc.) */
+@@ -235,7 +238,7 @@
+
+ x_putc('\r'); x_putc('\n'); x_flush();
+
+- if (c == -1)
++ if (c == -1 || len <= es->linelen)
+ return -1;
+
+ if (es->cbuf != buf)
+@@ -459,15 +462,22 @@
+ else {
+ locpat[srchlen++] = ch;
+ if ((ch & 0x80) && Flag(FVISHOW8)) {
++ if (es->linelen + 2 > es->cbufsize)
++ vi_error();
+ es->cbuf[es->linelen++] = 'M';
+ es->cbuf[es->linelen++] = '-';
+ ch &= 0x7f;
+ }
+ if (ch < ' ' || ch == 0x7f) {
++ if (es->linelen + 2 > es->cbufsize)
++ vi_error();
+ es->cbuf[es->linelen++] = '^';
+ es->cbuf[es->linelen++] = ch ^ '@';
+- } else
++ } else {
++ if (es->linelen >= es->cbufsize)
++ vi_error();
+ es->cbuf[es->linelen++] = ch;
++ }
+ es->cursor = es->linelen;
+ refresh(0);
+ }
+@@ -690,7 +700,7 @@
+ /* End nonstandard vi commands } */
+
+ default:
+- if (es->linelen == es->cbufsize - 1)
++ if (es->linelen >= es->cbufsize - 1)
+ return -1;
+ ibuf[inslen++] = ch;
+ if (insert == INSERT) {
+@@ -1403,7 +1413,7 @@
+ new = (struct edstate *)alloc(sizeof(struct edstate), APERM);
+ new->cbuf = alloc(old->cbufsize, APERM);
+ new->cbufsize = old->cbufsize;
+- strcpy(new->cbuf, old->cbuf);
++ memcpy(new->cbuf, old->cbuf, old->linelen);
+ new->linelen = old->linelen;
+ new->cursor = old->cursor;
+ new->winleft = old->winleft;
+@@ -1414,7 +1424,7 @@
+ restore_edstate(new, old)
+ struct edstate *old, *new;
+ {
+- strncpy(new->cbuf, old->cbuf, old->linelen);
++ memcpy(new->cbuf, old->cbuf, old->linelen);
+ new->linelen = old->linelen;
+ new->cursor = old->cursor;
+ new->winleft = old->winleft;
+@@ -1470,6 +1480,19 @@
+ holdlen = 0;
+ }
+
++#ifdef DEBIAN /* patch from OpenBSD */
++/*
++ * this is used for calling x_escape() in complete_word()
++ */
++static int
++x_vi_putbuf(s, len)
++ const char *s;
++ size_t len;
++{
++ return putbuf(s, len, 0);
++}
++
++#endif /* DEBIAN */
+ static int
+ putbuf(buf, len, repl)
+ const char *buf;
+@@ -1965,7 +1988,11 @@
+ del_range(start, end);
+ es->cursor = start;
+ for (i = 0; i < nwords; ) {
++#ifndef DEBIAN
+ if (putbuf(words[i], (int) strlen(words[i]), 0) != 0) {
++#else /* DEBIAN */ /* patch from OpenBSD */
++ if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
++#endif /* DEBIAN */
+ rval = -1;
+ break;
+ }
+@@ -2068,9 +2095,18 @@
+ buf = save_edstate(es);
+ del_range(start, end);
+ es->cursor = start;
++#ifndef DEBIAN
+ if (putbuf(match, match_len, 0) != 0)
+ rval = -1;
+ else if (is_unique) {
++#else /* DEBIAN */ /* patch from OpenBSD */
++
++ /* escape all shell-sensitive characters and put the result into
++ * command buffer */
++ rval = x_escape(match, match_len, x_vi_putbuf);
++
++ if (rval == 0 && is_unique) {
++#endif /* DEBIAN */
+ /* If exact match, don't undo. Allows directory completions
+ * to be used (ie, complete the next portion of the path).
+ */
+Index: pdksh-5.2.14/c_sh.c
+===================================================================
+--- pdksh-5.2.14.orig/c_sh.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/c_sh.c 2009-09-17 00:32:08.000000000 +0200
+@@ -643,6 +643,7 @@
+ for (wp = l->argv; (*wp++ = *owp++) != NULL; )
+ ;
+ }
++#ifndef DEBIAN
+ /* POSIX says set exit status is 0, but old scripts that use
+ * getopt(1), use the construct: set -- `getopt ab:c "$@"`
+ * which assumes the exit value set will be that of the ``
+@@ -650,6 +651,12 @@
+ * if there are no command substitutions).
+ */
+ return Flag(FPOSIX) ? 0 : subst_exstat;
++#else
++ /* On Debian we always want set to return 0 like ksh93 does.
++ * See: Bug#118476.
++ */
++ return 0;
++#endif /* DEBIAN */
+ }
+
+ int
+@@ -844,7 +851,7 @@
+ * keeps them open).
+ */
+ #ifdef KSH
+- if (i > 2 && e->savefd[i])
++ if (!Flag(FSH) &&i > 2 && e->savefd[i])
+ fd_clexec(i);
+ #endif /* KSH */
+ }
+Index: pdksh-5.2.14/exec.c
+===================================================================
+--- pdksh-5.2.14.orig/exec.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/exec.c 2009-09-17 00:32:31.000000000 +0200
+@@ -228,8 +228,10 @@
+ e->savefd[1] = savefd(1, 0);
+
+ openpipe(pv);
+- ksh_dup2(pv[0], 0, FALSE);
+- close(pv[0]);
++ if (pv[0] != 0) {
++ ksh_dup2(pv[0], 0, FALSE);
++ close(pv[0]);
++ }
+ coproc.write = pv[1];
+ coproc.job = (void *) 0;
+
+@@ -448,18 +450,19 @@
+ int volatile flags;
+ {
+ int i;
+- int rv = 0;
++ volatile int rv = 0;
+ register char *cp;
+ register char **lastp;
+ static struct op texec; /* Must be static (XXX but why?) */
+ int type_flags;
+ int keepasn_ok;
+ int fcflags = FC_BI|FC_FUNC|FC_PATH;
++ int bourne_function_call = 0;
+
+ #ifdef KSH
+ /* snag the last argument for $_ XXX not the same as at&t ksh,
+ * which only seems to set $_ after a newline (but not in
+- * functions/dot scripts, but in interactive and scipt) -
++ * functions/dot scripts, but in interactive and script) -
+ * perhaps save last arg here and set it in shell()?.
+ */
+ if (Flag(FTALKING) && *(lastp = ap)) {
+@@ -544,9 +547,10 @@
+ newblock();
+ /* ksh functions don't keep assignments, POSIX functions do. */
+ if (keepasn_ok && tp && tp->type == CFUNC
+- && !(tp->flag & FKSH))
+- type_flags = 0;
+- else
++ && !(tp->flag & FKSH)) {
++ bourne_function_call = 1;
++ type_flags = 0;
++ } else
+ type_flags = LOCAL|LOCAL_COPY|EXPORT;
+ }
+ if (Flag(FEXPORT))
+@@ -563,6 +567,8 @@
+ shf_flush(shl_out);
+ }
+ typeset(cp, type_flags, 0, 0, 0);
++ if (bourne_function_call && !(type_flags & EXPORT))
++ typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0);
+ }
+
+ if ((cp = *ap) == NULL) {
+@@ -710,10 +716,12 @@
+ }
+
+ #ifdef KSH
+- /* set $_ to program's full path */
+- /* setstr() can't fail here */
+- setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0), tp->val.s,
+- KSH_RETURN_ERROR);
++ if (!Flag(FSH)) {
++ /* set $_ to program's full path */
++ /* setstr() can't fail here */
++ setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0), tp->val.s,
++ KSH_RETURN_ERROR);
++ }
+ #endif /* KSH */
+
+ if (flags&XEXEC) {
+@@ -1351,6 +1359,8 @@
+ snptreef((char *) 0, 32, "%R", &iotmp), emsg);
+ return -1;
+ }
++ if (u == iop->unit)
++ return 0; /* "dup from" == "dup to" */
+ break;
+ }
+ }
+@@ -1375,13 +1385,20 @@
+ return -1;
+ }
+ /* Do not save if it has already been redirected (i.e. "cat >x >y"). */
+- if (e->savefd[iop->unit] == 0)
+- /* c_exec() assumes e->savefd[fd] set for any redirections.
+- * Ask savefd() not to close iop->unit - allows error messages
+- * to be seen if iop->unit is 2; also means we can't lose
+- * the fd (eg, both dup2 below and dup2 in restfd() failing).
+- */
+- e->savefd[iop->unit] = savefd(iop->unit, 1);
++ if (e->savefd[iop->unit] == 0) {
++#ifdef DEBIAN /* patch from OpenBSD */
++ /* If these are the same, it means unit was previously closed */
++ if (u == iop->unit)
++ e->savefd[iop->unit] = -1;
++ else
++#endif
++ /* c_exec() assumes e->savefd[fd] set for any redirections.
++ * Ask savefd() not to close iop->unit - allows error messages
++ * to be seen if iop->unit is 2; also means we can't lose
++ * the fd (eg, both dup2 below and dup2 in restfd() failing).
++ */
++ e->savefd[iop->unit] = savefd(iop->unit, 1);
++ }
+
+ if (do_close)
+ close(iop->unit);
+Index: pdksh-5.2.14/history.c
+===================================================================
+--- pdksh-5.2.14.orig/history.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/history.c 2009-09-17 00:32:08.000000000 +0200
+@@ -858,8 +858,8 @@
+ /*
+ * check on its validity
+ */
+- if ((int)base == -1 || *base != HMAGIC1 || base[1] != HMAGIC2) {
+- if ((int)base != -1)
++ if (base == MAP_FAILED || *base != HMAGIC1 || base[1] != HMAGIC2) {
++ if (base != MAP_FAILED)
+ munmap((caddr_t)base, hsize);
+ hist_finish();
+ unlink(hname);
+@@ -893,7 +893,7 @@
+ static int
+ hist_count_lines(base, bytes)
+ register unsigned char *base;
+- register int bytes;
++ int bytes;
+ {
+ State state = shdr;
+ register lines = 0;
+@@ -1015,8 +1015,8 @@
+ register int bytes;
+ {
+ State state;
+- int lno;
+- unsigned char *line;
++ int lno = -1;
++ unsigned char *line = NULL;
+
+ for (state = shdr; bytes-- > 0; base++) {
+ switch (state) {
+@@ -1105,7 +1105,7 @@
+ /* someone has added some lines */
+ bytes = sizenow - hsize;
+ base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0);
+- if ((int)base == -1)
++ if (base == MAP_FAILED)
+ goto bad;
+ new = base + hsize;
+ if (*new != COMMAND) {
+Index: pdksh-5.2.14/jobs.c
+===================================================================
+--- pdksh-5.2.14.orig/jobs.c 2009-09-17 00:32:06.000000000 +0200
++++ pdksh-5.2.14/jobs.c 2009-09-17 00:32:08.000000000 +0200
+@@ -627,8 +627,10 @@
+ SS_RESTORE_IGN|SS_FORCE);
+ if (!(flags & (XPIPEI | XCOPROC))) {
+ int fd = open("/dev/null", 0);
+- (void) ksh_dup2(fd, 0, TRUE);
+- close(fd);
++ if (fd != 0) {
++ (void) ksh_dup2(fd, 0, TRUE);
++ close(fd);
++ }
+ }
+ }
+ remove_job(j, "child"); /* in case of `jobs` command */
+@@ -811,7 +813,7 @@
+ int sig;
+ {
+ Job *j;
+- Proc *p;
++/* Proc *p; */ /* unused */
+ int rv = 0;
+ int ecode;
+ #ifdef JOB_SIGS
+Index: pdksh-5.2.14/lex.c
+===================================================================
+--- pdksh-5.2.14.orig/lex.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/lex.c 2009-09-17 00:32:08.000000000 +0200
+@@ -699,11 +699,13 @@
+
+ case '(': /*)*/
+ #ifdef KSH
+- if ((c2 = getsc()) == '(') /*)*/
+- /* XXX need to handle ((...); (...)) */
+- c = MDPAREN;
+- else
+- ungetsc(c2);
++ if (!Flag(FSH)) {
++ if ((c2 = getsc()) == '(') /*)*/
++ /* XXX need to handle ((...); (...)) */
++ c = MDPAREN;
++ else
++ ungetsc(c2);
++ }
+ #endif /* KSH */
+ return c;
+ /*(*/
+@@ -1119,7 +1121,7 @@
+ */
+ {
+ struct shf *shf;
+- char *ps1;
++ char * volatile ps1;
+ Area *saved_atemp;
+
+ ps1 = str_val(global("PS1"));
+Index: pdksh-5.2.14/main.c
+===================================================================
+--- pdksh-5.2.14.orig/main.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/main.c 2009-09-17 00:32:08.000000000 +0200
+@@ -201,7 +201,24 @@
+ change_flag(FPOSIX, OF_SPECIAL, 1);
+ #endif /* POSIXLY_CORRECT */
+
+- /* import enviroment */
++#ifdef DEBIAN /* patch from OpenBSD */
++ /* Check to see if we're /bin/sh. */
++ if (!strcmp(&kshname[strlen(kshname) - 3], "/sh")
++ || !strcmp(kshname, "sh") || !strcmp(kshname, "-sh"))
++ Flag(FSH) = 1;
++
++ /* 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. */
++#if defined(EDIT) && defined(EMACS)
++ change_flag(FEMACS, OF_SPECIAL, 1);
++#endif /* EDIT && EMACS */
++ #if defined(EDIT) && defined(VI)
++ Flag(FVITABCOMPLETE) = 1;
++#endif /* EDIT && VI */
++#endif /* DEBIAN */
++
++ /* import environment */
+ if (environ != NULL)
+ for (wp = environ; *wp != NULL; wp++)
+ typeset(*wp, IMPORT|EXPORT, 0, 0, 0);
+Index: pdksh-5.2.14/misc.c
+===================================================================
+--- pdksh-5.2.14.orig/misc.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/misc.c 2009-09-17 00:32:08.000000000 +0200
+@@ -159,6 +159,7 @@
+ { "posix", 0, OF_ANY }, /* non-standard */
+ { "privileged", 'p', OF_ANY },
+ { "restricted", 'r', OF_CMDLINE },
++ { "sh", 0, OF_ANY }, /* non-standard */ /* from OpenBSD */
+ { "stdin", 's', OF_CMDLINE }, /* pseudo non-standard */
+ { "trackall", 'h', OF_ANY },
+ { "verbose", 'v', OF_ANY },
+@@ -309,8 +310,15 @@
+ #ifdef OS2
+ ;
+ #else /* OS2 */
++#ifndef DEBIAN
+ setuid(ksheuid = getuid());
+ setgid(getgid());
++#else /* patch from OpenBSD */
++ seteuid(ksheuid = getuid());
++ setuid(ksheuid);
++ setegid(getgid());
++ setgid(getgid());
++#endif /* DEBIAN */
+ #endif /* OS2 */
+ } else if (f == FPOSIX && newval) {
+ #ifdef BRACE_EXPAND
+@@ -471,6 +479,7 @@
+ const char *as;
+ int *ai;
+ {
++#ifndef DEBIAN
+ const char *s;
+ register int n;
+ int sawdigit = 0;
+@@ -484,6 +493,19 @@
+ if (*s || !sawdigit)
+ return 0;
+ return 1;
++#else /* patch from OpenBSD */
++ char *p;
++ long n;
++
++ n = strtol(as, &p, 10);
++
++ if (!*as || *p || INT_MIN >= n || n >= INT_MAX)
++ return 0;
++
++ *ai = (int)n;
++ return 1;
++#endif
++
+ }
+
+ /* getn() that prints error */
+Index: pdksh-5.2.14/sh.h
+===================================================================
+--- pdksh-5.2.14.orig/sh.h 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/sh.h 2009-09-17 00:32:08.000000000 +0200
+@@ -382,7 +382,11 @@
+ */
+
+ typedef struct Area {
++#ifndef DEBIAN
+ struct Block *freelist; /* free list */
++#else /* patch from OpenBSD */
++ struct link *freelist; /* free list */
++#endif
+ } Area;
+
+ EXTERN Area aperm; /* permanent object space */
+@@ -501,6 +505,7 @@
+ FPOSIX, /* -o posix: be posixly correct */
+ FPRIVILEGED, /* -p: use suid_profile */
+ FRESTRICTED, /* -r: restricted shell */
++ FSH, /* -o sh: favor sh behavour */
+ FSTDIN, /* -s: (invocation) parse stdin */
+ FTRACKALL, /* -h: create tracked aliases for all commands */
+ FVERBOSE, /* -v: echo input */
+Index: pdksh-5.2.14/tests/th
+===================================================================
+--- pdksh-5.2.14.orig/tests/th 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/tests/th 2009-09-17 00:32:08.000000000 +0200
+@@ -1,4 +1,4 @@
+-#!/usr/local/bin/perl
++#!/usr/bin/perl
+
+ #
+ # Test harness for pdksh tests.
+@@ -131,7 +131,7 @@
+ $os = defined $^O ? $^O : 'unknown';
+
+ require 'signal.ph' unless $os eq 'os2';
+-require 'errno.ph' unless $os eq 'os2';
++# require 'errno.ph' unless $os eq 'os2';
+ require 'getopts.pl';
+
+ ($prog = $0) =~ s#.*/##;
+Index: pdksh-5.2.14/trap.c
+===================================================================
+--- pdksh-5.2.14.orig/trap.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/trap.c 2009-09-17 00:32:08.000000000 +0200
+@@ -68,6 +68,8 @@
+ alarm_catcher(sig)
+ int sig;
+ {
++ int errno_ = errno;
++
+ if (ksh_tmout_state == TMOUT_READING) {
+ int left = alarm(0);
+
+@@ -77,6 +79,7 @@
+ } else
+ alarm(left);
+ }
++ errno = errno_;
+ return RETSIGVAL;
+ }
+ #endif /* KSH */
+@@ -111,6 +114,7 @@
+ int i;
+ {
+ Trap *p = &sigtraps[i];
++ int errno_ = errno;
+
+ trap = p->set = 1;
+ if (p->flags & TF_DFL_INTR)
+@@ -125,6 +129,7 @@
+ if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */
+ sigaction(i, &Sigact_trap, (struct sigaction *) 0);
+ #endif /* V7_SIGNALS */
++ errno = errno_;
+ return RETSIGVAL;
+ }
+
+Index: pdksh-5.2.14/tree.c
+===================================================================
+--- pdksh-5.2.14.orig/tree.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/tree.c 2009-09-17 00:32:08.000000000 +0200
+@@ -506,7 +506,7 @@
+ for (tw = t->vars; *tw++ != NULL; )
+ ;
+ rw = r->vars = (char **)
+- alloc((int)(tw - t->vars) * sizeof(*tw), ap);
++ alloc((tw - t->vars + 1) * sizeof(*tw), ap);
+ for (tw = t->vars; *tw != NULL; )
+ *rw++ = wdcopy(*tw++, ap);
+ *rw = NULL;
+@@ -518,7 +518,7 @@
+ for (tw = t->args; *tw++ != NULL; )
+ ;
+ rw = r->args = (char **)
+- alloc((int)(tw - t->args) * sizeof(*tw), ap);
++ alloc((tw - t->args + 1) * sizeof(*tw), ap);
+ for (tw = t->args; *tw != NULL; )
+ *rw++ = wdcopy(*tw++, ap);
+ *rw = NULL;
+@@ -679,7 +679,7 @@
+
+ for (ior = iow; *ior++ != NULL; )
+ ;
+- ior = (struct ioword **) alloc((int)(ior - iow) * sizeof(*ior), ap);
++ ior = (struct ioword **) alloc((ior - iow + 1) * sizeof(*ior), ap);
+
+ for (i = 0; iow[i] != NULL; i++) {
+ register struct ioword *p, *q;
+Index: pdksh-5.2.14/c_test.c
+===================================================================
+--- pdksh-5.2.14.orig/c_test.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/c_test.c 2009-09-17 00:32:08.000000000 +0200
+@@ -124,10 +124,10 @@
+ te.pos.wp = wp + 1;
+ 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 ommited steps.
++ * Implementation of all the rules isn't necessary since
++ * our parser does the right thing for the omitted steps.
+ */
+ if (argc <= 5) {
+ char **owp = wp;
+@@ -238,7 +238,7 @@
+ if (not)
+ res = !res;
+ }
+- return res;
++ return res;
+ case TO_FILRD: /* -r */
+ return test_eaccess(opnd1, R_OK) == 0;
+ case TO_FILWR: /* -w */
+@@ -456,10 +456,12 @@
+ }
+ #endif /* !HAVE_DEV_FD */
+
+- /* On most (all?) unixes, access() says everything is executable for
++ res = eaccess(path, mode);
++ /*
++ * On most (all?) unixes, access() says everything is executable for
+ * root - avoid this on files by using stat().
+ */
+- if ((mode & X_OK) && ksheuid == 0) {
++ if (res == 0 && ksheuid == 0 && (mode & X_OK)) {
+ struct stat statb;
+
+ if (stat(path, &statb) < 0)
+@@ -469,13 +471,7 @@
+ else
+ res = (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
+ ? 0 : -1;
+- /* Need to check other permissions? If so, use access() as
+- * this will deal with root on NFS.
+- */
+- if (res == 0 && (mode & (R_OK|W_OK)))
+- res = eaccess(path, mode);
+- } else
+- res = eaccess(path, mode);
++ }
+
+ return res;
+ }
+Index: pdksh-5.2.14/eval.c
+===================================================================
+--- pdksh-5.2.14.orig/eval.c 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/eval.c 2009-09-17 00:32:08.000000000 +0200
+@@ -870,8 +870,11 @@
+ openpipe(pv);
+ shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0);
+ ofd1 = savefd(1, 0); /* fd 1 may be closed... */
+- ksh_dup2(pv[1], 1, FALSE);
+- close(pv[1]);
++ if (pv[1] != 1) {
++ ksh_dup2(pv[1], 1, FALSE);
++ close(pv[1]);
++ }
++
+ execute(t, XFORK|XXCOM|XPIPEO);
+ restfd(1, ofd1);
+ startlast();
+Index: pdksh-5.2.14/tests/regress.t
+===================================================================
+--- pdksh-5.2.14.orig/tests/regress.t 2009-09-17 00:31:58.000000000 +0200
++++ pdksh-5.2.14/tests/regress.t 2009-09-17 00:32:08.000000000 +0200
+@@ -156,11 +156,12 @@
+ echo $?
+ shoud not print 0. (according to /bin/sh, at&t ksh88, and the
+ getopt(1) man page - not according to POSIX)
++ For Debian - it should print 0, cf. bug#118476.
+ stdin:
+ set -- `false`
+ echo $?
+ expected-stdout:
+- 1
++ 0
+ ---
+
+