From: Timo Korvola <tkorvola@e.math.helsinki.fi>
Subject: Unix Frotz patch
Date: 10 Sep 1999 00:00:00 GMT
Message-ID: <m3g10mq41a.fsf@e.math.helsinki.fi>
Content-Transfer-Encoding: 7bit
References: <7qduif$ro@senator-bedfellow.MIT.EDU>
Content-Type: multipart/mixed; boundary="Multipart_Fri_Sep_10_16:31:50_1999-1"
X-Complaints-To: usenet@news.helsinki.fi
X-Trace: oravannahka.helsinki.fi 936970772 26428 128.214.72.172 (10 Sep 1999 13:39:32 GMT)
Organization: University of Helsinki
Mime-Version: 1.0 (generated by tm-edit 7.108)
Reply-To: Timo.Korvola@iki.fi
NNTP-Posting-Date: 10 Sep 1999 13:39:32 GMT
Newsgroups: rec.games.int-fiction

--Multipart_Fri_Sep_10_16:31:50_1999-1
Content-Type: text/plain; charset=US-ASCII

wild_dj@mit.edu (Jake Wildstrom) writes:

> frotz-- I usually use xzip, which never gives me any trouble, but
> occasionally (on dialup, or for color), I get stuck with frotz,
> which I mostly like, except that it doesn't seem to like the
> backspace key. It simply doesn't respond to it at all. The delete
> key, which is the other possibility, replaces whatever's at the
> prompt with "3~".

The backspace problem is the result of an unfortunate misunderstanding
about terminals.  Frotz assumes that the backspace key of the terminal
produces BS, i.e., 8 in ASCII, and that the delete key produces DEL,
127 in ASCII.  In reality the backspace key produces DEL on about half
of the terminals and BS on the others, and the delete key, if
available, usually produces some control sequence hopefully known to
the terminal database.  Fortunately the curses library is designed to
manage all this fuss.

The delete key problem would suggest that in your case the terminal
database does not know the control sequence produced by the delete key 
on your terminal.

This along with some other glitches caused me to patch the unix Frotz
rather heavily.  I mailed the diffs to Galen Hazelwood, who made the
unix port, but never got any reply.  Neither has there been any new
release of unix Frotz.  So maybe I'll just post the diffs in case
people find them useful.  These are against
UnixFrotz232R2Std10.tar.gz, which you can find in the IF archive.
There are lots of cosmetic changes and major changes in functionality
include:

	The terminal erase character, as set with stty, now functions
	as backspace.

	Colours work better: blanked areas get the correct background colour.

	timeout() from the curses library is now used for timed input.

	^L and ^R (control-L etc.) now redraw the screen and are not
	considered input for the Z-machine.  Useful if the display
	gets cluttered.

	ISO-Latin-1 characters are now valid input unless in plain
	ASCII mode.  This makes the meta bit unusable for hotkeys.  On
	terminals with 8-bit character sets alt usually sends ESC
	anyway.

	History search has been added.  Type the beginning of a past
	command and hit the up or down arrow key.

I have only tested this with Ncurses -- some more broken curses
implementations might cause problems I know nothing of.  But I have
completed Beyond Zork and Border Zone with it.  Oh yes: you need to
use page up/down to scroll the upper window in Beyond Zork.

-- 
	Timo Korvola		<URL:http://www.iki.fi/tkorvola>


--Multipart_Fri_Sep_10_16:31:50_1999-1
Content-Type: application/octet-stream; type=patch
Content-Disposition: attachment; filename="frotz-2.32.patch"
Content-Transfer-Encoding: 8bit

--- frotz-2.32/ux_init.c.orig	Sat Oct  4 04:12:10 1997
+++ frotz-2.32/ux_init.c	Thu Apr 22 23:02:48 1999
@@ -320,7 +320,6 @@
 	else
 	  h_default_background = BLUE_COLOUR;
 	os_set_colour(h_default_foreground, h_default_background);
-	bkgdset(current_color);
 	erase();
 	/*os_erase_area(1, 1, h_screen_rows, h_screen_cols);*/
     }
