* 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 , 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. 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 /* needed for */ # include /* needed for struct winsize */ #endif /* OS_SCO */ +#ifdef DEBIAN +#include +#endif /* DEBIAN */ #include #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 ---