loksh-noxz

[discontinued] a Linux port of OpenBSD's ksh
git clone https://noxz.tech/git/loksh-noxz.git
loksh-noxz

commit: 5f43fd7f991c08d7b694579b643bc7423c75b957
parent: 116a1847123df86241856d5e744c0afeb35a2f78
author: Chris Noxz <chris@noxz.tech>
date:   Tue, 9 Mar 2021 19:25:38 +0100
Match upstream 6.8
Mc_sh.c8++-
Memacs.c12++--
Meval.c65++++++++++++-----
Mjobs.c30+++++++-
Mksh.123++++--
Mmisc.c6+-
Msh.h3+-
Mvi.c76+++++++++++++-------
8 files changed, 166 insertions(+), 57 deletions(-)
diff --git a/c_sh.c b/c_sh.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: c_sh.c,v 1.63 2018/04/09 17:53:36 tobias Exp $	*/
+/*	$OpenBSD: c_sh.c,v 1.64 2020/05/22 07:50:07 benno Exp $	*/
 
 /*
  * built-in Bourne commands
@@ -425,6 +425,8 @@ int
 c_eval(char **wp)
 {
 	struct source *s;
+	struct source *saves = source;
+	int savef;
 	int rv;
 
 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
@@ -459,7 +461,11 @@ c_eval(char **wp)
 		exstat = subst_exstat;
 	}
 
+	savef = Flag(FERREXIT);
+	Flag(FERREXIT) = 0;
 	rv = shell(s, false);
+	Flag(FERREXIT) = savef;
+	source = saves;
 	afree(s, ATEMP);
 	return (rv);
 }
diff --git a/emacs.c b/emacs.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: emacs.c,v 1.86 2019/04/03 14:55:12 jca Exp $	*/
+/*	$OpenBSD: emacs.c,v 1.87 2020/05/08 14:30:42 jca Exp $	*/
 
 /*
  *  Emacs-like command line editing and history
@@ -41,8 +41,10 @@ static	Area	aedit;
 #define	KEOL	1		/* ^M, ^J */
 #define	KINTR	2		/* ^G, ^C */
 
+typedef int (*kb_func)(int);
+
 struct	x_ftab {
-	int		(*xf_func)(int c);
+	kb_func		xf_func;
 	const char	*xf_name;
 	short		xf_flags;
 };
@@ -861,7 +863,7 @@ x_eot_del(int c)
 		return (x_del_char(c));
 }
 
-static void *
+static kb_func
 kb_find_hist_func(char c)
 {
 	struct kb_entry		*k;
@@ -1315,7 +1317,7 @@ kb_del(struct kb_entry *k)
 }
 
 static struct kb_entry *
-kb_add_string(void *func, void *args, char *str)
+kb_add_string(kb_func func, void *args, char *str)
 {
 	unsigned int		ele, count;
 	struct kb_entry		*k;
@@ -1350,7 +1352,7 @@ kb_add_string(void *func, void *args, char *str)
 }
 
 static struct kb_entry *