--- frotz-2.32/ux_input.c.orig	Fri Oct 17 23:11:33 1997
+++ frotz-2.32/ux_input.c	Wed Apr 28 02:24:26 1999
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 
 #include <sys/time.h>
 
@@ -24,10 +25,22 @@
 
 static struct timeval global_timeout;
 
+/* Some special characters. */
+#define CHR_CTRL_L 12
+#define CHR_CTRL_R 18
+#define CHR_DEL 127
+#define MOD_META 0x80
+
+/* These are useful for circular buffers.
+ */
+#define RING_DEC( ptr, beg, end) (ptr > (beg) ? --ptr : (ptr = (end)))
+#define RING_INC( ptr, beg, end) (ptr < (end) ? ++ptr : (ptr = (beg)))
+
 #define MAX_HISTORY 20
 static char *history_buffer[MAX_HISTORY];
-static short history_pointer = 0; /* Pointer to next available slot */
-static short history_frame = 0; /* Pointer to what the user's looking at */
+static char **history_next = history_buffer; /* Next available slot. */
+static char **history_view = history_buffer; /* What the user is looking at. */
+#define history_end (history_buffer + MAX_HISTORY - 1)
 
 extern bool is_terminator (zchar);
 extern void read_string (int, zchar *);
@@ -56,107 +69,147 @@
     }
 }
 
+/* This returns the number of milliseconds until the input timeout
+ * elapses or zero if it has already elapsed.  -1 is returned if no
+ * timeout is in effect, otherwise the return value is non-negative.
+ */
+static int timeout_to_ms()
+{
+    struct timeval now, diff;
+
+    if (global_timeout.tv_sec == 0) return -1;
+    gettimeofday( &now, NULL);
+    diff.tv_usec = global_timeout.tv_usec - now.tv_usec;
+    if (diff.tv_usec < 0) {
+	/* Carry */
+	now.tv_sec++;
+	diff.tv_usec += 1000000;
+    }
+    diff.tv_sec = global_timeout.tv_sec - now.tv_sec;
+    if (diff.tv_sec < 0) return 0;
+    if (diff.tv_sec >= INT_MAX / 1000 - 1) /* Paranoia... */
+	return INT_MAX - 1000;
+    return diff.tv_sec * 1000 + diff.tv_usec / 1000;
+}
+
 /*
  * unix_read_char
  *
- * This uses the curses getch() routine to get the next character typed,
- * and returns it unless the global timeout is reached.  It returns values
- * which the standard considers to be legal input, and also returns editing
- * and frotz hot keys.  If called with a non-zero flag, it will also return
- * line-editing keys like INSERT, etc,
+ * This uses the curses getch() routine to get the next character
+ * typed, and returns it.  It returns values which the standard
+ * considers to be legal input, and also returns editing and frotz hot
+ * keys.  If called with extkeys set it will also return line-editing
+ * keys like INSERT etc.
  *
+ * If unix_set_global_timeout has been used to set a global timeout
+ * this routine may also return ZC_TIME_OUT if input times out.
  */
