oksh-noxz

[fork] Portable OpenBSD ksh, based on the Public Domain Korn Shell (pdksh).
git clone https://noxz.tech/git/oksh-noxz.git
oksh-noxz

commit: bbbcad24eea02b8fc58c93bc732d3cc207af4116
parent: af6237e8bd9ae40d6b47b6d4536f0868cc19cf6c
author: Brian Callahan <bcallah@openbsd.org>
date:   Sun, 23 Sep 2018 14:31:27 -0400
We're approaching OpenBSD 6.4 now; this is close to what will be
released.
Note now that oksh will use ncurses if it finds it during the
configure stage. If you don't want ncurses support (used for screen
clearing), pass the --disable-curses flag to configure.
MCVS/Entries80++++++++---------
ACVS/Entries.Log2+
Mconfigure93+++++++++++++++++++-
Medit.c6+-
Medit.h4+-
Memacs.c45++++++++--
Meval.c39+++++++-
Mksh.113++-
Mmail.c17++--
Mtable.h3+-
Mvar.c30++++++-
11 files changed, 265 insertions(+), 67 deletions(-)
diff --git a/CVS/Entries b/CVS/Entries
@@ -1,46 +1,46 @@
 /config.h/1.19/Tue Jan 16 02:21:56 2018//
 /history.c/1.80/Tue Jan 16 02:21:56 2018//
-/emacs.c/1.84/Result of merge//
 /main.c/1.92/Result of merge//
-/CONTRIBUTORS/1.10/Fri Jun  1 19:10:08 2018//
-/LEGAL/1.2/Fri Jun  1 19:10:08 2018//
-/Makefile/1.38/Fri Jun  1 19:10:21 2018//
-/NOTES/1.16/Fri Jun  1 19:10:08 2018//
-/PROJECTS/1.9/Fri Jun  1 19:10:08 2018//
-/README/1.16/Fri Jun  1 19:10:21 2018//
-/alloc.c/1.19/Fri Jun  1 19:10:08 2018//
-/c_ksh.c/1.61/Fri Jun  1 19:10:08 2018//
-/c_sh.c/1.63/Fri Jun  1 19:10:08 2018//
-/c_test.c/1.25/Fri Jun  1 19:10:08 2018//
-/c_test.h/1.4/Fri Jun  1 19:10:08 2018//
-/c_ulimit.c/1.28/Fri Jun  1 19:10:08 2018//
-/edit.c/1.65/Fri Jun  1 19:10:08 2018//
-/edit.h/1.11/Fri Jun  1 19:10:08 2018//
-/eval.c/1.60/Fri Jun  1 19:10:08 2018//
-/exec.c/1.73/Fri Jun  1 19:10:08 2018//
-/expand.h/1.15/Fri Jun  1 19:10:08 2018//
-/expr.c/1.33/Fri Jun  1 19:10:08 2018//
 /io.c/1.36/Fri Jun  1 19:10:08 2018//
-/jobs.c/1.60/Fri Jun  1 19:10:08 2018//
-/ksh.1/1.200/Fri Jun  1 19:10:21 2018//
-/lex.c/1.78/Fri Jun  1 19:10:08 2018//
-/lex.h/1.21/Fri Jun  1 19:10:08 2018//
-/mail.c/1.23/Fri Jun  1 19:10:08 2018//
-/misc.c/1.70/Fri Jun  1 19:10:08 2018//
-/path.c/1.22/Fri Jun  1 19:10:08 2018//
-/sh.1/1.148/Fri Jun  1 19:10:08 2018//
-/sh.h/1.73/Fri Jun  1 19:10:08 2018//
-/shf.c/1.33/Fri Jun  1 19:10:08 2018//
-/shf.h/1.8/Fri Jun  1 19:10:08 2018//
-/syn.c/1.39/Fri Jun  1 19:10:08 2018//
-/table.c/1.25/Fri Jun  1 19:10:08 2018//
-/table.h/1.14/Fri Jun  1 19:10:08 2018//
-/trap.c/1.32/Fri Jun  1 19:10:08 2018//
-/tree.c/1.34/Fri Jun  1 19:10:08 2018//
-/tree.h/1.12/Fri Jun  1 19:10:08 2018//
 /tty.c/1.17/Fri Jun  1 19:10:08 2018//