-kb_add(void *func, ...)
+kb_add(kb_func func, ...)
 {
 	va_list			ap;
 	unsigned char		ch;
diff --git a/eval.c b/eval.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: eval.c,v 1.65 2019/06/28 13:34:59 deraadt Exp $	*/
+/*	$OpenBSD: eval.c,v 1.66 2020/09/13 15:39:09 tb Exp $	*/
 
 /*
  * Expansion - quoting, separation, substitution, globbing
@@ -47,6 +47,8 @@ typedef struct Expand {
 #define IFS_WORD	0	/* word has chars (or quotes) */
 #define IFS_WS		1	/* have seen IFS white-space */
 #define IFS_NWS		2	/* have seen IFS non-white-space */
+#define IFS_IWS		3	/* beginning of word, ignore IFS white-space */
+#define IFS_QUOTE	4	/* beg.w/quote, becomes IFS_WORD unless "$@" */
 
 static	int	varsub(Expand *, char *, char *, int *, int *);
 static	int	comsub(Expand *, char *);
@@ -217,7 +219,17 @@ expand(char *cp,	/* input word */
 				c = *sp++;
 				break;
 			case OQUOTE:
-				word = IFS_WORD;
+				switch (word) {
+				case IFS_QUOTE:
+					/* """something */
+					word = IFS_WORD;
+					break;
+				case IFS_WORD:
+					break;
+				default:
+					word = IFS_QUOTE;
+					break;
+				}
 				tilde_ok = 0;
 				quote = 1;
 				continue;
@@ -297,6 +309,8 @@ expand(char *cp,	/* input word */
 				if (f&DOBLANK)
 					doblank++;
 				tilde_ok = 0;
+				if (word == IFS_QUOTE && type != XNULLSUB)
+					word = IFS_WORD;
 				if (type == XBASE) {	/* expand? */
 					if (!st->next) {
 						SubType *newst;
@@ -358,6 +372,11 @@ expand(char *cp,	/* input word */
 						f |= DOTEMP_;
 						/* FALLTHROUGH */
 					default:
+						/* '-' '+' '?' */
+						if (quote)
+							word = IFS_WORD;
+						else if (dp == Xstring(ds, dp))
+							word = IFS_IWS;
 						/* Enable tilde expansion */
 						tilde_ok = 1;
 						f |= DOTILDE;
@@ -387,10 +406,17 @@ expand(char *cp,	/* input word */
 					 */
 					x.str = trimsub(str_val(st->var),
 						dp, st->stype);
-					if (x.str[0] != '\0' || st->quote)
+					if (x.str[0] != '\0') {
+						word = IFS_IWS;
 						type = XSUB;
-					else
+					} else if (quote) {
+						word = IFS_WORD;
+						type = XSUB;
+					} else {
+						if (dp == Xstring(ds, dp))
+							word = IFS_IWS;
 						type = XNULLSUB;
+					}
 					if (f&DOBLANK)
 						doblank++;
 					st = st->prev;
@@ -422,6 +448,10 @@ expand(char *cp,	/* input word */
 					if (f&DOBLANK)
 						doblank++;
 					st = st->prev;
+					if (quote || !*x.str)
+						word = IFS_WORD;
+					else
+						word = IFS_IWS;
 					continue;
 				case '?':
 				    {
@@ -463,12 +493,8 @@ expand(char *cp,	/* input word */
 			type = XBASE;
 			if (f&DOBLANK) {
 				doblank--;
-				/* not really correct: x=; "$x$@" should
-				 * generate a null argument and
-				 * set A; "${@:+}" shouldn't.
-				 */
-				if (dp == Xstring(ds, dp))
-					word = IFS_WS;
+				if (dp == Xstring(ds, dp) && word != IFS_WORD)
+					word = IFS_IWS;
 			}
 			continue;
 
@@ -503,7 +529,12 @@ expand(char *cp,	/* input word */
 				if (c == 0) {
 					if (quote && !x.split)
 						continue;
+					if (!quote && word == IFS_WS)
+						continue;
+					/* this is so we don't terminate */
 					c = ' ';
+					/* now force-emit a word */
+					goto emit_word;
 				}
 				if (quote && x.split) {
 					/* terminate word for "$@" */
@@ -554,15 +585,15 @@ expand(char *cp,	/* input word */
 			 *	-----------------------------------
 			 *	IFS_WORD	w/WS	w/NWS	w
 			 *	IFS_WS		-/WS	w/NWS	-
-			 *	IFS_NWS		-/NWS	w/NWS	w
+			 *	IFS_NWS		-/NWS	w/NWS	-
+			 *	IFS_IWS		-/WS	w/NWS	-
 			 *   (w means generate a word)
-			 * Note that IFS_NWS/0 generates a word (at&t ksh
-			 * doesn't do this, but POSIX does).
 			 */
-			if (word == IFS_WORD ||
-			    (!ctype(c, C_IFSWS) && c && word == IFS_NWS)) {
-				char *p;
-
+			if ((word == IFS_WORD) || (word == IFS_QUOTE) || (c &&
+			    (word == IFS_IWS || word == IFS_NWS) &&
+			    !ctype(c, C_IFSWS))) {
+ 				char *p;
+ emit_word:
 				*dp++ = '\0';
 				p = Xclose(ds, dp);
 				if (fdo & DOBRACE_)
diff --git a/jobs.c b/jobs.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: jobs.c,v 1.61 2019/06/28 13:34:59 deraadt Exp $	*/
+/*	$OpenBSD: jobs.c,v 1.62 2020/07/07 10:33:58 jca Exp $	*/
 
 /*
  * Process and job control
@@ -71,6 +71,7 @@ struct proc {
 #define JF_REMOVE	0x200	/* flagged for removal (j_jobs()/j_notify()) */
 #define JF_USETTYMODE	0x400	/* tty mode saved if process exits normally */
 #define JF_SAVEDTTYPGRP	0x800	/* j->saved_ttypgrp is valid */
+#define JF_PIPEFAIL	0x1000	/* pipefail on when job was started */
 
 typedef struct job Job;
 struct job {
@@ -426,6 +427,8 @@ exchild(struct op *t, int flags, volatile int *xerrok,
 		 */
 		j->flags = (flags & XXCOM) ? JF_XXCOM :
 		    ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));
+		if (Flag(FPIPEFAIL))
+			j->flags |= JF_PIPEFAIL;
 		timerclear(&j->usrtime);
 		timerclear(&j->systime);
 		j->state = PRUNNING;
@@ -1089,7 +1092,30 @@ j_waitj(Job *j,
 
 	j_usrtime = j->usrtime;
 	j_systime = j->systime;
-	rv = j->status;
+
+	if (j->flags & JF_PIPEFAIL) {
+		Proc *p;
+		int status;
+
+		rv = 0;
+		for (p = j->proc_list; p != NULL; p = p->next) {
+			switch (p->state) {
+			case PEXITED:
+				status = WEXITSTATUS(p->status);
+				break;
+			case PSIGNALLED:
+				status = 128 + WTERMSIG(p->status);
+				break;
+			default:
+				status = 0;
+				break;
+			}
+			if (status)
+				rv = status;
+		}
+	} else
+		rv = j->status;
+
 
 	if (!(flags & JW_ASYNCNOTIFY) &&
 	    (!Flag(FMONITOR) || j->state != PSTOPPED)) {
diff --git a/ksh.1 b/ksh.1
@@ -1,8 +1,8 @@
-.\"	$OpenBSD: ksh.1,v 1.208 2019/11/26 22:49:01 jmc Exp $
+.\"	$OpenBSD: ksh.1,v 1.210 2020/09/20 14:40:45 millert Exp $
 .\"
 .\"	Public Domain
 .\"
-.Dd $Mdocdate: November 26 2019 $
+.Dd $Mdocdate: September 20 2020 $
 .Dt KSH 1
 .Os
 .Sh NAME
@@ -361,7 +361,9 @@ token to form pipelines, in which the standard output of each command but the
 last is piped (see
 .Xr pipe 2 )
 to the standard input of the following command.
-The exit status of a pipeline is that of its last command.
+The exit status of a pipeline is that of its last command, unless the
+.Ic pipefail
+option is set.
 A pipeline may be prefixed by the
 .Ql \&!
 reserved word, which causes the exit status of the pipeline to be logically
@@ -3664,6 +3666,10 @@ See the
 and
 .Ic pwd
 commands above for more details.
+.It Ic pipefail
+The exit status of a pipeline is the exit status of the rightmost
+command in the pipeline that doesn't return 0, or 0 if all commands
+returned a 0 exit status.
 .It Ic posix
 Enable POSIX mode.
 See
@@ -5047,6 +5053,13 @@ Erases previous character.
 .It ^J | ^M
 End of line.
 The current line is read, parsed, and executed by the shell.
+.It ^L
+Clear the screen (if possible) and redraw the current line.
+See the
+.Em clear-screen
+command in
+.Sx Emacs editing mode
+for more information.
 .It ^V
 Literal next.
 The next character typed is not treated specially (can be used
@@ -5504,7 +5517,9 @@ Miscellaneous vi commands
 .Bl -tag -width Ds
 .It ^J and ^M
 The current line is read, parsed, and executed by the shell.
-.It ^L and ^R
+.It ^L
+Clear the screen (if possible) and redraw the current line.
+.It ^R
 Redraw the current line.
 .It Xo
 .Oo Ar n Oc Ns \&.
diff --git a/misc.c b/misc.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: misc.c,v 1.73 2019/06/28 13:34:59 deraadt Exp $	*/
+/*	$OpenBSD: misc.c,v 1.75 2020/07/22 19:20:41 millert Exp $	*/
 
 /*
  * Miscellaneous functions
@@ -149,6 +149,7 @@ const struct option sh_options[] = {
 	{ "notify",	'b',		OF_ANY },
 	{ "nounset",	'u',		OF_ANY },
 	{ "physical",	  0,		OF_ANY }, /* non-standard */
+	{ "pipefail",	  0,		OF_ANY }, /* non-standard */
 	{ "posix",	  0,		OF_ANY }, /* non-standard */
 	{ "privileged",	'p',		OF_ANY },
 	{ "restricted",	'r',	    OF_CMDLINE },
@@ -613,6 +614,9 @@ do_gmatch(const unsigned char *s, const unsigned char *se,
 			break;
 
 		case '*':
+			/* collapse consecutive stars */
+			while (ISMAGIC(p[0]) && p[1] == '*')
+				p += 2;
 			if (p == pe)
 				return 1;
 			s--;
diff --git a/sh.h b/sh.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: sh.h,v 1.75 2019/02/20 23:59:17 schwarze Exp $	*/
+/*	$OpenBSD: sh.h,v 1.76 2020/07/07 10:33:58 jca Exp $	*/
 
 /*
  * Public Domain Bourne/Korn shell
@@ -160,6 +160,7 @@ enum sh_flag {
 	FNOTIFY,	/* -b: asynchronous job completion notification */
 	FNOUNSET,	/* -u: using an unset var is an error */
 	FPHYSICAL,	/* -o physical: don't do logical cd's/pwd's */
+	FPIPEFAIL,	/* -o pipefail: all commands in pipeline can affect $? */
 	FPOSIX,		/* -o posix: be posixly correct */
 	FPRIVILEGED,	/* -p: use suid_profile */
 	FRESTRICTED,	/* -r: restricted shell */
diff --git a/vi.c b/vi.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: vi.c,v 1.56 2018/03/15 16:51:29 anton Exp $	*/
+/*	$OpenBSD: vi.c,v 1.57 2020/09/20 14:40:45 millert Exp $	*/
 
 /*
  *	vi command editing
@@ -14,12 +14,14 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
+#ifndef SMALL
+# include <term.h>
+# include <curses.h>
+#endif
 
 #include "sh.h"
 #include "edit.h"
 
-#define CTRL(c)		(c & 0x1f)
-
 struct edstate {
 	char	*cbuf;		/* main buffer to build the command line */
 	int	cbufsize;	/* number of bytes allocated for cbuf */
@@ -56,8 +58,9 @@ static int	Backword(int);
 static int	Endword(int);
 static int	grabhist(int, int);
 static int	grabsearch(int, int, int, char *);
+static void	do_clear_screen(void);
 static void	redraw_line(int);
-static void	refresh(int);
+static void	refresh_line(int);
 static int	outofwin(void);
 static void	rewindow(void);
 static int	newcol(int, int);
@@ -275,9 +278,9 @@ vi_hook(int ch)
 			case 0:
 				if (state == VLIT) {
 					es->cursor--;
-					refresh(0);
+					refresh_line(0);
 				} else
-					refresh(insert != 0);
+					refresh_line(insert != 0);
 				break;
 			case 1:
 				return 1;
@@ -302,7 +305,7 @@ vi_hook(int ch)
 							return -1;
 					} else if (putbuf("?", 1, 0) != 0)
 						return -1;
-					refresh(0);
+					refresh_line(0);
 				}
 			}
 		}
@@ -314,7 +317,7 @@ vi_hook(int ch)
 			vi_error();
 		} else
 			es->cbuf[es->cursor++] = ch;
-		refresh(1);
+		refresh_line(1);
 		state = VNORMAL;
 		break;
 
@@ -379,7 +382,7 @@ vi_hook(int ch)
 				if (!srchpat[0]) {
 					vi_error();
 					state = VNORMAL;
-					refresh(0);
+					refresh_line(0);
 					return 0;
 				}
 			} else {
@@ -396,17 +399,17 @@ vi_hook(int ch)
 				} while (srchlen > 0 &&
 				    isu8cont(locpat[srchlen]));
 				es->cursor = es->linelen;
-				refresh(0);
+				refresh_line(0);
 				return 0;
 			}
 			restore_cbuf();
 			state = VNORMAL;
-			refresh(0);
+			refresh_line(0);
 		} else if (ch == edchars.kill) {
 			srchlen = 0;
 			es->linelen = 1;
 			es->cursor = 1;
-			refresh(0);
+			refresh_line(0);
 			return 0;
 		} else if (ch == edchars.werase) {
 			struct edstate new_es, *save_es;
@@ -425,7 +428,7 @@ vi_hook(int ch)
 				es->linelen -= char_len((unsigned char)locpat[i]);
 			srchlen = n;
 			es->cursor = es->linelen;
-			refresh(0);
+			refresh_line(0);
 			return 0;
 		} else {
 			if (srchlen == SRCHLEN - 1)
@@ -450,7 +453,7 @@ vi_hook(int ch)
 					es->cbuf[es->linelen++] = ch;
 				}
 				es->cursor = es->linelen;
-				refresh(0);
+				refresh_line(0);
 			}
 			return 0;
 		}
@@ -463,15 +466,15 @@ vi_hook(int ch)
 		switch (vi_cmd(argc1, curcmd)) {
 		case -1:
 			vi_error();
-			refresh(0);
+			refresh_line(0);
 			break;
 		case 0:
 			if (insert != 0)
 				inslen = 0;
-			refresh(insert != 0);
+			refresh_line(insert != 0);
 			break;
 		case 1:
-			refresh(0);
+			refresh_line(0);
 			return 1;
 		case 2:
 			/* back from a 'v' command - don't redraw the screen */
@@ -486,7 +489,7 @@ vi_hook(int ch)
 		switch (vi_cmd(lastac, lastcmd)) {
 		case -1:
 			vi_error();
-			refresh(0);
+			refresh_line(0);
 			break;
 		case 0:
 			if (insert != 0) {
@@ -499,10 +502,10 @@ vi_hook(int ch)
 						vi_error();
 				}
 			}
-			refresh(0);
+			refresh_line(0);
 			break;
 		case 1:
-			refresh(0);
+			refresh_line(0);
 			return 1;
 		case 2:
 			/* back from a 'v' command - can't happen */
@@ -655,6 +658,10 @@ vi_insert(int ch)
 		print_expansions(es);
 		break;
 
+	case CTRL('l'):
+		do_clear_screen();
+		break;
+
 	case CTRL('i'):
 		if (Flag(FVITABCOMPLETE)) {
 			complete_word(0, 0);
@@ -712,6 +719,9 @@ vi_cmd(int argcnt, const char *cmd)
 		switch (*cmd) {
 
 		case CTRL('l'):
+			do_clear_screen();
+			break;
+
 		case CTRL('r'):
 			redraw_line(1);
 			break;
@@ -1032,7 +1042,7 @@ vi_cmd(int argcnt, const char *cmd)
 			    c1, srchpat)) < 0) {
 				if (c3) {
 					restore_cbuf();
-					refresh(0);
+					refresh_line(0);
 				}
 				return -1;
 			} else {
@@ -1721,10 +1731,24 @@ grabsearch(int save, int start, int fwd, char *pat)
 }
 
 static void
-redraw_line(int newline)
+do_clear_screen(void)
+{
+	int neednl = 1;
+
+#ifndef SMALL
+	if (cur_term != NULL && clear_screen != NULL) {
+		if (tputs(clear_screen, 1, x_putc) != ERR)
+			neednl = 0;
+	}
+#endif
+	redraw_line(neednl);
+}
+
+static void
+redraw_line(int neednl)
 {
 	(void) memset(wbuf[win], ' ', wbuf_len);
-	if (newline) {
+	if (neednl) {
 		x_putc('\r');
 		x_putc('\n');
 	}
@@ -1734,7 +1758,7 @@ redraw_line(int newline)
 }
 
 static void
-refresh(int leftside)
+refresh_line(int leftside)
 {
 	if (outofwin())
 		rewindow();
@@ -2037,7 +2061,7 @@ expand_word(int command)
 	modified = 1; hnum = hlast;
 	set_insert(INSERT);
 	lastac = 0;
-	refresh(0);
+	refresh_line(0);
 	return rval;
 }
 
@@ -2141,7 +2165,7 @@ complete_word(int command, int count)
 	modified = 1; hnum = hlast;
 	set_insert(INSERT);
 	lastac = 0;	 /* prevent this from being redone... */
-	refresh(0);
+	refresh_line(0);
 
 	return rval;
 }