-
-static int unix_read_char(int flag)
+static int unix_read_char(int extkeys)
 {
     int c;
-    struct timeval ctime;
 
     while(1) {
-      /* Timed keyboard input.  Crude but functional. */
-      if (global_timeout.tv_sec) {
-	  nodelay(stdscr, TRUE);
-	  do {
-              c = getch();
-              if (c == ERR) {
-                  gettimeofday(&ctime, NULL);
-                  if ((ctime.tv_sec >= global_timeout.tv_sec) &&
-                      (ctime.tv_usec >= global_timeout.tv_usec)) {
-                      nodelay(stdscr, FALSE);
-                      return 0;
-                  }
-              }
-          } while (c == ERR);
-          nodelay(stdscr, FALSE);
-      }
-      /* The easy way. */
-      else c = getch();
-
-      /* Catch 98% of all input right here... */
-      if ( ((c >= 32) && (c <= 126)) || (c == 13) || (c == 8)) 
-        return c;
-
-      /* ...and the other 2% makes up 98% of the code. :( */
-      switch(c) {
-        /* Lucian P. Smith reports KEY_ENTER on Irix 5.3.  10 has never
-           been reported, but I'm leaving it in just in case. */
-        case 10: case KEY_ENTER: return 13;
-        /* I've seen KEY_BACKSPACE returned on some terminals. */
-        case KEY_BACKSPACE: return 8;
-        /* On seven-bit connections, "Alt-Foo" is returned as an escape 
-           followed by the ASCII value of the letter.  We have to decide
-           here whether to return a single escape or a frotz hot key. */
-        case 27: nodelay(stdscr, TRUE); c = getch(); nodelay(stdscr, FALSE);
-                 if (c == ERR) return 27;
-                 switch(c) {
-                   case 112: return ZC_HKEY_PLAYBACK; /* Alt-P */
-                   case 114: return ZC_HKEY_RECORD; /* Alt-R */
-                   case 115: return ZC_HKEY_SEED; /* Alt-S */
-                   case 117: return ZC_HKEY_UNDO; /* Alt-U */
-                   case 110: return ZC_HKEY_RESTART; /* Alt-N */
-                   case 120: return ZC_HKEY_QUIT; /* Alt-X */
-		   case 100: return ZC_HKEY_DEBUG; /* Alt-D */
-		   case 104: return ZC_HKEY_HELP; /* Alt-H */
-                   default: return 27;
-                 }
-                 break;
+	timeout( timeout_to_ms());
+	c = getch();
+
+	/* Catch 98% of all input right here... */
+	if ((c >= ZC_ASCII_MIN && c <= ZC_ASCII_MAX)
+	    || (!unix_plain_ascii
+		&& c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX)) 
+	    return c;
+
+	/* ...and the other 2% makes up 98% of the code. :( */
+
+	/* On many terminals the backspace key returns DEL. */
+	if (c == erasechar()) return ZC_BACKSPACE;;
+	switch(c) {
+	/* Normally ERR means timeout.  I suppose we might also get
+	   ERR if a signal hits getch. */
+	case ERR:
+	    if (timeout_to_ms() == 0)
+		return ZC_TIME_OUT;
+	    else
+		continue;
+	/* Screen decluttering. */
+	case CHR_CTRL_L: case CHR_CTRL_R:
+	    clearok( curscr, 1); refresh(); clearok( curscr, 0);
+	    continue;
+	/* Lucian P. Smith reports KEY_ENTER on Irix 5.3.  LF has never
+	   been reported, but I'm leaving it in just in case. */
+	case '\n': case '\r': case KEY_ENTER: return ZC_RETURN;
+	/* I've seen KEY_BACKSPACE returned on some terminals. */
+	case KEY_BACKSPACE: case '\b': return ZC_BACKSPACE;
+	/* On terminals with 8-bit character sets or 7-bit connections
+	   "Alt-Foo" may be returned as an escape followed by the ASCII
+	   value of the letter.  We have to decide here whether to
+	   return a single escape or a frotz hot key. */
+	case ZC_ESCAPE:
+	    nodelay(stdscr, TRUE); c = getch(); nodelay(stdscr, FALSE);
+	    switch(c) {
+	    case ERR: return ZC_ESCAPE;
+	    case 'p': return ZC_HKEY_PLAYBACK;
+	    case 'r': return ZC_HKEY_RECORD;
+	    case 's': return ZC_HKEY_SEED;
+	    case 'u': return ZC_HKEY_UNDO;
+	    case 'n': return ZC_HKEY_RESTART;
+	    case 'x': return ZC_HKEY_QUIT;
+	    case 'd': return ZC_HKEY_DEBUG;
+	    case 'h': return ZC_HKEY_HELP;
+	    default: continue;	/* Ignore unknown combinations. */
+	    }
 	/* The standard function key block. */
-        case KEY_UP: return 129;
-        case KEY_DOWN: return 130;
-        case KEY_LEFT: return 131;
-        case KEY_RIGHT: return 132;
-        case KEY_F(1): return 133; case KEY_F(2): return 134;
-        case KEY_F(3): return 135; case KEY_F(4): return 136;
-        case KEY_F(5): return 137; case KEY_F(6): return 138;
-        case KEY_F(7): return 139; case KEY_F(8): return 140;
-        case KEY_F(9): return 141; case KEY_F(10): return 142;
-        case KEY_F(11): return 143; case KEY_F(12): return 144;
-        /* Curses can't differentiate keypad numbers from cursor keys. Damn. */
-        /* This will catch the alt-keys on 8-bit clean input streams... */
-        case 240: return ZC_HKEY_PLAYBACK; /* Alt-P */
-        case 242: return ZC_HKEY_RECORD; /* Alt-R */
-        case 243: return ZC_HKEY_SEED; /* Alt-S */
-        case 245: return ZC_HKEY_UNDO; /* Alt-U */
-        case 238: return ZC_HKEY_RESTART; /* Alt-N */
-        case 248: return ZC_HKEY_QUIT; /* Alt-X */
-        case 228: return ZC_HKEY_DEBUG; /* Alt-D */
-        case 232: return ZC_HKEY_HELP; /* Alt-H */
+	case KEY_UP: return ZC_ARROW_UP;
+	case KEY_DOWN: return ZC_ARROW_DOWN;
+	case KEY_LEFT: return ZC_ARROW_LEFT;
+	case KEY_RIGHT: return ZC_ARROW_RIGHT;
+	case KEY_F(1): return ZC_FKEY_MIN;
+	case KEY_F(2): return ZC_FKEY_MIN + 1;
+	case KEY_F(3): return ZC_FKEY_MIN + 2;
+	case KEY_F(4): return ZC_FKEY_MIN + 3;
+	case KEY_F(5): return ZC_FKEY_MIN + 4;
+	case KEY_F(6): return ZC_FKEY_MIN + 5;
+	case KEY_F(7): return ZC_FKEY_MIN + 6;
+	case KEY_F(8): return ZC_FKEY_MIN + 7;
+	case KEY_F(9): return ZC_FKEY_MIN + 8;
+	case KEY_F(10): return ZC_FKEY_MIN + 9;
+	case KEY_F(11): return ZC_FKEY_MIN + 10;
+	case KEY_F(12): return ZC_FKEY_MIN + 11;
+	/* Curses can't differentiate keypad numbers from cursor keys,
+	   which is annoying, as cursor and keypad keys have
+	   nothing to do with each other on, say, a vt200.
+	   So we can only read 1, 3, 5, 7 and 9 from the keypad.  This
+	   would be so silly that we choose not to provide keypad keys at all.
+	*/
+        /* Catch the meta key on those plain old ASCII terminals where
+	   it sets the high bit.  This only works in
+	   unix_plain_ascii mode: otherwise these character codes
+	   would have been interpreted according to ISO-Latin-1
+	   earlier. */
+	case MOD_META | 'p': return ZC_HKEY_PLAYBACK;
+	case MOD_META | 'r': return ZC_HKEY_RECORD;
+	case MOD_META | 's': return ZC_HKEY_SEED;
+	case MOD_META | 'u': return ZC_HKEY_UNDO;
+	case MOD_META | 'n': return ZC_HKEY_RESTART;
+	case MOD_META | 'x': return ZC_HKEY_QUIT;
+	case MOD_META | 'd': return ZC_HKEY_DEBUG;
+	case MOD_META | 'h': return ZC_HKEY_HELP;
 #ifdef EMACS_EDITING
-        case 21: return 27; /* Ctrl-U, erase line */
-        case 2: return 131; /* Ctrl-B, left arrow  */
-        case 6: return 132; /* Ctrl-F, right arrow */
-        case 16: return 129; /* Ctrl-P, up arrow */
-        case 14: return 130; /* Ctrl-N, down arrow */
-        case 1: c = KEY_HOME; break; /* Ctrl-A */
-        case 4: c = 127; break; /* Ctrl-D */
-        case 5: c = KEY_END; break; /* Ctrl-E */
+	case 21: return ZC_ESCAPE; /* Ctrl-U, erase line */
+	case 2: return ZC_ARROW_LEFT; /* Ctrl-B */
+	case 6: return ZC_ARROW_RIGHT; /* Ctrl-F */
+	case 16: return ZC_ARROW_UP; /* Ctrl-P */
+	case 14: return ZC_ARROW_DOWN; /* Ctrl-N */
+	case 1: c = KEY_HOME; break; /* Ctrl-A */
+	case 4: c = CHR_DEL; break; /* Ctrl-D */
+	case 5: c = KEY_END; break; /* Ctrl-E */
 #endif
 	default: break; /* Who knows? */
-      }
+	}
 
-      /* Finally, if we're in full line mode (os_read_line), we might return
-         codes which aren't legal Z-machine keys but are used by the editor. */
-      if (flag) return c;
+	/* Finally, if we're in full line mode (os_read_line), we
+	   might return codes which aren't legal Z-machine keys but
+	   are used by the editor. */
+	if (extkeys) return c;
     }
 }
 