-/tty.h/1.6/Fri Jun  1 19:10:08 2018//
-/var.c/1.68/Fri Jun  1 19:10:08 2018//
-/version.c/1.12/Fri Jun  1 19:10:08 2018//
-/vi.c/1.56/Fri Jun  1 19:10:08 2018//
+/CONTRIBUTORS/1.10/Sun Sep 23 17:58:57 2018//
+/LEGAL/1.2/Sun Sep 23 17:58:57 2018//
+/Makefile/1.39/Sun Sep 23 17:59:17 2018//
+/NOTES/1.16/Sun Sep 23 17:58:57 2018//
+/PROJECTS/1.9/Sun Sep 23 17:58:57 2018//
+/README/1.16/Sun Sep 23 17:59:17 2018//
+/alloc.c/1.19/Sun Sep 23 17:58:57 2018//
+/c_ksh.c/1.61/Sun Sep 23 17:58:57 2018//
+/c_sh.c/1.63/Sun Sep 23 17:58:57 2018//
+/c_test.c/1.25/Sun Sep 23 17:58:57 2018//
+/c_test.h/1.4/Sun Sep 23 17:58:57 2018//
+/c_ulimit.c/1.28/Sun Sep 23 17:58:57 2018//
+/edit.c/1.66/Sun Sep 23 17:59:17 2018//
+/edit.h/1.12/Sun Sep 23 17:59:17 2018//
+/emacs.c/1.85/Result of merge//
+/eval.c/1.63/Sun Sep 23 17:59:17 2018//
+/exec.c/1.73/Sun Sep 23 17:58:57 2018//
+/expand.h/1.15/Sun Sep 23 17:58:57 2018//
+/expr.c/1.33/Sun Sep 23 17:58:57 2018//
+/jobs.c/1.60/Sun Sep 23 17:58:57 2018//
+/ksh.1/1.201/Sun Sep 23 17:59:17 2018//
+/lex.c/1.78/Sun Sep 23 17:58:57 2018//
+/lex.h/1.21/Sun Sep 23 17:58:57 2018//
+/mail.c/1.24/Sun Sep 23 17:59:17 2018//
+/misc.c/1.70/Sun Sep 23 17:58:57 2018//
+/path.c/1.22/Sun Sep 23 17:58:57 2018//
+/sh.1/1.148/Sun Sep 23 17:58:57 2018//
+/sh.h/1.73/Sun Sep 23 17:58:57 2018//
+/shf.c/1.33/Sun Sep 23 17:58:57 2018//
+/shf.h/1.8/Sun Sep 23 17:58:57 2018//
+/syn.c/1.39/Sun Sep 23 17:58:57 2018//
+/table.c/1.25/Sun Sep 23 17:58:57 2018//
+/table.h/1.15/Sun Sep 23 17:59:17 2018//
+/trap.c/1.32/Sun Sep 23 17:58:57 2018//
+/tree.c/1.34/Sun Sep 23 17:58:57 2018//
+/tree.h/1.12/Sun Sep 23 17:58:57 2018//
+/tty.h/1.6/Sun Sep 23 17:58:57 2018//
+/var.c/1.70/Sun Sep 23 17:59:17 2018//
+/version.c/1.12/Sun Sep 23 17:58:57 2018//
+/vi.c/1.56/Sun Sep 23 17:58:57 2018//
 D
diff --git a/CVS/Entries.Log b/CVS/Entries.Log
@@ -0,0 +1,2 @@
+A D/tests////
+R D/tests////
diff --git a/configure b/configure
@@ -95,6 +95,22 @@ EOF
   fi
 }
 
+cursescheck() {
+  cat << EOF > conftest.c
+#include <term.h>
+#include <curses.h>
+int main(void){setupterm(NULL, 0, NULL);return 0;}
+EOF
+  $cc $cflags $ldflags -o conftest conftest.c -lcurses > /dev/null 2>&1
+  if [ $? -eq 0 ] ; then
+    rm -f conftest conftest.o conftest.c
+    return 0
+  else
+    rm -f conftest conftest.o conftest.c
+    return 1
+  fi
+}
+
 deadcheck() {
   cat << EOF > conftest.c
 #include <stdlib.h>
@@ -125,6 +141,38 @@ EOF
   fi
 }
 
