[alt.sources.patches] PD KSH 3.2 alpha - update-1/2

sjg@zen.void.oz.au (Simon J. Gerraty) (04/25/91)

As promissed here are my updates to the PD KSH, to compile
it on a Sun, fix a few bugs and add a few features.  The README
file is just below...

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README src/edit.c
# Wrapped by sjg@zen on Thu Apr 25 18:06:08 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(1824 characters\)
sed "s/^X//" >README <<'END_OF_README'
XPD KSH 3.2 alpha  Update README
X
XThis is the README file for my patches to the PD KSH (posted to
Xalt.sources in Dec 90).  The update consists of two shell
Xarchives. 
X
XArchive 1 contents:
XREADME			# this file
Xsrc/edit.c		# new edit.c
X
XArchive 2 contents:
Xsrc/ChangeLog		# change log :-)
Xstdc/ChangeLog		# ditto
Xsun386i.diffs		# sun 386i related diffs
Xbugs.diffs		# bug fixes and new features
X
XThe new edit.c adds several features to the PD KSH.
XThe ability to edit lines longer than the screen width.
XThis is done in a manner comaptible with the real ksh.
X
XNew features:
XM-[0-9]		Esc followed by a digit 0-9 sets an argument
X		that applies to the following command.
XM-.
XM-_		Retrieves the last (or n'th if preceded by arg)
X		word from previous command line.
XM-u		Upcase-word
XM-l		Downcase-word
XM-c		Capitalize-word
X
XAll word related functions now pay attention to the arg set by
XM-[0-9].
XThe editor's behaviour now agrees with the description in the
Xreal man page.  That is; the editor treats only a sequence of
Xdigits and alpha chars as a word.  Thus '/','.' etc are all word
Xseparators as one might expect.  Under certain circumstances
Xsuch as file name completion and retrieving words from previous
Xcommands the original word separators ' ' and '\t' are used.
X
XI use this shell all day on my workstation at work where I also
Xuse the real ksh on another system.  With the new edit.c it is
Xvery difficult to pick the difference between the real and PD
Xkorn-shell. 
X
XThe important features still missing are support for:
X
X$HISTFILE
X$HISTSIZE
X$MAILPATH
X$CDPATH
X[[]]
X
XPlus there are still bugs in some of the variable expansion code
X${var%%*.c} doesn't work for instance.
XHopefully one day some one will address these.
XIf you do, please send a copy to:
X
XSimon J. Gerraty	<sjg@zen.void.oz.au>
X		     or <sjg@melb.bull.oz.au>
X
X
X
END_OF_README
if test 1824 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/edit.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/edit.c\"
else
echo shar: Extracting \"src/edit.c\" \(36696 characters\)
sed "s/^X//" >src/edit.c <<'END_OF_src/edit.c'
X/*
X *  EDIT.C -- Emacs-like command line editing and history
X *
X *  created by Ron Natalie at BRL
X *  modified by Doug Kingston, Doug Gwyn, and Lou Salkind
X *  adapted to PD ksh by Eric Gisin
X *  Modified Feb 1991 by Simon Gerraty, to handle editing lines 
X *  longer than $COLUMNS, in a manner similar to the real KSH.
X *  
X */
X
X#if EDIT
X#ifndef lint
Xstatic char *RCSid = "$Header: /tmp/egisin/src/RCS/edit.c,v 3.2 88/12/14 20:11:44 egisin Exp $";
Xstatic char *sccsid = "@(#)edit.c 1.10 91/04/25 15:39:13 (sjg)";
X#endif
X
X#include <stddef.h>
X#include <stdlib.h>
X#include <string.h>
X#include <stdio.h>
X#include <unistd.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X#include "lex.h"
X#include "tree.h"		/* DOTILDE */
X#include "tty.h"
X#include "table.h"
X#include "expand.h"
X
X#define PUSH_DELETE 1			/* push all deletes of >1 char */
X
XArea	aedit;
X#define	AEDIT	&aedit		/* area for kill ring and macro defns */
X
X#undef CTRL			/* _BSD brain damage */
X#define	CTRL(x)		((x) == '?' ? 0x7F : (x) & 0x1F)	/* ASCII */
X#define	UNCTRL(x)	((x) == 0x7F ? '?' : (x) | 0x40)	/* ASCII */
X
X#if ! defined S_ISDIR
X#define S_ISDIR(mode)	(((mode) & S_IFMT) == S_IFDIR)
X#endif
X
X#if ! defined S_ISREG
X#define S_ISREG(mode)	(((mode) & S_IFMT) == S_IFREG)
X#endif
X
X#if defined _CRAY2
Xextern unsigned	sleep();
X#endif
X
X/* values returned by keyboard functions */
X#define	KSTD	0
X#define	KPREF	1		/* ^[, ^X */
X#define	KEOL	2		/* ^M, ^J */
X#define	KINTR	3		/* ^G, ^C */
X	
Xstruct	x_ftab  {
X	int	(*xf_func)();
X	char	*xf_name;
X	char	xf_db_tab;
X	char	xf_db_char;
X	short	xf_flags;
X};
X
X#define	XF_NINPUT	1
X#define	XF_ALLOC	2
X#define	XF_NOBIND	4
X
X
X/*
X * "the editor's idea of a word is a string containing only
X * letters digits and underscores"
X */
X#define isfs(c)	((int) strchr(" \t!@#$%^&*()-=+|{}[]:;\"'~`<,>.?/", c))
X/*
X * this is the original fs test,
X * it is still useful in come cases.
X */
X#define ISFS(c)	(c == ' ' || c == '\t')
X
X#define	BEL		0x07
X#define	CMASK		0x7F	/* 7-bit ASCII character mask */
X
Xtypedef int bool_t;
X#define	FALSE	0
X#define	TRUE	1
X
Xstatic bool_t	x_mode = FALSE;
Xstatic int	x_prefix1 = CTRL('['), x_prefix2 = CTRL('X');
Xstatic char   **x_histp;	/* history position */
Xstatic	char   *xbuf;		/* beg input buffer */
Xstatic	char   *xend;		/* end input buffer */
Xstatic char    *xcp;
Xstatic char    *xep;
Xstatic char    *xbp;		/* start of visible portion of input buffer */
Xstatic int	x_adj_ok = 1;
X/*
X * we use x_adj_done so that functions can tell 
X * whether x_adjust() has been called while they are active.
X */
Xstatic int	x_adj_done = 0;
X
Xstatic int    (*x_last_command)();
X/*static struct	x_ftab *x_tab[3][128];*/
Xstatic struct	x_ftab Const *(*x_tab)[128] = NULL; /* key definition */
Xstatic char    *(*x_atab)[128] = NULL; /* macro definitions */
X#define	KILLSIZE	20
Xstatic char    *killstack[KILLSIZE];
Xstatic int	killsp, killtp;
Xstatic int	x_curprefix;
Xstatic char    *macroptr;
Xstatic int	first_time = 1;
Xstatic int	x_maxlen;	/* to determine column width */
Xstatic int	x_cols = 80;	/* todo: $COLUMNS */
Xstatic int	x_col = 0;
Xstatic int	x_displen;
Xstatic int	x_arg;		/* general purpose arg */
X
Xstatic void	x_flush(), x_putc(), x_puts();
Xstatic void	x_goto(), x_bs(), x_delete(), x_ins(), x_mapin();
Xstatic int	x_fword(), x_bword(), x_size(), x_size_str();
Xstatic void	x_zotc(), x_zots(), x_push(), x_redraw(), x_load_hist();
Xstatic void	compl_command(), compl_dec(), compl_file();
Xstatic int	x_insert(), x_ins_string(), x_del_back();
Xstatic int	x_del_char(), x_del_bword(), x_mv_bword(), x_mv_fword();
Xstatic int	x_del_fword(), x_mv_back(), x_mv_forw(), x_search_char();
Xstatic int	x_newline(), x_end_of_text(), x_abort(), x_error();
Xstatic int	x_beg_hist(), x_end_hist(), x_prev_com(), x_next_com();
Xstatic int	x_search_hist(), x_del_line(), x_mv_end(), x_mv_begin();
Xstatic int	x_draw_line(), x_transpose(), x_meta1(), x_meta2();
Xstatic int	x_kill(), x_yank(), x_meta_yank(), x_literal();
Xstatic int	x_stuffreset(), x_stuff(), x_complete(), x_enumerate();
X#if SILLY
Xstatic int	x_game_of_life();
X#endif
Xstatic int	x_comp_file(), x_comp_comm();
Xstatic int	x_list_file(), x_list_comm();
Xstatic int	strmatch();
X#ifdef DEBUG
Xstatic int	x_debug_info();
X#endif
Xstatic int	x_prev_histword();
Xstatic int	x_set_arg();
Xstatic int	x_fold_case();
X
Xstatic struct x_ftab Const x_ftab[] = {
X 	{x_insert,	"auto-insert",		0,	 0,	0 },
X	{x_error,	"error",		0,	 0,	0 },
X 	{x_ins_string,	"macro-string",		0,	 0,	XF_NOBIND|XF_ALLOC},
X/* Do not move the above! */
X	{x_del_back,	"delete-char-backward",	0, CTRL('H'),	0 },
X	{x_del_char,	"delete-char-forward",	0, CTRL('D'),	0 },
X	{x_del_bword,	"delete-word-backward",	0, CTRL('W'),	0 },
X	{x_mv_bword,	"backward-word", 	1,	'b',	0 },
X	{x_mv_fword,	"forward-word",		1,	'f',	0 },
X	{x_del_fword,	"delete-word-forward", 	1,	'd',	0 },
X	{x_mv_back,	"backward-char",	0, CTRL('B'),	0 },
X	{x_mv_forw,	"forward-char",		0, CTRL('F'),	0 },
X	{x_search_char,	"search-character",	0, CTRL(']'),	0 },
X	{x_newline,	"newline",		0, CTRL('M'),	0 },
X	{x_newline,	"newline",		0, CTRL('J'),	0 },
X	{x_end_of_text,	"eot",			0, CTRL('_'),	0 },
X	{x_abort,	"abort",		0, CTRL('G'),	0 },
X	{x_prev_com,	"up-history",		0, CTRL('P'),	XF_NINPUT},
X	{x_next_com,	"down-history",		0, CTRL('N'),	XF_NINPUT},
X	{x_search_hist,	"search-history",	0, CTRL('R'),	XF_NINPUT},
X	{x_beg_hist,	"beginning-of-history",	1,	'<',	XF_NINPUT},
X	{x_end_hist,	"end-of-history",	1,	'>',	XF_NINPUT},
X	{x_del_line,	"kill-line",		0, CTRL('U'),	0 },
X	{x_mv_end,	"end-of-line",		0, CTRL('E'),	0 },
X	{x_mv_begin,	"beginning-of-line",	0, CTRL('A'),	0 },
X	{x_draw_line,	"redraw",		0, CTRL('L'),	0 },
X	{x_meta1,	"prefix-1",		0, CTRL('['),	0 },
X	{x_meta2,	"prefix-2",		0, CTRL('X'),	0 },
X	{x_kill,	"kill-to-eol",		0, CTRL('K'),	0 },
X	{x_yank,	"yank",			0, CTRL('Y'),	0 },
X	{x_meta_yank,	"yank-pop", 		1,	'y',	0 },
X	{x_literal,	"quote",		0, CTRL('^'),	0 },
X	{x_stuffreset, 	"stuff-reset",		0,	 0,	0 },
X#if BRL && defined(TIOCSTI)
X	{x_stuff, 	"stuff",		0, CTRL('T'),	0 },
X	{x_transpose,	"transpose-chars",	0,	 0,	0 },
X#else
X	{x_stuff, 	"stuff",		0,	 0,	0 },
X	{x_transpose,	"transpose-chars",	0, CTRL('T'),	0 },
X#endif
X	{x_complete,	"complete",		1, CTRL('['),	0 },
X	{x_enumerate,	"list",			1,	'?',	0 },
X	{x_comp_file,	"complete-file",	2, CTRL('X'),	0 },
X	{x_comp_comm,	"complete-command",	2, CTRL('['),	0 },
X	{x_list_file,	"list-file",		0,	 0,	0 },
X	{x_list_comm,	"list-command",		2,	'?',	0 },
X#if SILLY
X	{x_game_of_life, "play-game-of-life",	0,	0,	0 },
X#endif 
X#ifdef DEBUG
X        {x_debug_info,	"debug-info",		1, CTRL('H'),	0 },
X#endif
X	{x_prev_histword, "prev-hist-word", 	1,	'.',	0 },
X	{x_prev_histword, "prev-hist-word", 	1,	'_',	0 },
X        {x_set_arg,	"",			1,	'0',	0 },
X        {x_set_arg,	"",			1,	'1',	0 },
X        {x_set_arg,	"",			1,	'2',	0 },
X        {x_set_arg,	"",			1,	'3',	0 },
X        {x_set_arg,	"",			1,	'4',	0 },
X        {x_set_arg,	"",			1,	'5',	0 },
X        {x_set_arg,	"",			1,	'6',	0 },
X        {x_set_arg,	"",			1,	'7',	0 },
X        {x_set_arg,	"",			1,	'8',	0 },
X        {x_set_arg,	"",			1,	'9',	0 },
X        {x_fold_case,	"upcase-word",		1,	'U',	0 },
X        {x_fold_case,	"downcase-word",	1,	'L',	0 },
X        {x_fold_case,	"capitalize-word",	1,	'C',	0 },
X        {x_fold_case,	"upcase-word",		1,	'u',	0 },
X        {x_fold_case,	"downcase-word",	1,	'l',	0 },
X        {x_fold_case,	"capitalize-word",	1,	'c',	0 },
X
X	{ 0 }
X};
X
X#define	xft_insert &x_ftab[0]
X#define	xft_error &x_ftab[1]
X#define	xft_ins_string &x_ftab[2]
X
Xint
Xx_read(fd, buf, len)
X	int fd;			/* not used */
X	char *buf;
X	size_t len;
X{
X	char	c;
X	int	i;
X	int   (*func)();
X	extern	x_insert();
X	
X	(void)set_xmode(TRUE);
X	xbuf = buf; xend = buf + len;
X	xbp = xcp = xep = buf;
X	*xcp = 0;
X	x_curprefix = 0;
X	macroptr = null;
X	x_histp = histptr + 1;
X
X	/* <sjg@sun0> this may not be correct */
X	if ((i = atoi(strval(global("COLUMNS")))) > 0)
X	  x_cols = i;
X	x_col = strlen(prompt);
X	x_adj_ok = 1;
X	x_displen = x_cols - 2 - x_col;
X	x_adj_done = 0;
X	
X	while (1)  {
X		x_flush();
X		if (*macroptr)  {
X			c = *macroptr++;
X			if (*macroptr == 0)
X				macroptr = null;
X		}
X		else {
X			i = read(ttyfd, &c, 1);
X			if (i != 1)
X				goto Exit;
X		}
X
X		if (x_curprefix == -1)
X			func = x_insert;
X		else
X			func = x_tab[x_curprefix][c&CMASK]->xf_func;
X		if (func == NULL)
X			func = x_error;
X		i = c | (x_curprefix << 8);
X		x_curprefix = 0;
X		switch (i = (*func)(i))  {
X		  case KSTD:
X			x_last_command = func;
X		  case KPREF:
X			break;
X		  case KEOL:
X			i = xep - xbuf;
X			x_last_command = 0;
X			/* XXX -- doesn't get them all */
X			if (strncmp(xbuf, "stty", 4) == 0)
X				first_time = 1;
X			goto Exit;
X		  case KINTR:	/* special case for interrupt */
X			i = -1;
X			errno = EINTR;
X			goto Exit;
X		}
X	}
X  Exit:
X	(void)set_xmode(FALSE);
X	if (i < 0 && errno == EINTR)
X		trapsig(SIGINT);
X	return i;
X}
X
Xstatic int
Xx_insert(c)
X  int c;
X{
X	char	str[2];
X
X	/*
X	 *  Should allow tab and control chars.
X	 */
X	if (c == 0)  {
X		x_putc(BEL);
X		return KSTD;
X	}
X	str[0] = c;
X	str[1] = 0;
X	x_ins(str);
X	return KSTD;
X}
X
Xstatic int
Xx_ins_string(c)
X  int c;
X{
X	if (*macroptr)   {
X		x_putc(BEL);
X		return KSTD;
X	}
X	macroptr = x_atab[c>>8][c & CMASK];
X	return KSTD;
X}
X
Xstatic void
Xx_ins(cp)
X	char	*cp;
X{
X	int	count, i;
X	register int	adj = x_adj_done;
X
X	x_adj_ok = (xcp == xep);
X	count = strlen(cp);
X	if (xep+count >= xend) {
X		x_putc(BEL);
X		return;
X	}
X
X	if (xcp != xep)
X		memmove(xcp+count, xcp, xep - xcp + 1);
X	else
X		xcp[count] = 0;
X	memmove(xcp, cp, count);
X#if 1
X	/*
X	 * x_zots() may result in a call to x_adjust()
X	 * we want xcp to reflect the new position.
X	 */
X	cp = xcp;
X	xcp += count;
X	xep += count;
X	x_zots(cp);
X#else
X	x_zots(xcp);
X	xcp += count;
X	xep += count;
X#endif
X	if (adj == x_adj_done)	/* has x_adjust() been called? */
X	{
X	  if ((cp = xbp + x_displen) > xep)
X	    cp = xep;
X	  i = cp - xcp;
X	  while (i-- > 0)
X	    x_bs(*--cp);
X	}
X	x_adj_ok = 1;
X	return;
X}
X
Xstatic int
Xx_del_back(c)
X  int c;
X{
X	if (xcp == xbuf)  {
X		x_putc(BEL);
X		return KSTD;
X	}
X	x_goto(xcp - 1);
X	x_delete(1);
X	return KSTD;
X}
X
Xstatic int
Xx_del_char(c)
X  int c;
X{
X	if (xcp == xep)  {
X		x_putc(BEL);
X		return KSTD;
X	}
X	x_delete(1);
X	return KSTD;
X}
X
Xstatic void
Xx_delete(nc)
X  int nc;
X{
X	int	i,j;
X	char	*cp;
X	
X	if (nc == 0)
X		return;
X#ifdef PUSH_DELETE
X	/*
X	 * This lets us yank a word we have deleted.
X	 */
X	if (nc > 1)
X	  x_push(nc);
X#endif
X	xep -= nc;
X	cp = xcp;
X	j = 0;
X	i = nc;
X	while (i--)  {
X		j += x_size(*cp++);
X	}
X	memmove(xcp, xcp+nc, xep - xcp + 1);	/* Copies the null */
X	x_adj_ok = 0;			/* don't redraw */
X	x_zots(xcp);
X	/*
X	 * if we are already filling the line,
X	 * there is no need to ' ','\b'.
X	 * But if we must, make sure we do the minimum.
X	 */
X	if ((i = x_cols - 2 - x_col) > 0)
X	{
X	  j = (j < i) ? j : i;
X	  i = j;
X	  while (i--)
X	    x_putc(' ');
X	  i = j;
X	  while (i--)
X	    x_putc('\b');
X	}
X	/*x_goto(xcp);*/
X	x_adj_ok = 1;
X	if ((cp = xbp + x_displen) > xep)
X	  cp = xep;
X	i = cp - xcp;
X	while (i-- > 0)
X	  x_bs(*--cp);
X	return;	
X}
X
Xstatic int
Xx_del_bword(c)
X  int c;
X{
X	x_delete(x_bword());
X	return KSTD;
X}
X
Xstatic int
Xx_mv_bword(c)
X  int c;
X{
X	(void)x_bword();
X	return KSTD;
X}
X
Xstatic int
Xx_mv_fword(c)
X  int c;
X{
X	x_goto(xcp + x_fword());
X	return KSTD;
X}
X
Xstatic int
Xx_del_fword(c)
X  int c;
X{
X	x_delete(x_fword());
X	return KSTD;
X}
X
Xstatic int
Xx_bword()  {
X	int	nc = 0;
X	register char *cp = xcp;
X
X	if (cp == xbuf)  {
X		x_putc(BEL);
X		return 0;
X	}
X	if (x_last_command != x_set_arg)
X	  x_arg = 1;
X	while (x_arg--)
X	{
X	  while (cp != xbuf && isfs(cp[-1]))
X	  {
X	    cp--;
X	    nc++;
X	  }
X	  while (cp != xbuf && !isfs(cp[-1]))
X	  {
X	    cp--;
X	    nc++;
X	  }
X	}
X	x_goto(cp);
X	return nc;
X}
X
Xstatic int
Xx_fword()  {
X	int	nc = 0;
X	register char	*cp = xcp;
X
X	if (cp == xep)  {
X		x_putc(BEL);
X		return 0;
X	}
X	if (x_last_command != x_set_arg)
X	  x_arg = 1;
X	while (x_arg--)
X	{
X	  while (cp != xep && !isfs(*cp))
X	  {
X	    cp++;
X	    nc++;
X	  }
X	  while (cp != xep && isfs(*cp))
X	  {
X	    cp++;
X	    nc++;
X	  }
X	}
X	return nc;
X}
X
Xstatic void
Xx_goto(cp)
X	register char *cp;
X{
X  if (cp < xbp || cp >= (xbp + x_displen)) /* off screen */
X  {
X    xcp = cp;
X    x_adjust();
X  }
X  else
X  {
X    if (cp < xcp)		/* move back */
X    {
X      while (cp < xcp)
X	x_bs(*--xcp);
X    }
X    else
X    {
X      if (cp > xcp) 		/* move forward */
X      {
X	while (cp > xcp)
X	  x_zotc(*xcp++);
X      }
X    }
X  }
X}
X
Xstatic void
Xx_bs(c)
X  int c;
X{
X	register i;
X	i = x_size(c);
X	while (i--)
X		x_putc('\b');
X}
X
Xstatic int
Xx_size_str(cp)
X	register char *cp;
X{
X	register size = 0;
X	while (*cp)
X		size += x_size(*cp++);
X	return size;
X}
X
Xstatic int
Xx_size(c)
X  int c;
X{
X	if (c=='\t')
X		return 4;	/* Kludge, tabs are always four spaces. */
X	if (c < ' ' || c == 0x7F) /* ASCII control char */
X		return 2;
X	return 1;
X}
X
Xstatic void
Xx_zots(str)
X	register char *str;
X{
X  register int	adj = x_adj_done;
X  
X  while (*str && x_col < (x_cols - 2) && adj == x_adj_done)
X    x_zotc(*str++);
X}
X
Xstatic void
Xx_zotc(c)
X	int c;
X{
X	if (c == '\t')  {
X		/*  Kludge, tabs are always four spaces.  */
X		x_puts("    ");
X	} else if (c < ' ' || c == 0x7F)  { /* ASCII */
X		x_putc('^');
X		x_putc(UNCTRL(c));
X	} else
X		x_putc(c);
X}
X
X/* tty output */
X
Xstatic void
Xx_flush()
X{
X	fflush(stdout);
X}
X
X/* NAME:
X *      x_adjust - redraw the line adjusting starting point etc.
X *
X * DESCRIPTION:
X *      This function is called when we have exceeded the bounds 
X *      of the edit window.  It increments x_adj_done so that 
X *      functions like x_ins and x_delete know that we have been 
X *      called and can skip the x_bs() stuff which has already 
X *      been done by x_redraw.
X *
X * RETURN VALUE:
X *      None
X */
X
Xx_adjust()
X{
X  x_adj_done++;			/* flag the fact that we were called. */
X  /*
X   * we had a promblem if the prompt length > x_cols / 2
X   */
X  if ((xbp = xcp - (x_displen / 2)) < xbuf)
X    xbp = xbuf;
X
X  x_redraw(x_cols);
X  x_flush();
X}
X
Xstatic void
Xx_putc(c)
X	int c;
X{
X  if (c == '\r' || c == '\n')
X    x_col = 0;
X  if (x_col < x_cols)
X  {
X    putc(c, stdout);
X    switch(c)
X    {
X    case BEL:
X      break;
X    case '\r':
X    case '\n':
X    break;
X    case '\b':
X      x_col--;
X      break;
X    default:
X      x_col++;
X      break;
X    }
X  }
X  if (x_adj_ok && (x_col < 0 || x_col >= (x_cols - 2)))
X  {
X    x_adjust();
X  }
X}
X
X#ifdef DEBUG
Xstatic int
Xx_debug_info()
X{
X  x_flush();
X  printf("\nksh debug:\n");
X  printf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n",
X	 x_col, x_cols, x_displen);
X  printf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep);
X  printf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf);
X  printf("\n");
X  x_redraw(-1);
X  return KSTD;
X}
X#endif
X
Xstatic void
Xx_puts(s)
X	register char *s;
X{
X  register int	adj = x_adj_done;
X
X  while (*s && adj == x_adj_done)
X    x_putc(*s++);
X}
X
Xstatic int
Xx_mv_back(c)
X  int c;
X{
X	if (xcp == xbuf)  {
X		x_putc(BEL);
X		return KSTD;
X	}
X	x_goto(xcp-1);
X	return KSTD;
X}
X
Xstatic int
Xx_mv_forw(c)
X  int c;
X{
X	if (xcp == xep)  {
X		x_putc(BEL);
X		return KSTD;
X	}
X	x_goto(xcp+1);
X	return KSTD;
X}
X
Xstatic int
Xx_search_char(c)
X  int c;
X{
X	char ci, *cp;
X
X	*xep = '\0';
X	if (read(ttyfd, &ci, 1) != 1 ||
X	    /* we search forward, I don't know what Korn does */
X	    (cp = (xcp == xep) ? NULL : strchr(xcp+1, ci)) == NULL &&
X	    (cp = strchr(xbuf, ci)) == NULL) {
X		x_putc(BEL);
X		return KSTD;
X	}
X	x_goto(cp);
X	return KSTD;
X}
X
Xstatic int
Xx_newline(c)
X  int c;
X{
X	x_putc('\n');
X	x_flush();
X	*xep++ = '\n';
X	return KEOL;
X}
X
Xstatic int
Xx_end_of_text(c)
X  int c;
X{
X#if 0
X	x_store_hist();
X#endif
X	return KEOL;
X}
X
Xstatic int x_beg_hist(c) int c; {x_load_hist(history); return KSTD;}
X
Xstatic int x_end_hist(c) int c; {x_load_hist(histptr); return KSTD;}
X
Xstatic int x_prev_com(c) int c; {x_load_hist(x_histp-1); return KSTD;}
X
Xstatic int x_next_com(c) int c; {x_load_hist(x_histp+1); return KSTD;}
X
Xstatic void
Xx_load_hist(hp)
X	register char **hp;
X{
X	int	oldsize;
X
X	if (hp < history || hp > histptr) {
X		x_putc(BEL);
X		return;
X	}
X	x_histp = hp;
X	oldsize = x_size_str(xbuf);
X	(void)strcpy(xbuf, *hp);
X	xbp = xbuf;
X	xep = xcp = xbuf + strlen(*hp);
X	if (xep - xbuf >= x_displen)
X	  x_goto(xep);
X	else
X	  x_redraw(oldsize);
X}
X
Xstatic int x_search(), x_match();
X
X/* reverse incremental history search */
Xstatic int
Xx_search_hist(ci)
X  int ci;
X{
X	int offset = -1;	/* offset of match in xbuf, else -1 */
X	static char c[2];	/* input buffer */
X	char pat [256+1];	/* pattern buffer */
X	register char *p = pat;
X	int (*func)();
X
X	*p = 0;
X	while (1) {
X		if (offset < 0) {
X			x_puts("\nI-search: ");
X			x_zots(pat);
X		}
X		x_flush();
X		if (read(ttyfd, c, 1) < 1)
X			return KSTD;
X		func = x_tab[0][*c&CMASK]->xf_func;
X		if (*c == CTRL('['))
X			break;
X		else if (func == x_search_hist)
X			offset = x_search(pat, offset);
X		else if (func == x_del_back)
X			continue;	/* todo */
X		else if (func == x_insert) {
X			/* add char to pattern */
X			*p++ = *c, *p = 0;
X			if (offset >= 0) {
X				/* already have partial match */
X				offset = x_match(xbuf, pat);
X				if (offset >= 0) {
X					x_goto(xbuf + offset + (p - pat) - (*pat == '^'));
X					continue;
X				}
X			}
X			offset = x_search(pat, offset);
X		} else { /* other command */
X			macroptr = c; /* push command */
X			break;
X		}
X	}
X	if (offset < 0)
X		x_redraw(-1);
X	return KSTD;
X}
X
X/* search backward from current line */
Xstatic int
Xx_search(pat, offset)
X	char *pat;
X	int offset;
X{
X	register char **hp;
X	int i;
X
X	for (hp = x_histp; --hp >= history; ) {
X		i = x_match(*hp, pat);
X		if (i >= 0) {
X			if (offset < 0)
X				x_putc('\n');
X			x_load_hist(hp);
X			x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
X			return i;
X		}
X	}
X	x_putc(BEL);
X	x_histp = histptr;
X	return -1;
X}
X
X/* return position of first match of pattern in string, else -1 */
Xstatic int
Xx_match(str, pat)
X	char *str, *pat;
X{
X	if (*pat == '^') {
X		return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1;
X	} else {
X		char *q = strstr(str, pat);
X		return (q == NULL) ? -1 : q - str;
X	}
X}
X
Xstatic int
Xx_del_line(c)
X  int c;
X{
X	int	i, j;
X
X	*xep = 0;
X	i = xep - xbuf;
X	j = x_size_str(xbuf);
X	xcp = xbuf;
X	x_push(i);
X	xep = xbp = xbuf;
X	*xcp = 0;
X	x_redraw(j);
X	return KSTD;
X}
X
Xstatic int
Xx_mv_end(c)
X  int c;
X{
X	x_goto(xep);
X	return KSTD;
X}
X
Xstatic int
Xx_mv_begin(c)
X  int c;
X{
X	x_goto(xbuf);
X	return KSTD;
X}
X
Xstatic int
Xx_draw_line(c)
X  int c;
X{
X	x_redraw(-1);
X	return KSTD;
X
X}
X
Xstatic void
Xx_redraw(limit)
X  int limit;
X{
X	int	i, j;
X	char	*cp;
X	
X	x_adj_ok = 0;
X	if (limit == -1)
X		x_putc('\n');
X	else 
X		x_putc('\r');
X	x_flush();
X	if (xbp == xbuf)
X	{
X	  pprompt(prompt);
X	  x_col = strlen(prompt);
X	}
X	x_displen = x_cols - 2 - x_col;
X	x_zots(xbp);
X#if 0 /* <sjg@zen> */
X	if (limit >= 0)  {
X		i = limit - x_size_str(xbp);
X		j = 0;
X		while (j < i && x_col < (x_cols - 2))  {
X			x_putc(' ');
X			j++;
X		}
X		while (j--)
X			x_putc('\b');
X	}
X#else
X	if (xep - xbuf >= x_displen || xbp != xbuf)
X	  limit = x_cols;
X	if (limit >= 0)
X	{
X	  i = limit - x_size_str(xbp);
X	  for (j = 0; j < i && x_col < (x_cols - 2);j++)
X	    x_putc(' ');
X	  i = ' ';
X	  if (xep - xbp >= x_displen) /* more off screen */
X	  {
X	    if (xbp > xbuf)
X	      i = '*';
X	    else
X	      i = '>';
X	  }
X	  else
X	    if (xbp > xbuf)
X	      i = '<';
X	  x_putc(i);
X	  j++;
X	  while (j--)
X	    x_putc('\b');
X	}
X#endif
X	if ((cp = xbp + x_displen) > xep)
X	  cp = xep;
X	i = cp - xcp;
X
X	while (i-- > 0)
X		x_bs(*--cp);
X	x_adj_ok = 1;
X	return;
X}
X
Xstatic int
Xx_transpose(c)
X  int c;
X{
X	char	tmp;
X	if (xcp == xbuf || xcp == xep)  {
X		x_putc(BEL);
X		return KSTD;
X	}
X	x_bs(xcp[-1]);
X	x_zotc(xcp[0]);
X	x_zotc(xcp[-1]);
X	tmp = xcp[-1];
X	xcp[-1] = xcp[0];
X	xcp[0] = tmp;
X	x_bs(xcp[0]);
X	return KSTD;
X}
X
Xstatic int
Xx_literal(c)
X  int c;
X{
X	x_curprefix = -1;
X	return KSTD;
X}
X
Xstatic int
Xx_meta1(c)
X  int c;
X{
X	x_curprefix = 1;
X	return KPREF;
X}
X
Xstatic int
Xx_meta2(c)
X  int c;
X{
X	x_curprefix = 2;
X	return KPREF;
X}
X
Xstatic int
Xx_kill(c)
X  int c;
X{
X	int	i;
X
X	i = xep - xcp;
X#ifndef PUSH_DELETE
X	/*
X	 * 91-04-25 <sjg>
X	 * We make x_delete do the push,
X	 * that way we can yank after del-word etc.
X	 */
X	x_push(i);
X#endif
X	x_delete(i);
X	return KSTD;
X}
X
Xstatic void
Xx_push(nchars)
X  int nchars;
X{
X	char	*cp;
X	cp = alloc((size_t)(nchars+1), AEDIT);
X	memmove(cp, xcp, nchars);
X	cp[nchars] = 0;
X	if (killstack[killsp])
X		afree((Void *)killstack[killsp], AEDIT);
X	killstack[killsp] = cp;
X	killsp = (killsp + 1) % KILLSIZE;
X}
X
Xstatic int
Xx_yank(c)
X  int c;
X{
X	if (killsp == 0)
X		killtp = KILLSIZE;
X	else
X		killtp = killsp;
X	killtp --;
X	if (killstack[killtp] == 0)  {
X		x_puts("\nnothing to yank");
X		x_redraw(-1);
X		return KSTD;
X	}
X	x_ins(killstack[killtp]);
X	return KSTD;
X}
X
Xstatic int
Xx_meta_yank(c)
X  int c;
X{
X	int	len;
X	if (x_last_command != x_yank && x_last_command != x_meta_yank)  {
X		x_puts("\nyank something first");
X		x_redraw(-1);
X		return KSTD;
X	}
X	len = strlen(killstack[killtp]);
X	x_goto(xcp - len);
X	x_delete(len);
X	do  {
X		if (killtp == 0)
X			killtp = KILLSIZE - 1;
X		else
X			killtp--;
X	}  while (killstack[killtp] == 0);
X	x_ins(killstack[killtp]);
X	return KSTD;
X}
X
Xstatic int
Xx_abort(c)
X  int c;
X{
X	/* x_zotc(c); */
X	return KINTR;
X}
X
Xstatic int
Xx_error(c)
X  int c;
X{
X	x_putc(BEL);
X	return KSTD;
X}
X
X#if _BSD
X#if defined TIOCGATC
Xstatic struct ttychars lchars, lcharsorig;
X#else
Xstatic struct tchars tchars, tcharsorig;
Xstatic struct ltchars ltchars, ltcharsorig;
X#endif
X#endif
X
X#if _BSD
Xbool_t
Xset_xmode(onoff)
Xbool_t	onoff;
X{
X	bool_t	prev;
Xtypedef	struct sgttyb	Sgttyb;
X	static 	Sgttyb cb;
X	static  Sgttyb orig;
X
X	if (x_mode == onoff) return x_mode;
X	prev = x_mode;
X	x_mode = onoff;
X	if (onoff)  {
X		if (first_time)  {
X			(void)ioctl(0, TIOCGETP, &cb);
X			orig = cb;
X			cb.sg_flags &= ~ECHO;
X			cb.sg_flags |= CBREAK;
X#if defined TIOCGATC
X			(void)ioctl(0, TIOCGATC, &lcharsorig);
X			lchars = lcharsorig;
X			lchars.tc_suspc = -1;
X			lchars.tc_dsuspc = -1;
X			lchars.tc_lnextc = -1;
X			lchars.tc_statc = -1;
X			lchars.tc_intrc = -1;
X			lchars.tc_quitc = -1;
X			lchars.tc_rprntc = -1;
X#else
X			(void)ioctl(0, TIOCGETC, &tcharsorig);
X			(void)ioctl(0, TIOCGLTC, &ltcharsorig);
X			tchars = tcharsorig;
X			ltchars = ltcharsorig;
X			ltchars.t_suspc = -1;
X			ltchars.t_dsuspc = -1;
X			ltchars.t_lnextc = -1;
X			tchars.t_intrc = -1;
X			tchars.t_quitc = -1;
X			ltchars.t_rprntc = -1;
X#endif
X			first_time = 0;
X		}
X		(void)ioctl(0, TIOCSETN, &cb);
X#if defined TIOCGATC
X		(void)ioctl(0, TIOCSATC, &lchars);
X#else
X		(void)ioctl(0, TIOCSETC, &tchars);
X		(void)ioctl(0, TIOCSLTC, &ltchars);
X#endif
X	}
X	else {
X		(void)ioctl(0, TIOCSETN, &orig);
X#if defined TIOCGATC
X		(void)ioctl(0, TIOCSATC, &lcharsorig);
X#else
X		(void)ioctl(0, TIOCSETC, &tcharsorig);
X		(void)ioctl(0, TIOCSLTC, &ltcharsorig);
X#endif
X	}
X	return prev;
X}
X
X#else	/* !_BSD */
X
Xbool_t
Xset_xmode(onoff)
Xbool_t	onoff;
X{
X	bool_t	prev;
X	static	struct termio cb, orig;
X
X	if (x_mode == onoff) return x_mode;
X	prev = x_mode;
X	x_mode = onoff;
X
X	if (onoff)  {
X		if (first_time)  {
X			(void)ioctl(0, TCGETA, &cb);
X			orig = cb;
X#if defined _CRAY2				/* brain-damaged terminal handler */
X			cb.c_lflag &= ~(ICANON|ECHO);
X			/* rely on print routine to map '\n' to CR,LF */
X#else
X			cb.c_iflag &= ~(INLCR|ICRNL);
X#if _BSD_SYSV	/* need to force CBREAK instead of RAW (need CRMOD on output) */
X			cb.c_lflag &= ~(ICANON|ECHO);
X#else
X			cb.c_lflag &= ~(ISIG|ICANON|ECHO);
X#endif
X			cb.c_cc[VTIME] = 0;
X			cb.c_cc[VMIN] = 1;
X#endif	/* _CRAY2 */
X			first_time = 0;
X		}
X#if ! defined TCSETAW				/* e.g. Cray-2 */
X		/* first wait for output to drain */
X#if defined TCSBRK
X		(void)ioctl(0, TCSBRK, 1);
X#else	/* the following kludge is minimally intrusive, but sometimes fails */
X		(void)sleep((unsigned)1);	/* fake it */
X#endif
X#endif
X#if _BSD_SYSV || !defined(TCSETAW)
X/* _BSD_SYSV needs to force TIOCSETN instead of TIOCSETP (preserve type-ahead) */
X		(void)ioctl(0, TCSETA, &cb);
X#else
X		(void)ioctl(0, TCSETAW, &cb);
X#endif
X	}
X	else {
X#if ! defined TCSETAW				/* e.g. Cray-2 */
X		/* first wait for output to drain */
X#if defined TCSBRK
X		(void)ioctl(0, TCSBRK, 1);
X#else
X/* doesn't seem to be necessary when leaving xmode */
X/*		(void)sleep((unsigned)1);	/* fake it */
X#endif
X#endif
X#if _BSD_SYSV || !defined(TCSETAW)
X/* _BSD_SYSV needs to force TIOCSETN instead of TIOCSETP (preserve type-ahead) */
X		(void)ioctl(0, TCSETA, &orig);
X#else
X		(void)ioctl(0, TCSETAW, &orig);
X#endif
X	}
X	return prev;
X}
X#endif	/* _BSD */
X
Xstatic int
Xx_stuffreset(c)
X  int c;
X{
X#if defined TIOCSTI
X	(void)x_stuff(c);
X	return KINTR;
X#else
X	x_zotc(c);
X	xcp = xep = xbp = xbuf;
X	*xcp = 0;
X	x_redraw(-1);
X	return KSTD;
X#endif
X}
X
Xstatic int
Xx_stuff(c)
X  int c;
X{
X#if defined TIOCSTI
X	char	ch = c;
X	bool_t	savmode = set_xmode(FALSE);
X
X	(void)ioctl(0, TIOCSTI, &ch);
X	(void)set_xmode(savmode);
X	x_redraw(-1);
X#endif
X	return KSTD;
X}
X
Xstatic void
Xx_mapin(cp)
X	char	*cp;
X{
X	char	*op;
X
X	op = cp;
X	while (*cp)  {
X		/* XXX -- should handle \^ escape? */
X		if (*cp == '^')  {
X			cp++;
X			if (*cp >= '?')	/* includes '?'; ASCII */
X				*op++ = CTRL(*cp);
X			else  {
X				*op++ = '^';
X				cp--;
X			}
X		} else
X			*op++ = *cp;
X		cp++;
X	}
X	*op = 0;
X}
X
Xstatic char *
Xx_mapout(c)
X	int c;
X{
X	static char buf[8];
X	register char *p = buf;
X
X	if (c < ' ' || c == 0x7F)  { /* ASCII */
X		*p++ = '^';
X		*p++ = (c == 0x7F) ? '?' : (c | 0x40);
X	} else
X		*p++ = c;
X	*p = 0;
X	return buf;
X}
X
Xstatic void
Xx_print(prefix, key)
X	int prefix, key;
X{
X	if (prefix == 1)
X		shellf("%s", x_mapout(x_prefix1));
X	if (prefix == 2)
X		shellf("%s", x_mapout(x_prefix2));
X	shellf("%s = ", x_mapout(key));
X	if (x_tab[prefix][key]->xf_func != x_ins_string)
X		shellf("%s\n", x_tab[prefix][key]->xf_name);
X	else
X		shellf("'%s'\n", x_atab[prefix][key]);
X}
X
Xvoid
Xx_bind(a1, a2, macro)
X	char *a1, *a2;
X	int macro;		/* bind -m */
X{
X	struct x_ftab Const *fp;
X	int prefix, key;
X	char *sp = NULL;
X
X	if (x_tab == NULL)
X		errorf("cannot bind, not a tty\n");
X
X	if (a1 == NULL) {
X		for (prefix = 0; prefix < 3; prefix++)
X		    for (key = 0; key < 0x80; key++) {
X			fp = x_tab[prefix][key];
X			if (fp == NULL ||
X			    fp->xf_func == x_insert || fp->xf_func == x_error)
X				continue;
X			x_print(prefix, key);
X		    }
X		return;
X	}
X
X	x_mapin(a1);
X	prefix = key = 0;
X	for (;; a1++) {
X		key = *a1;
X		if (x_tab[prefix][key]->xf_func == x_meta1)
X			prefix = 1;
X		else
X		if (x_tab[prefix][key]->xf_func == x_meta2)
X			prefix = 2;
X		else
X			break;
X	}
X
X	if (a2 == NULL) {
X		x_print(prefix, key);
X		return;
X	}
X
X	if (*a2 == 0)
X		fp = xft_insert;
X	else if (!macro) {
X		for (fp = x_ftab; fp->xf_func; fp++)
X			if (strcmp(fp->xf_name, a2) == 0)
X				break;
X		if (fp->xf_func == NULL || (fp->xf_flags & XF_NOBIND))
X			errorf("%s: no such function\n", a2);
X		if (fp->xf_func == x_meta1)
X			x_prefix1 = key;
X		if (fp->xf_func == x_meta2)
X			x_prefix2 = key;
X	} else {
X		fp = xft_ins_string;
X		x_mapin(a2);
X		sp = strsave(a2, AEDIT);
X	}
X
X	if ((x_tab[prefix][key]->xf_flags & XF_ALLOC) && x_atab[prefix][key])
X		afree((Void *)x_atab[prefix][key], AEDIT);
X	x_tab[prefix][key] = fp;
X	x_atab[prefix][key] = sp;
X}
X
Xvoid
Xx_init()
X{
X	register int i, j;
X	struct x_ftab Const *fp;
X
X	ainit(AEDIT);
X
X	x_tab = alloc(sizeofN(*x_tab, 3), AEDIT);
X	for (j = 0; j < 128; j++)
X		x_tab[0][j] = xft_insert;
X	for (i = 1; i < 3; i++)
X		for (j = 0; j < 128; j++)
X			x_tab[i][j] = xft_error;
X	for (fp = x_ftab; fp->xf_func; fp++)
X		if (fp->xf_db_char || fp->xf_db_tab)
X			x_tab[fp->xf_db_tab][fp->xf_db_char] = fp;
X
X	x_atab = alloc(sizeofN(*x_atab, 3), AEDIT);
X	for (i = 1; i < 3; i++)
X		for (j = 0; j < 128; j++)
X			x_atab[i][j] = NULL;
X}
X
X#if SILLY
Xstatic int
Xx_game_of_life(c)  {
X	char	newbuf [256+1];
X	register char *ip, *op;
X	int	i, len;
X
X	i = xep - xbuf;
X	*xep = 0;
X	len = x_size_str(xbuf);
X	xcp = xbp = xbuf;
X	memmove(newbuf+1, xbuf, i);
X	newbuf[0] = 'A';
X	newbuf[i] = 'A';
X	for (ip = newbuf+1, op = xbuf; --i >= 0; ip++, op++)  {
X		/*  Empty space  */
X		if (*ip < '@' || *ip == '_' || *ip == 0x7F)  {
X			/*  Two adults, make whoopee */
X			if (ip[-1] < '_' && ip[1] < '_')  {
X				/*  Make kid look like parents.  */
X				*op = '`' + ((ip[-1] + ip[1])/2)%32;
X				if (*op == 0x7F) /* Birth defect */
X					*op = '`';
X			}
X			else
X				*op = ' ';	/* nothing happens */
X			continue;
X		}
X		/*  Child */
X		if (*ip > '`')  {
X			/*  All alone, dies  */
X			if (ip[-1] == ' ' && ip[1] == ' ')
X				*op = ' ';
X			else	/*  Gets older */
X				*op = *ip-'`'+'@';
X			continue;
X		}
X		/*  Adult  */
X		/*  Overcrowded, dies */
X		if (ip[-1] >= '@' && ip[1] >= '@')  {
X			*op = ' ';
X			continue;
X		}
X		*op = *ip;
X	}
X	*op = 0;
X	x_redraw(len);
X	return KSTD;
X}
X#endif
X
X/*
X *	File/command name completion routines
X */
X
X/* type: 0 for list, 1 for completion */
X
Xstatic	XPtrV words;
X
Xstatic void
Xadd_stash(dirnam, name)
X	char *dirnam;	/* directory name, if file */
X	char *name;
X{
X	char *cp;
X	register int type = 0;	/* '*' if executable, '/' if directory, else 0 */
X	register int len = strlen(name);
X
X	/* determine file type */
X	if (dirnam)  {
X		struct stat statb;
X		char *buf = alloc((size_t)(strlen(dirnam)+len+2), ATEMP);
X
X		if (strcmp(dirnam, ".") == 0)
X			*buf = '\0';
X		else if (strcmp(dirnam, "/") == 0)
X			(void)strcpy(buf, "/");
X		else
X			(void)strcat(strcpy(buf, dirnam), "/");
X		(void)strcat(buf, name);
X		if (stat(buf, &statb)==0)
X			if (S_ISDIR(statb.st_mode))
X				type = '/';
X			else if (S_ISREG(statb.st_mode) && access(buf, 01)==0)
X				type = '*';
X		if (type)
X			++len;
X		afree((Void *)buf, ATEMP);
X	}
X
X	if (len > x_maxlen)
X		x_maxlen = len;
X
X	/* stash name for later sorting */
X	cp = alloc((size_t)(len+1), ATEMP);
X	(void)strcpy(cp = alloc((size_t)(len+1), ATEMP), name);
X	if (dirnam && type)  {	/* append file type indicator */
X		cp[len-1] = type;
X		cp[len] = '\0';
X	}
X	XPput(words, cp);
X}
X
Xstatic void
Xlist_stash()
X{
X	register char **array, **record;
X	int items = 0, tabstop, loc, nrows, jump, offset;
X
X	items = XPsize(words);
X	array = (char**) XPptrv(words);
X	if (items == 0)
X		return;
X	qsortp(XPptrv(words), (size_t)XPsize(words), xstrcmp);
X
X	/* print names */
X	x_maxlen = (x_maxlen/8 + 1) * 8;	/* column width */
X	nrows = (items-1) / (x_cols/x_maxlen) + 1;
X	for (offset = 0; offset < nrows; ++offset)  {
X		tabstop = loc = 0;
X		x_putc('\n');
X		for (jump = 0; offset+jump < items; jump += nrows)  {
X			if (jump)
X				while (loc < tabstop)  {
X					x_putc('\t');
X					loc = (loc/8 + 1) * 8;
X				}
X			record = array + (offset + jump);
X			x_puts(*record);
X			afree((Void *)*record, ATEMP);
X			loc += strlen(*record);
X			tabstop += x_maxlen;	/* next tab stop */
X		}
X	}
X
X	afree((Void*)array, ATEMP);
X	x_redraw(-1);
X}
X
Xstatic int
Xx_comp_comm(c)
X  int c;
X{
X	compl_command(1);
X	return KSTD;
X}
Xstatic int
Xx_list_comm(c)
X  int c;
X{
X	compl_command(0);
X	return KSTD;
X}
Xstatic int
Xx_complete(c)
X  int c;
X{
X	compl_dec(1);
X	return KSTD;
X}
Xstatic int
Xx_enumerate(c)
X  int c;
X{
X	compl_dec(0);
X	return KSTD;
X}
Xstatic int
Xx_comp_file(c)
X  int c;
X{
X	compl_file(1);
X	return KSTD;
X}
Xstatic int
Xx_list_file(c)
X  int c;
X{
X	compl_file(0);
X	return KSTD;
X}
X
Xstatic void
Xcompl_dec(type)
X  int type;
X{
X	char	*cp;
X	cp = xcp;
X
X	while (cp != xbuf && !ISFS(*cp))
X		cp--;
X	if (cp == xbuf && strchr(cp, '/') == NULL)
X		compl_command(type);
X	else
X		compl_file(type);
X}
X
Xstatic void
Xcompl_file(type)
X  int type;
X{
X	char	*str;
X	register char *cp, *xp;
X	char	*lastp;
X	char	*dirnam;
X	char	buf [256+1];
X	char	bug [256+1];
X	DIR    *dirp;
X	struct dirent *dp;
X	long	loc = -1;
X	int	len;
X	int	multi = 0;
X
X	str = xcp;
X	cp = buf;
X	xp = str;
X	while (xp != xbuf)  {
X		--xp;
X		if (ISFS(*xp))  {
X			xp++;
X			break;
X		}
X	}
X	if (digit(*xp) && (xp[1] == '<' || xp[1] == '>'))
X		xp++;
X	while (*xp == '<' || *xp == '>')
X		xp++;
X	if (type)
X		while (*xcp && !ISFS(*xcp))
X			x_zotc(*xcp++);
X	else {
X		x_maxlen = 0;
X		XPinit(words, 16);
X	}
X	while (*xp && !ISFS(*xp))
X		*cp++ = *xp++;
X
X	*cp = 0;
X	strcpy(buf, cp = substitute(buf, DOTILDE));
X	afree((Void*)cp, ATEMP);
X	lastp = strrchr(buf, '/');
X	if (lastp)
X		*lastp = 0;
X
X	dirnam = (lastp == NULL) ? "." : (lastp == buf) ? "/" : buf;
X	dirp = opendir(dirnam);
X	if (dirp == NULL) {
X		x_putc(BEL);
X		return;
X	}
X
X	if (lastp == NULL)
X		lastp = buf;
X	else
X		lastp++;
X	len = strlen(lastp);
X
X	while ((dp = readdir(dirp)) != NULL)  {
X		cp = dp->d_name;
X		if (cp[0] == '.' &&
X		    (cp[1] == '\0' || cp[1] == '.' && cp[2] == '\0'))
X			continue; /* always ignore . and .. */
X		if (strncmp(lastp, cp, len) == 0)
X			if (type)  {
X				if (loc == -1)  {
X					(void)strcpy(bug, cp);
X					loc = strlen(cp);
X				} else {
X					multi = 1;
X					loc = strmatch(bug, cp);
X					bug[loc] = 0;
X				}
X			} else
X				add_stash(dirnam, cp);
X	}
X	(void)closedir(dirp);
X
X	if (type) {
X		if (loc <= 0)  {
X			x_putc(BEL);
X			return;
X		}
X		cp = bug + len;
X		x_ins(cp);
X		if (!multi)  {
X			struct stat statb;
X			if (lastp == buf)
X				buf[0] = 0;
X			else if (lastp == buf + 1)  {
X				buf[1] = 0;
X				buf[0] = '/';
X			}  else
X				(void)strcat(buf, "/");
X			(void)strcat(buf, bug);
X			if (stat(buf, &statb) == 0 && S_ISDIR(statb.st_mode))
X				x_ins("/");
X			else
X				x_ins(" ");
X		}
X	} else
X		list_stash();
X}
X
Xstatic void
Xcompl_command(type)
X  int type;
X{
X	register struct tbl *tp;
X	char	*str;
X	char	buf [256+1];
X	char	bug [256+1];
X	char	*xp;
X	char	*cp;
X	int  len;
X	int  multi;
X	int  loc;
X
X	str = xcp;
X	cp = buf;
X	xp = str;
X	while (xp != xbuf)  {
X		--xp;
X		if (ISFS(*xp))  {
X			xp++;
X			break;
X		}
X	}
X	if (type)
X		while (*xcp && !ISFS(*xcp))
X			x_zotc(*xcp++);
X	else {
X		x_maxlen = 0;
X		XPinit(words, 16);
X	}
X	while (*xp && !ISFS(*xp))
X		*cp++ = *xp++;
X	*cp = 0;
X
X	len = strlen(buf);
X	loc = -1;
X	multi = 0;
X
X	for (twalk(&commands); (tp = tnext()) != NULL; ) {
X		int	klen;
X
X		if (!(tp->flag&ISSET))
X			continue;
X		klen = strlen(tp->name);
X		if (klen < len)
X			return;
X		if (strncmp(buf, tp->name, len) ==0)
X			if (type)  {
X				if (loc == -1)  {
X					(void)strcpy(bug, tp->name);
X					loc = klen;
X				} else {
X					multi = 1;
X					loc = strmatch(bug, tp->name);
X					bug[loc] = 0;
X				}
X			} else
X				add_stash((char *)0, tp->name);
X	}
X
X	if (type)  {
X		if (loc <= 0)  {
X			x_putc(BEL);
X			return;
X		}
X		cp = bug + len;
X		x_ins(cp);
X		if (!multi)
X			x_ins(" ");
X	} else
X		list_stash();
X}
X
Xstatic int
Xstrmatch(s1, s2)
X	register char *s1, *s2;
X{
X	register char *p;
X
X	for (p = s1; *p == *s2++ && *p != 0; p++)
X		;
X	return p - s1;
X}
X
X
X/* NAME:
X *      x_set_arg - set an arg value for next function
X *
X * DESCRIPTION:
X *      This is a simple implementation of M-[0-9].
X *
X * RETURN VALUE:
X *      KSTD
X */
X
Xstatic int
Xx_set_arg(c)
X  int c;
X{
X  if ((x_arg = (c &= CMASK) - '0') < 0 || x_arg > 9)
X  {
X    x_arg = 1;
X    x_putc(BEL);
X  }
X  return KSTD;
X}
X
X
X/* NAME:
X *      x_prev_histword - recover word from prev command
X *
X * DESCRIPTION:
X *      This function recovers the last word from the previous 
X *      command and inserts it into the current edit line.  If a 
X *      numeric arg is supplied then the n'th word from the 
X *      start of the previous command is used.  
X *      
X *      Bound to M-.
X *
X * RETURN VALUE:
X *      KSTD
X */
X
Xstatic int
Xx_prev_histword()
X{
X  register char *rcp;
X  char *cp;
X  char **hp;
X
X  hp = x_histp-1;
X  if (hp < history || hp > histptr)
X  {
X    x_putc(BEL);
X    return;
X  }
X  cp = *hp;
X  if (x_last_command != x_set_arg)
X  {
X    rcp = &cp[strlen(cp) - 1];
X    /*
X     * ignore white-space after the last word
X     */
X    while (rcp > cp && ISFS(*rcp))
X      rcp--;
X    while (rcp > cp && !ISFS(*rcp))
X      rcp--;
X    if (ISFS(*rcp))
X      rcp++;
X    x_ins(rcp);
X  }
X  else
X  {
X    int c;
X    
X    rcp = cp;
X    /*
X     * ignore white-space at start of line
X     */
X    while (*rcp && ISFS(*rcp))
X      rcp++;
X    while (x_arg-- > 1)
X    {
X      while (*rcp && !ISFS(*rcp))
X	rcp++;
X      while (*rcp && ISFS(*rcp))
X	rcp++;
X    }
X    cp = rcp;
X    while (*rcp && !ISFS(*rcp))
X      rcp++;
X    c = *rcp;
X    *rcp = '\0';
X    x_ins(cp);
X    *rcp = c;
X  }
X  return KSTD;
X}
X
X/* NAME:
X *      x_fold_case - convert word to UPPER/lower case
X *
X * DESCRIPTION:
X *      This function is used to implement M-u,M-l and M-c
X *      to upper case, lower case or Capitalize words.
X *
X * RETURN VALUE:
X *      None
X */
X
Xstatic int
Xx_fold_case(c)
X  int c;
X{
X  register char	*cp = xcp;
X  
X  if (cp == xep)
X  {
X    x_putc(BEL);
X    return 0;
X  }
X  c &= 0137;				/* strip prefixes and case */
X  if (x_last_command != x_set_arg)
X    x_arg = 1;
X  while (x_arg--)
X  {
X    /*
X     * fisrt skip over any white-space
X     */
X    while (cp != xep && isfs(*cp))
X    {
X      cp++;
X    }
X    /*
X     * do the first char on its own since it may be
X     * a different action than for the rest.
X     */
X    if (cp != xep)
X    {
X      if (c == 'L')			/* M-l */
X      {
X	if (isupper(*cp))
X	  *cp = tolower(*cp);
X      }
X      else				/* M-u or M-c */
X      {
X	if (islower(*cp))
X	  *cp = toupper(*cp);
X      }
X      cp++;
X    }
X    /*
X     * now for the rest of the word
X     */
X    while (cp != xep && !isfs(*cp))
X    {
X      if (c == 'U')			/* M-u */
X      {
X	if (islower(*cp))
X	  *cp = toupper(*cp);
X      }
X      else				/* M-l or M-c */
X      {
X	if (isupper(*cp))
X	  *cp = tolower(*cp);
X      }
X      cp++;
X    }
X  }
X  x_goto(cp);
X  return 0;
X}
X
X#endif /* EDIT */
END_OF_src/edit.c
if test 36696 -ne `wc -c <src/edit.c`; then
    echo shar: \"src/edit.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0
-- 
Simon J. Gerraty        <sjg@zen.void.oz.au>

#include <disclaimer>   /* imagine something _very_ witty here */