@@ -164,65 +217,67 @@
 /*
  * unix_add_to_history
  *
- * Add the given string to the next available history buffer slot.  Commandeer
- * that slot if necessary using realloc.
+ * Add the given string to the next available history buffer slot.
  *
  */
 
 static void unix_add_to_history(zchar *str)
 {
 
-    if (history_buffer[history_pointer] == NULL)
-      history_buffer[history_pointer] = (char *) malloc(strlen(str) + 1);
-    else
-      history_buffer[history_pointer] =
-        (char *) realloc(history_buffer[history_pointer], strlen(str) + 1);
-    strcpy(history_buffer[history_pointer], str);
-    history_pointer = ((history_pointer + 1) % MAX_HISTORY);
-    history_frame = history_pointer; /* Reset user frame after each line */
+    if (*history_next != NULL)
+	free( *history_next);
+    *history_next = (char *)malloc(strlen(str) + 1);
+    strcpy( *history_next, str);
+    RING_INC( history_next, history_buffer, history_end);
+    history_view = history_next; /* Reset user frame after each line */
 }
 
 /*
  * unix_history_back
  *
  * Copy last available string to str, if possible.  Return 1 if successful.
- *
+ * Only lines of at most maxlen characters will be considered.  In addition
+ * the first searchlen characters of the history entry must match those of str.
  */