+ncursescheck() {
+  cat << EOF > conftest.c
+#include <term.h>
+#include <ncurses.h>
+int main(void){setupterm(NULL, 0, NULL);return 0;}
+EOF
+  $cc $cflags $ldflags -o conftest conftest.c -lncurses > /dev/null 2>&1
+  if [ $? -eq 0 ] ; then
+    rm -f conftest conftest.o conftest.c
+    return 0
+  else
+    rm -f conftest conftest.o conftest.c
+    return 1
+  fi
+}
+
+ncursesncursescheck() {
+  cat << EOF > conftest.c
+#include <ncurses/term.h>
+#include <ncurses/ncurses.h>
+int main(void){setupterm(NULL, 0, NULL);return 0;}
+EOF
+  $cc $cflags $ldflags -o conftest conftest.c -lncurses > /dev/null 2>&1
+  if [ $? -eq 0 ] ; then
+    rm -f conftest conftest.o conftest.c
+    return 0
+  else
+    rm -f conftest conftest.o conftest.c
+    return 1
+  fi
+}
+
 pledgecheck() {
   cat << EOF > conftest.c
 #include <unistd.h>
@@ -413,6 +461,8 @@ fi
 mandir="$prefix/man"
 
 instprog="oksh"
+libs=""
+curses=1
 instsh=0
 static=0
 
@@ -426,6 +476,13 @@ do
     --mandir=*)
 	mandir=`echo $opt | cut -d '=' -f 2`
 	;;
+    --disable-curses|--enable-curses)
+	if [ "x$opt" = "x--disable-curses" ] ; then
+	  curses=0
+	else
+	  curses=1
+	fi
+	;;
     --disable-ksh|--enable-ksh)
 	if [ "x$opt" = "x--enable-ksh" ] ; then
 	  instprog="ksh"
@@ -457,6 +514,8 @@ do
 	echo "Top level install directory is PREFIX [$prefix]"
 	printf "  --mandir=MANDIR         "
 	echo "Manual pages are installed to MANDIR [$mandir]"
+	printf "  --enable-curses         "
+	echo "Use curses library for screen clear [default=yes]"
 	printf "  --enable-ksh            "
 	echo "Install executable as ksh [default=no]"
 	printf "  --enable-sh             "
@@ -580,6 +639,38 @@ else
   echo "no"
 fi
 
+printf "checking for curses library... "
+if [ $curses -eq 1 ] ; then
+  cursescheck
+  if [ $? -eq 0 ] ; then
+    libs="-lcurses"
+    echo "#define HAVE_CURSES" >> pconfig.h
+    echo "curses.h"
+  else
+    ncursescheck
+    if [ $? -eq 0 ] ; then
+      libs="-lncurses"
+      echo "#define HAVE_NCURSES" >> pconfig.h
+      echo "ncurses.h"
+    else
+      ncursesncursescheck
+      if [ $? -eq 0 ] ; then
+	libs="-lncurses"
+	echo "#define HAVE_NCURSESNCURSES" >> pconfig.h
+	echo "ncurses/ncurses.h"
+      else
+	echo "#define NO_CURSES" >> pconfig.h
+	echo "not found"
+	echo "oksh will be built without screen clearing support"
+      fi
+    fi
+  fi
+else
+  echo "#define NO_CURSES" >> pconfig.h
+  echo "disabled by user"
+  echo "oksh will be built without screen clearing support"
+fi
+
 printf "checking for pledge... "
 pledgecheck
 if [ $? -eq 0 ] ; then