-
-static int unix_history_back(zchar *str)
+static int unix_history_back(zchar *str, int searchlen, int maxlen)
 {
+    char **prev = history_view;
 
-    history_frame--; if (history_frame==-1) history_frame = (MAX_HISTORY - 1);
-    if ((history_frame == history_pointer) ||
-        (history_buffer[history_frame] == NULL)) {
-        beep(); history_frame = (history_frame + 1) % MAX_HISTORY;
-        return 0;
-    }
-    strcpy(str, history_buffer[history_frame]);
+    do {
+	RING_DEC( history_view, history_buffer, history_end);
+	if ((history_view == history_next)
+	    || (*history_view == NULL)) {
+	    beep();
+	    history_view = prev;
+	    return 0;
+	}
+    } while (strlen( *history_view) > maxlen
+	     || (searchlen != 0 && strncmp( str, *history_view, searchlen)));
+    strcpy(str + searchlen, *history_view + searchlen);
     return 1;
-
 }
 
 /*
  * unix_history_forward
  *
  * Opposite of unix_history_back, and works in the same way.
- *
  */
-
-static int unix_history_forward(zchar *str)
+static int unix_history_forward(zchar *str, int searchlen, int maxlen)
 {
+    char **prev = history_view;
 
-    history_frame = (history_frame + 1) % MAX_HISTORY;
-    if ((history_frame == history_pointer) ||
-        (history_buffer[history_frame] == NULL)) {
-        beep(); history_frame--; if (history_frame == -1) history_frame =
-                                                            (MAX_HISTORY - 1);
-        return 0;
-    }
-    strcpy(str, history_buffer[history_frame]);
+    do {
+	RING_INC( history_view, history_buffer, history_end);
+	if ((history_view == history_next)
+	    || (*history_view == NULL)) {
+	    beep();
+	    history_view = prev;
+	    return 0;
+	}
+    } while (strlen( *history_view) > maxlen
+	     || (searchlen != 0 && strncmp( str, *history_view, searchlen)));
+    strcpy(str + searchlen, *history_view + searchlen);
     return 1;
-
 }
 
 /*
@@ -253,7 +308,7 @@
  *     ZC_HKEY_HELP (Alt-H)
  *
  * If the timeout argument is not zero, the input gets interrupted
- * after timeout/10 seconds (and the return value is 0).
+ * after timeout/10 seconds (and the return value is ZC_TIME_OUT).
  *
  * The complete input line including the cursor must fit in "width"
  * screen units.
@@ -273,145 +328,161 @@
 
 zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued)
 {
-    int ch, scrpos, pos, y, x;
-    char insert_flag = 1;  /* Insert mode is now default */
-
-    scrpos = pos = strlen((char *) buf);
-
-    if (!continued)
-      history_frame = history_pointer;  /* Reset user's history view */
-
-    unix_set_global_timeout(timeout);
+    int ch, y, x, len = strlen( (char *)buf);
+    /* These are static to allow input continuation to work smoothly. */
+    static int scrpos = 0, searchpos = -1, insert_flag = 1;
 
     getyx(stdscr, y, x);
+    if (width < max) max = width;
+    /* Better be careful here or it might segv.  I wonder if we should just
+       ignore 'continued' and check for len > 0 instead?  Might work better
+       with Beyond Zork. */
+    if (continued && scrpos <= len && searchpos <= len) {
+	x += scrpos - len;
+	move( y, x);
+    } else {
+	scrpos = len;
+	history_view = history_next; /* Reset user's history view. */
+	searchpos = -1;		/* -1 means initialize from len. */
+	insert_flag = 1;	/* Insert mode is now default. */
+    }
 
-    do {
-        refresh(); /* Shouldn't be necessary, but is, to print spaces */
-
-        ch = unix_read_char(1);
-
-	/* Backspace */
-        if ((ch == 8) && (scrpos)) {
-            mvdelch(y, --x);
-            pos--; scrpos--;
-            if (scrpos != pos)
-              memmove(&(buf[scrpos]), &(buf[scrpos+1]), pos-scrpos);
-        }
-
-	/* Delete */
-	if (((ch == 127) || (ch == KEY_DC)) && (scrpos < pos)) {
-	    delch(); pos--;
-	    memmove(&(buf[scrpos]), &(buf[scrpos+1]), pos-scrpos);
-	}
-
-        /* Left key */
-        if ((ch == 131) && (scrpos)) {
-            scrpos--;
-            move(y, --x);
-        }
-        /* Right key */
-        if ((ch == 132) && (scrpos < pos)) {
-            scrpos++;
-            move(y, ++x);
-        }
-
-        /* Home */
-        if (ch == KEY_HOME) {
-            x -= scrpos; scrpos = 0;
+    unix_set_global_timeout(timeout);
+    for (;;) {
+        switch (ch = unix_read_char(1)) {
+	case ZC_BACKSPACE:
+	    if (scrpos > 0) {
+		mvdelch(y, --x);
+		len--; scrpos--; searchpos = -1;
+		if (scrpos != len)
+		    memmove(&(buf[scrpos]), &(buf[scrpos+1]), len-scrpos);
+	    }
+	    break;
+	case CHR_DEL:
+	case KEY_DC:		/* Delete Character */
+	    if (scrpos < len) {
+		delch();
+		len--; searchpos = -1;
+		memmove(&(buf[scrpos]), &(buf[scrpos+1]), len-scrpos);
+	    }
+	    continue;		/* Don't feed is_terminator bad zchars. */
+	case ZC_ARROW_LEFT:
+	    if (scrpos > 0) {
+		scrpos--;
+		move(y, --x);
+	    }
+	    continue;
+        case ZC_ARROW_RIGHT:
+	    if (scrpos < len) {
+		scrpos++;
+		move(y, ++x);
+	    }
+	    continue;
+	case KEY_HOME:
+            x -= scrpos;
+	    scrpos = 0;
             move(y, x);
-        }
-        /* End */
-        if (ch == KEY_END) {
-            x += (pos - scrpos); scrpos = pos;
+	    continue;
+        case KEY_END:
+            x += (len - scrpos); scrpos = len;
             move(y,x);
-        }
-
-        /* Insert */
-        if (ch == KEY_IC)
-          insert_flag = (insert_flag ? 0 : 1);
-
-        /* Up and down keys */
-        if (ch == 129) {
-            if (unix_history_back(buf)) {
-              x -= scrpos;
-              move(y, x);
-              while (scrpos) {addch(' '); scrpos--;}
-              move(y, x);
-              addstr(buf);
-              scrpos = pos = strlen(buf);
-              x += scrpos;
+	    continue;
+	case KEY_IC:		/* Insert Character */
+	    insert_flag = !insert_flag;
+	    continue;
+	case ZC_ARROW_UP:
+	    if (searchpos < 0)
+		searchpos = len;
+            if (unix_history_back(buf, searchpos, max)) {
+		x -= scrpos;
+		move(y, x);
+		clrtoeol();
+		addstr(buf);
+		x += scrpos = len = strlen(buf);
             }
-        }
-        if (ch == 130) {
-            if (unix_history_forward(buf)) {
-              x -= scrpos;
-              move(y, x);
-              while(scrpos) {addch(' '); scrpos--;}
-              move(y, x);
-              addstr(buf);
-              scrpos = pos = strlen(buf);
-              x += scrpos;
+	    continue;
+        case ZC_ARROW_DOWN:
+	    if (searchpos < 0)
+		searchpos = len;
+            if (unix_history_forward(buf, searchpos, max)) {
+		x -= scrpos;
+		move(y, x);
+		clrtoeol();
+		addstr(buf);
+		x += scrpos = len = strlen(buf);
             }
-        }
-
-	/* Page up/down (passthrough as up/down arrows for beyond zork) */
-        if (ch == KEY_PPAGE) ch = 129;
-        if (ch == KEY_NPAGE) ch = 130;
-
-        /* Escape */
-        if (ch == 27) {
-	    /* Move cursor to end of buffer first */
-            x += (pos - scrpos); scrpos = pos; move(y,x);
+	    continue;
+	/* Passthrough as up/down arrows for Beyond Zork. */
+	case KEY_PPAGE: ch = ZC_ARROW_UP; break;
+	case KEY_NPAGE: ch = ZC_ARROW_DOWN; break;
+	case ZC_ESCAPE:
             x -= scrpos;
             move(y, x);
-            while (scrpos) {addch(' '); scrpos--;}
-            move(y, x); pos = 0;
-        }
-
-	/* Tab */
-	if ((ch == 9) && (scrpos == pos)) {
-	    int status;
-	    zchar extension[10];
-
-	    status = completion(buf, extension);
-	    if (status != 2) {
-	      addstr(extension);
-	      strcpy(&buf[pos], extension);
-	      pos += strlen(extension); scrpos += strlen(extension);
+	    clrtoeol();
+	    scrpos = len = 0;
+	    searchpos = -1;
+	    history_view = history_next;
+	    break;
+	case '\t':
+	    /* This really should be fixed to work also in the middle of a
+	       sentence. */
+	    if (scrpos == len) {
+		int status;
+		zchar extension[10];
+		
+		buf[len] = '\0';
+		status = completion( buf, extension);
+		if (status != 2) {
+		    strncpy( &buf[len], extension, max - len);
+		    len += strlen( extension);
+		    if (len > max) {len = max; status = 1;}
+		    addnstr( extension, len - scrpos);
+		    x += len - scrpos;
+		    scrpos = len;
+		    searchpos = -1;
+		}
+		if (status) beep();
+	    }
+	    continue;		/* TAB is invalid as an input character. */
+	default:
+	    /* ASCII or ISO-Latin-1 */
+	    if ((ch >= ZC_ASCII_MIN && ch <= ZC_ASCII_MAX)
+		|| (!unix_plain_ascii
+		    && ch >= ZC_LATIN1_MIN && ch <= ZC_LATIN1_MAX)) {
+		if (len == scrpos)
+		    /* Append to end of buffer */
+		    if (len < max) {
+			buf[len++] = (char) ch;
+			addch(ch);
+			scrpos++; x++;
+		    } else
+			beep();
+		else
+		    /* Insert/overwrite in middle of buffer */
+		    if (insert_flag) {
+			memmove(&buf[scrpos+1], &buf[scrpos], len-scrpos);
+			buf[scrpos++] = (char) ch;
+			insch(ch);
+			len++; x++; move(y, x);
+		    } else {
+			buf[scrpos++] = (char) ch;
+			addch(ch);
+			x++;
+		    }
+		searchpos = -1;
 	    }
-	    if (status) beep();
-	}
-
-        /* ASCII printable */
-        if ((ch >= 32) && (ch <= 126)) {
-            if (pos == scrpos) {
-                /* Append to end of buffer */
-                if ((pos < max) && (pos < width)) {
-                    buf[pos++] = (char) ch;
-                    addch(ch);
-                    scrpos++; x++;
-                } else beep();
-            }
-            else {
-                /* Insert/overwrite in middle of buffer */
-                if (insert_flag) {
-                    memmove(&buf[scrpos+1], &buf[scrpos], pos-scrpos);
-                    buf[scrpos++] = (char) ch;
-                    insch(ch);
-                    pos++; x++; move(y, x);
-                } else {
-                    buf[scrpos++] = (char) ch;
-                    addch(ch);
-                    x++;
-                }
-            }
         }
-    } while (!is_terminator(ch));
-
-    buf[pos] = '\0';
-    if (ch == 13) unix_add_to_history(buf);
-    return ch;
-
+	if (is_terminator(ch)) {
+	    buf[len] = '\0';
+	    if (ch == ZC_RETURN)
+		unix_add_to_history(buf);
+	    /* Games don't know about line editing and might get
+	       confused if the cursor is not at the end of the input
+	       line. */
+	    move( y, x + len - scrpos);
+	    return ch;
+	}
+    }
 }/* os_read_line */
 
 /*
@@ -487,3 +558,9 @@
     return 1;
 
 } /* os_read_file_name */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * End:
+ */
--- frotz-2.32/ux_text.c.orig	Sat Oct  4 04:12:10 1997
+++ frotz-2.32/ux_text.c	Sat Apr 24 12:45:17 1999
@@ -111,26 +111,21 @@
  *
  */
 
-#ifdef COLOR_SUPPORT
-static int colorspace[10][10];
-static int count = 0;
-#endif
-
 void os_set_colour (int new_foreground, int new_background)
 {
 #ifdef COLOR_SUPPORT
-    int saved_style;
+    static int colorspace[10][10];
+    static int n_colors = 0;
 
-    saved_style = current_text_style;
     if (new_foreground == 1) new_foreground = h_default_foreground;
     if (new_background == 1) new_background = h_default_background;
     if (!colorspace[new_foreground][new_background]) {
-      init_pair(++count, unix_convert(new_foreground), unix_convert(new_background));
-      colorspace[new_foreground][new_background] = count;
+      init_pair(++n_colors, unix_convert(new_foreground),
+		unix_convert(new_background));
+      colorspace[new_foreground][new_background] = n_colors;
     }
     current_color = COLOR_PAIR(colorspace[new_foreground][new_background]);
-    os_set_text_style(saved_style);
-
+    bkgdset( current_color | ' ');
 #endif
 }/* os_set_colour */
 
@@ -154,12 +149,7 @@
     if (new_style & REVERSE_STYLE) temp |= A_REVERSE;
     if (new_style & BOLDFACE_STYLE) temp |= A_BOLD;
     if (new_style & EMPHASIS_STYLE) temp |= A_UNDERLINE;
-#ifdef COLOR_SUPPORT
-    attrset(temp | current_color);
-#else
     attrset(temp);
-#endif
-
 }/* os_set_text_style */
 
 /*
@@ -211,7 +201,7 @@
 	  addch(c);
 	return;
     }
-    if (c >= 32 && c <= 126) {
+    if (c >= ZC_ASCII_MIN && c <= ZC_ASCII_MAX) {
         addch(c);
 	return;
     }

--Multipart_Fri_Sep_10_16:31:50_1999-1--