@@ -764,7 +855,7 @@ ETS =	\`grep -w \${PREFIX}/bin/\${PROG} /etc/shells > /dev/null; \\
 all: \${PROG}
 
 \${PROG}: \${OBJS}
-	\${CC} \${LDFLAGS} -o \${PROG} \${OBJS}
+	\${CC} \${LDFLAGS} -o \${PROG} \${OBJS} $libs
 
 install:
 	install -d \${DESTDIR}\${PREFIX}/bin
diff --git a/edit.c b/edit.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: edit.c,v 1.65 2018/04/09 17:53:36 tobias Exp $	*/
+/*	$OpenBSD: edit.c,v 1.66 2018/06/18 17:03:58 millert Exp $	*/
 
 /*
  * Command line editing - common code
@@ -138,10 +138,10 @@ x_flush(void)
 	shf_flush(shl_out);
 }
 
-void
+int
 x_putc(int c)
 {
-	shf_putc(c, shl_out);
+	return shf_putc(c, shl_out);
 }
 
 void
diff --git a/edit.h b/edit.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: edit.h,v 1.11 2016/01/26 17:39:31 mmcc Exp $	*/
+/*	$OpenBSD: edit.h,v 1.12 2018/06/18 17:03:58 millert Exp $	*/
 
 /* NAME:
  *      edit.h - globals for edit modes
@@ -37,7 +37,7 @@ extern X_chars edchars;
 /* edit.c */
 int	x_getc(void);
 void	x_flush(void);
-void	x_putc(int);
+int	x_putc(int);
 void	x_puts(const char *);
 bool	x_mode(bool);
 int	promptlen(const char *, const char **);
diff --git a/emacs.c b/emacs.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: emacs.c,v 1.84 2018/01/16 17:17:18 jca Exp $	*/
+/*	$OpenBSD: emacs.c,v 1.85 2018/06/18 17:03:58 millert Exp $	*/
 
 /*
  *  Emacs-like command line editing and history
@@ -20,6 +20,18 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#if !defined(SMALL) && !defined(NO_CURSES)
+#ifdef HAVE_CURSES
+# include <term.h>
+# include <curses.h>
+#elif HAVE_NCURSES
+# include <term.h>
+# include <ncurses.h>
+#elif HAVE_NCURSESNCURSES
+# include <ncurses/term.h>
+# include <ncurses/ncurses.h>
+#endif
+#endif
 
 #include "sys-queue.h"
 #include "sh.h"
@@ -28,6 +40,7 @@
 static	Area	aedit;
 #define	AEDIT	&aedit		/* area for kill ring and macro defns */
 
+#undef CTRL
 #define	CTRL(x)		((x) == '?' ? 0x7F : (x) & 0x1F)	/* ASCII */
 #define	UNCTRL(x)	((x) == 0x7F ? '?' : (x) | 0x40)	/* ASCII */
 
@@ -146,6 +159,7 @@ static int	isu8cont(unsigned char);
 /* proto's for keybindings */
 static int	x_abort(int);
 static int	x_beg_hist(int);
+static int	x_clear_screen(int);
 static int	x_comp_comm(int);
 static int	x_comp_file(int);
 static int	x_complete(int);
@@ -202,6 +216,7 @@ static int	x_debug_info(int);
 static const struct x_ftab x_ftab[] = {
 	{ x_abort,		"abort",			0 },
 	{ x_beg_hist,		"beginning-of-history",		0 },
+	{ x_clear_screen,	"clear-screen",			0 },
 	{ x_comp_comm,		"complete-command",		0 },
 	{ x_comp_file,		"complete-file",		0 },
 	{ x_complete,		"complete",			0 },
@@ -1004,12 +1019,19 @@ x_draw_line(int c)
 {
 	x_redraw(-1);
 	return KSTD;
+}
 
+static int
+x_clear_screen(int c)
+{
+	x_redraw(-2);
+	return KSTD;
 }
 
-/* Redraw (part of) the line.  If limit is < 0, the everything is redrawn
- * on a NEW line, otherwise limit is the screen column up to which needs
- * redrawing.
+/* Redraw (part of) the line.
+ * A non-negative limit is the screen column up to which needs
+ * redrawing. A limit of -1 redraws on a new line, while a limit
+ * of -2 (attempts to) clear the screen.
  */
 static void
 x_redraw(int limit)
@@ -1018,9 +1040,20 @@ x_redraw(int limit)
 	char	*cp;
 
 	x_adj_ok = 0;
-	if (limit == -1)
+	if (limit == -2) {
+		int cleared = 0;
+#if !defined(SMALL) && !defined(NO_CURSES)
+		if (cur_term != NULL && clear_screen != NULL) {
+			if (tputs(clear_screen, 1, x_putc) != ERR)
+				cleared = 1;
+		}
+#endif
+		if (!cleared)
+			x_e_putc('\n');
+	}
+	else if (limit == -1)
 		x_e_putc('\n');
-	else
+	else if (limit >= 0)
 		x_e_putc('\r');
 	x_flush();
 	if (xbp == xbuf) {
diff --git a/eval.c b/eval.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: eval.c,v 1.60 2018/04/09 17:53:36 tobias Exp $	*/
+/*	$OpenBSD: eval.c,v 1.63 2018/07/09 00:20:35 anton Exp $	*/
 
 /*
  * Expansion - quoting, separation, substitution, globbing
@@ -58,6 +58,8 @@ static	char   *tilde(char *);
 static	char   *homedir(char *);
 static void	alt_expand(XPtrV *, char *, char *, char *, int);
 
+static struct tbl *varcpy(struct tbl *);
+
 /* compile and expand word */
 char *
 substitute(const char *cp, int f)
@@ -190,7 +192,8 @@ expand(char *cp,	/* input word */
 	doblank = 0;
 	make_magic = 0;
 	word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
-	st_head.next = NULL;
+
+	memset(&st_head, 0, sizeof(st_head));
 	st = &st_head;
 
 	while (1) {
@@ -305,7 +308,7 @@ expand(char *cp,	/* input word */
 					st->stype = stype;
 					st->base = Xsavepos(ds, dp);
 					st->f = f;
-					st->var = x.var;
+					st->var = varcpy(x.var);
 					st->quote = quote;
 					/* skip qualifier(s) */
 					if (stype)
@@ -577,7 +580,7 @@ expand(char *cp,	/* input word */
 					Xinit(ds, dp, 128, ATEMP);
 			}
 			if (c == 0)
-				return;
+				goto done;
 			if (word != IFS_NWS)
 				word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
 		} else {
@@ -682,6 +685,14 @@ expand(char *cp,	/* input word */
 			word = IFS_WORD;
 		}
 	}
+
+done:
+	for (st = &st_head; st != NULL; st = st->next) {
+		if (st->var == NULL || (st->var->flag & RDONLY) == 0)
+			continue;
+
+		afree(st->var, ATEMP);
+	}
 }
 
 /*
@@ -1286,3 +1297,23 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
 	}
 	return;
 }
+
+/*
+ * Copy the given variable if it's flagged as read-only.
+ * Such variables have static storage and only one can therefore be referenced
+ * at a time.
+ * This is necessary in order to allow variable expansion expressions to refer
+ * to multiple read-only variables.
+ */
+static struct tbl *
+varcpy(struct tbl *vp)
+{
+	struct tbl *cpy;
+
+	if (vp == NULL || (vp->flag & RDONLY) == 0)
+		return vp;
+
+	cpy = alloc(sizeof(struct tbl), ATEMP);
+	memcpy(cpy, vp, sizeof(struct tbl));
+	return cpy;
+}
diff --git a/ksh.1 b/ksh.1
@@ -1,8 +1,8 @@
-.\"	$OpenBSD: ksh.1,v 1.200 2018/05/30 21:20:52 benno Exp $
+.\"	$OpenBSD: ksh.1,v 1.201 2018/06/18 17:03:58 millert Exp $
 .\"
 .\"	Public Domain
 .\"
-.Dd $Mdocdate: May 30 2018 $
+.Dd $Mdocdate: June 18 2018 $
 .Dt KSH 1
 .Os
 .Sh NAME
@@ -1717,6 +1717,10 @@ loops to store the value that is read from standard input.
 The number of seconds since the shell started or, if the parameter has been
 assigned an integer value, the number of seconds since the assignment plus the
 value that was assigned.
+.It Ev TERM
+The user's terminal type.
+If set, it will be used to determine the escape sequence used to
+clear the screen.
 .It Ev TMOUT
 If set to a positive integer in an interactive shell, it specifies the maximum
 number of seconds the shell will wait for input after printing the primary
@@ -4690,6 +4694,11 @@ Moves the cursor to the beginning of the edited input line.
 Uppercase the first character in the next
 .Ar n
 words, leaving the cursor past the end of the last word.
+.It clear-screen:
+Clears the screen if the
+.Ev TERM
+parameter is set and the terminal supports clearing the screen, then
+reprints the prompt string and the current input line.
 .It comment: ^[#
 If the current line does not begin with a comment character, one is added at
 the beginning of the line and the line is entered (as if return had been
diff --git a/mail.c b/mail.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: mail.c,v 1.23 2018/04/09 17:53:36 tobias Exp $	*/
+/*	$OpenBSD: mail.c,v 1.24 2018/06/25 15:22:30 cheloha Exp $	*/
 
 /*
  * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by
@@ -6,6 +6,7 @@
  */
 
 #include <sys/stat.h>
+#include <sys/time.h>
 
 #include <string.h>
 #include <time.h>
@@ -30,7 +31,7 @@ typedef struct mbox {
 
 static mbox_t	*mplist;
 static mbox_t	mbox;
-static time_t	mlastchkd;	/* when mail was last checked */
+static struct	timespec mlastchkd;	/* when mail was last checked */
 static time_t	mailcheck_interval;
 
 static void	munset(mbox_t *); /* free mlist and mval */
@@ -41,14 +42,18 @@ void
 mcheck(void)
 {
 	mbox_t		*mbp;
-	time_t		 now;
+	struct timespec	 elapsed, now;
 	struct tbl	*vp;
 	struct stat	 stbuf;
+	static int	 first = 1;
 
-	now = time(NULL);
-	if (mlastchkd == 0)
+	clock_gettime(CLOCK_MONOTONIC, &now);
+	if (first) {
 		mlastchkd = now;
-	if (now - mlastchkd >= mailcheck_interval) {
+		first = 0;
+	}
+	timespecsub(&now, &mlastchkd, &elapsed);
+	if (elapsed.tv_sec >= mailcheck_interval) {
 		mlastchkd = now;
 
 		if (mplist)
diff --git a/table.h b/table.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: table.h,v 1.14 2018/04/09 17:53:36 tobias Exp $	*/
+/*	$OpenBSD: table.h,v 1.15 2018/06/18 17:03:58 millert Exp $	*/
 
 /* $From: table.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */
 
@@ -170,6 +170,7 @@ extern const struct builtin shbuiltins [], kshbuiltins [];
 #define	V_TMOUT			16
 #define	V_TMPDIR		17
 #define	V_LINENO		18
+#define	V_TERM			19
 
 /* values for set_prompt() */
 #define PS1	0		/* command */
diff --git a/var.c b/var.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: var.c,v 1.68 2018/04/13 18:18:36 cheloha Exp $	*/
+/*	$OpenBSD: var.c,v 1.70 2018/06/18 21:46:05 millert Exp $	*/
 
 #include <sys/stat.h>
 #include <sys/time.h>
@@ -14,6 +14,19 @@
 
 #include "sh.h"
 
+#if !defined(SMALL) && !defined(NO_CURSES)
+#ifdef HAVE_CURSES
+# include <term.h>
+# include <curses.h>
+#elif HAVE_NCURSES
+# include <term.h>
+# include <ncurses.h>
+#elif HAVE_NCURSESNCURSES
+# include <ncurses/term.h>
+# include <ncurses/ncurses.h>
+#endif
+#endif
+
 /*
  * Variables
  *
@@ -111,12 +124,13 @@ initvar(void)
 		{ "SECONDS",		V_SECONDS },
 		{ "TMOUT",		V_TMOUT },
 		{ "LINENO",		V_LINENO },
+		{ "TERM",		V_TERM },
 		{ NULL,	0 }
 	};
 	int i;
 	struct tbl *tp;
 
-	ktinit(&specials, APERM, 32); /* must be 2^n (currently 17 specials) */
+	ktinit(&specials, APERM, 32); /* must be 2^n (currently 19 specials) */
 	for (i = 0; names[i].name; i++) {
 		tp = ktenter(&specials, names[i].name, hash(names[i].name));
 		tp->flag = DEFINED|ISSET;
@@ -1057,6 +1071,18 @@ setspec(struct tbl *vp)
 		user_lineno = (unsigned int) intval(vp) - current_lineno - 1;
 		vp->flag |= SPECIAL;
 		break;
+	case V_TERM:
+#if !defined(SMALL) && !defined(NO_CURSES)
+		{
+			int ret;
+
+			vp->flag &= ~SPECIAL;
+			if (setupterm(str_val(vp), shl_out->fd, &ret) == ERR)
+				del_curterm(cur_term);
+			vp->flag |= SPECIAL;
+		}
+#endif
+		break;
 	}
 }