Return-Path: osf.org!rsalz
Received: from postman.osf.org ([130.105.1.152]) by hawkwind.utcs.toronto.edu with SMTP id <2770>; Mon, 26 Oct 1992 16:59:19 -0500
Received: from earth.osf.org by postman.osf.org (5.64+/OSF 1.0)
	id AA12344; Mon, 26 Oct 92 16:59:05 -0500
Received: by earth.osf.org (5.64/4.7) id AA01084; Mon, 26 Oct 92 16:59:01 -0500
Date:	Mon, 26 Oct 1992 16:59:01 -0500
From:	rsalz@osf.org
Message-Id: <9210262159.AA01084@earth.osf.org>
To:	cks@hawkwind.utcs.toronto.edu
Subject: Re: Revised editline

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  README Makefile editline.3 editline.h unix.h editline.c
#   complete.c sysunix.c testit.c Make.os9 os9.h sysos9.c
# Wrapped by rsalz@earth on Mon Oct 26 11:17:04 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive."'
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
  echo shar: Extracting \"'README'\" \(1934 characters\)
  sed "s/^X//" >'README' <<'END_OF_FILE'
X$Revision: 1.5 $
X
XThis is a line-editing library.  It can be linked into almost any
Xprogram to provide command-line editing and recall.
X
XIt is call-compatible with the FSF readline library, but it is a
Xfraction of the size (and offers fewer features).  It does not use
Xstandard I/O.  It is distributed under a "C News-like" copyright.
X
XConfiguration is done in the Makefile.  Type "make testit" to get
Xa small slow shell for testing.
X
XAn earlier version was distributed with Byron's rc.  Principal
Xchanges over that version include:
X	Faster.
X	Is eight-bit clean (thanks to brendan@cs.widener.edu)
X	Written in K&R C, but ANSI compliant (gcc all warnings)
X	Propagates EOF properly; rc trip test now passes
X	Doesn't need or use or provide memmove.
X	More robust
X	Calling sequence changed to be compatible with readline.
X	Test program, new manpage, better configuration
X	More system-independant; includes Unix and OS-9 support.
X
XEnjoy,
X	Rich $alz
X	<rsalz@osf.org>
X
X Copyright 1992 Simmule Turner and Rich Salz.  All rights reserved.
X
X This software is not subject to any license of the American Telephone
X and Telegraph Company or of the Regents of the University of California.
X
X Permission is granted to anyone to use this software for any purpose on
X any computer system, and to alter it and redistribute it freely, subject
X to the following restrictions:
X 1. The authors are not responsible for the consequences of use of this
X    software, no matter how awful, even if they arise from flaws in it.
X 2. The origin of this software must not be misrepresented, either by
X    explicit claim or by omission.  Since few users ever read sources,
X    credits must appear in the documentation.
X 3. Altered versions must be plainly marked as such, and must not be
X    misrepresented as being the original software.  Since few users
X    ever read sources, credits must appear in the documentation.
X 4. This notice may not be removed or altered.
END_OF_FILE
  if test 1934 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
  fi
  # end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
  echo shar: Extracting \"'Makefile'\" \(1836 characters\)
  sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X##  $Revision: 1.3 $
X##
X##  Unix makefile for editline library.
X##
X
X##  Set your options:
X##	-DANSI_ARROWS		ANSI arrows keys work like emacs.
X##	-DHAVE_STDLIB		Have <stdlib.h>.
X##	-DHAVE_TCGETATTR	Have tcgetattr(), tcsetattr().
X##	-DHIDE			Make static functions static (non debug).
X##	-DHIST_SIZE=n		History size.
X##	-DNEED_STRDUP		Don't have strdup().
X##	-DUNIQUE_HISTORY	Don't save command if same as last one.
X##	-DUSE_DIRENT		Use <dirent.h>, not <sys/dir.h>?
X##	-DUSE_TERMCAP		Use the termcap library for terminal size
X##				see LDFLAGS, below, if you set this.
X##	-DNEED_PERROR		Don't have perror() (used in testit)
XDEFS	= -DANSI_ARROWS -DHAVE_TCGETATTR -DHIDE -DUSE_DIRENT -DSYS_UNIX
X
X##  Set your C compiler:
XWARN	= -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wwrite-strings \
X	-Wunused -Wcomment -Wswitch
XCC	= gcc -ansi $(WARN)
X#CC	= cc
XCFLAGS	= $(DEFS) -O -g
X
X##  If you have -DUSE_TERMCAP, set this as appropriate:
X#LDFLAGS = -ltermlib
X#LDFLAGS = -ltermcap
X
X##  Set ranlib as appropriate:
XRANLIB	= ranlib
X#RANLIB	= echo
X
X##  End of configuration.
X
XSOURCES	= editline.c complete.c sysunix.c
XOBJECTS	= editline.o complete.o sysunix.o
XSHARFILES =	README Makefile editline.3 editline.h unix.h editline.c \
X		complete.c sysunix.c testit.c \
X		Make.os9 os9.h sysos9.c
X
Xall:		libedit.a
X
Xtestit:		testit.c libedit.a
X	$(CC) $(CFLAGS) -o testit testit.c libedit.a $(LDFLAGS)
X
Xshar:		$(SHARFILES)
X	shar $(SHARFILES) >shar
X
Xclean:
X	rm -f *.[oa] testit foo core tags lint lint.all a.out shar
X
Xlint:		testit
X	lint -a -b -u -x $(DEFS) $(SOURCES) testit.c >lint.all
X	sed -e '/warning: function prototype not in scope/d' \
X		-e '/warning: old style argument declaration/'d \
X		-e '/mix of old and new style function declaration/'d \
X		<lint.all >lint
X
Xlibedit.a:	$(OBJECTS)
X	@rm -f $@
X	ar r $@ $(OBJECTS)
X	$(RANLIB) $@
X
X$(OBJECTS):	editline.h
END_OF_FILE
  if test 1836 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
  fi
  # end of 'Makefile'
fi
if test -f 'editline.3' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'editline.3'\"
else
  echo shar: Extracting \"'editline.3'\" \(5281 characters\)
  sed "s/^X//" >'editline.3' <<'END_OF_FILE'
X.\" $Revision: 1.1 $
X.TH EDITLINE 3
X.SH NAME
Xeditline \- command-line editing library with history
X.SH SYNOPSIS
X.nf
X.B "char *"
X.B "readline(prompt)"
X.B "     char	*prompt;"
X
X.B "void"
X.B "add_history(line)"
X.B "    char	*line;"
X.fi
X.SH DESCRIPTION
X.I Editline
Xis a library that provides an line-editing interface with text recall.
XIt is intended to be compatible with the
X.I readline
Xlibrary provided by the Free Software Foundation, but much smaller.
XThe bulk of this manual page describes the user interface.
X.PP
XThe
X.I readline
Xroutine returns a line of text with the trailing newline removed.
XThe data is returned in a buffer allocated with
X.IR malloc (3),
Xso the space should be released with
X.IR free (3)
Xwhen the calling program is done with it.
XBefore accepting input from the user, the specified
X.I prompt
Xis displayed on the terminal.
X.PP
XThe
X.I add_history
Xroutine makes a copy of the specified
X.I line
Xand adds it to the internal history list.
X.SS "User Interface"
XA program that uses this library provides a simple emacs-like editing
Xinterface to its users.
XA line may be edited before it is sent to the calling program by typing either
Xcontrol characters or escape sequences.
XA control character, shown as a caret followed by a letter, is typed by
Xholding down the ``control'' key while the letter is typed.
XFor example, ``^A'' is a control-A.
XAn escape sequence is entered by typing the ``escape'' key followed by one or
Xmore characters.
XThe escape key is abbreviated as ``ESC.''
XNote that unlike control keys, case matters in escape sequences; ``ESC\ F''
Xis not the same as ``ESC\ f''.
X.PP
XAn editing command may be typed anywhere on the line, not just at the
Xbeginning.
XIn addition, a return may also be typed anywhere on the line, not just at
Xthe end.
X.PP
XMost editing commands may be given a repeat count,
X.IR n ,
Xwhere
X.I n
Xis a number.
XTo enter a repeat count, type the escape key, the number, and then
Xthe command to execute.
XFor example, ``ESC\ 4\ ^f'' moves forward four characters.
XIf a command may be given a repeat count then the text ``[n]'' is given at the
Xend of its description.
X.PP
XThe following control characters are accepted:
X.RS
X.nf
X.ta \w'ESC DEL  'u
X^A	Move to the beginning of the line
X^B	Move left (backwards) [n]
X^D	Delete character [n]
X^E	Move to end of line
X^F	Move right (forwards) [n]
X^G	Ring the bell
X^H	Delete character before cursor (backspace key) [n]
X^I	Complete filename (tab key); see below
X^J	Done with line (return key)
X^K	Kill to end of line (or column [n])
X^L	Redisplay line
X^M	Done with line (alternate return key)
X^N	Get next line from history [n]
X^P	Get previous line from history [n]
X^R	Search backward (forward if [n]) through history for text;
X\&	must start line if text begins with an uparrow
X^T	Transpose characters
X^V	Insert next character, even if it is an edit command
X^W	Wipe to the mark
X^X^X	Exchange current location and mark
X^Y	Yank back last killed text
X^[	Start an escape sequence (escape key)
X^]c	Move forward to next character ``c''
X^?	Delete character before cursor (delete key) [n]
X.fi
X.RE
X.PP
XThe following escape sequences are provided.
X.RS
X.nf
X.ta \w'ESC DEL  'u
XESC\ ^H	Delete previous word (backspace key) [n]
XESC\ DEL	Delete previous word (delete key) [n]
XESC\ SP	Set the mark (space key); see ^X^X and ^Y above
XESC\ \.	Get the last (or [n]'th) word from previous line
XESC\ \?	Show possible completions; see below
XESC\ <	Move to start of history
XESC\ >	Move to end of history
XESC\ b	Move backward a word [n]
XESC\ d	Delete word under cursor [n]
XESC\ f	Move forward a word [n]
XESC\ l	Make word lowercase [n]
XESC\ u	Make word uppercase [n]
XESC\ y	Yank back last killed text
XESC\ v	Show library version
XESC\ w	Make area up to mark yankable
XESC\ nn	Set repeat count to the number nn
XESC\ C	Read from environment variable ``_C_'', where C is
X\&	an uppercase letter
X.fi
X.RE
X.PP
XThe
X.I editline
Xlibrary has a small macro facility.
XIf you type the escape key followed by an uppercase letter,
X.IR C ,
Xthen the contents of the environment variable
X.I _C_
Xare read in as if you had typed them at the keyboard.
XFor example, if the variable
X.I _L_
Xcontains the following:
X.RS
X^A^Kecho '^V^[[H^V^[[2J'^M
X.RE
XThen typing ``ESC L'' will move to the beginning of the line, kill the
Xentire line, enter the echo command needed to clear the terminal (if your
Xterminal is like a VT-100), and send the line back to the shell.
X.PP
XThe
X.I editline
Xlibrary also does filename completion.
XSuppose the root directory has the following files in it:
X.RS
X.nf
X.ta \w'core   'u
Xbin	vmunix
Xcore	vmunix.old
X.fi
X.RE
XIf you type ``rm\ /v'' and then the tab key.
X.I Editline
Xwill then finish off as much of the name as possible by adding ``munix''.
XBecause the name is not unique, it will then beep.
XIf you type the escape key and a question mark, it will display the
Xtwo choices.
XIf you then type a period and a tab, the library will finish off the filename
Xfor you:
X.RS
X.nf
X.RI "rm /v[TAB]" munix .TAB old
X.fi
X.RE
XThe tab key is shown by ``[TAB]'' and the automatically-entered text
Xis shown in italics.
X.SH "BUGS AND LIMITATIONS"
XCannot handle lines more than 80 columns.
X.SH AUTHORS
XSimmule R. Turner <uunet.uu.net!capitol!sysgo!simmy>
Xand Rich $alz <rsalz@osf.org>.
XOriginal manual page by DaviD W. Sanderson <dws@ssec.wisc.edu>.
END_OF_FILE
  if test 5281 -ne `wc -c <'editline.3'`; then
    echo shar: \"'editline.3'\" unpacked with wrong size!
  fi
  # end of 'editline.3'
fi
if test -f 'editline.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'editline.h'\"
else
  echo shar: Extracting \"'editline.h'\" \(1622 characters\)
  sed "s/^X//" >'editline.h' <<'END_OF_FILE'
X/*  $Revision: 1.3 $
X**
X**  Internal header file for editline library.
X*/
X#include <stdio.h>
X#if	defined(HAVE_STDLIB)
X#include <stdlib.h>
X#include <string.h>
X#endif	/* defined(HAVE_STDLIB) */
X#if	defined(SYS_UNIX)
X#include "unix.h"
X#endif	/* defined(SYS_UNIX) */
X#if	defined(SYS_OS9)
X#include "os9.h"
X#endif	/* defined(SYS_OS9) */
X
X#if	!defined(SIZE_T)
X#define SIZE_T	unsigned int
X#endif	/* !defined(SIZE_T) */
X
Xtypedef unsigned char	CHAR;
X
X#if	defined(HIDE)
X#define STATIC	static
X#else
X#define STATIC	/* NULL */
X#endif	/* !defined(HIDE) */
X
X#if	!defined(CONST)
X#if	defined(__STDC__)
X#define CONST	const
X#else
X#define CONST
X#endif	/* defined(__STDC__) */
X#endif	/* !defined(CONST) */
X
X
X#define MEM_INC		64
X#define SCREEN_INC	256
X
X#define DISPOSE(p)	free((char *)(p))
X#define NEW(T, c)	\
X	((T *)malloc((unsigned int)(sizeof (T) * (c))))
X#define RENEW(p, T, c)	\
X	(p = (T *)realloc((char *)(p), (unsigned int)(sizeof (T) * (c))))
X#define COPYFROMTO(new, p, len)	\
X	(void)memcpy((char *)(new), (char *)(p), (int)(len))
X
X
X/*
X**  Variables and routines internal to this package.
X*/
Xextern int	rl_eof;
Xextern int	rl_erase;
Xextern int	rl_intr;
Xextern int	rl_kill;
Xextern int	rl_quit;
Xextern char	*rl_complete();
Xextern int	rl_list_possib();
Xextern void	rl_ttyset();
Xextern void	rl_add_slash();
X
X#if	!defined(HAVE_STDLIB)
Xextern char	*getenv();
Xextern char	*malloc();
Xextern char	*realloc();
Xextern char	*memcpy();
Xextern char	*strcat();
Xextern char	*strchr();
Xextern char	*strrchr();
Xextern char	*strcpy();
Xextern char	*strdup();
Xextern int	strcmp();
Xextern int	strlen();
Xextern int	strncmp();
X#endif	/* !defined(HAVE_STDLIB) */
END_OF_FILE
  if test 1622 -ne `wc -c <'editline.h'`; then
    echo shar: \"'editline.h'\" unpacked with wrong size!
  fi
  # end of 'editline.h'
fi
if test -f 'unix.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'unix.h'\"
else
  echo shar: Extracting \"'unix.h'\" \(432 characters\)
  sed "s/^X//" >'unix.h' <<'END_OF_FILE'
X/*  $Revision: 1.1 $
X**
X**  Editline system header file for Unix.
X*/
X
X#define CRLF		"\r\n"
X#define FORWARD		STATIC
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#if	defined(USE_DIRENT)
X#include <dirent.h>
Xtypedef struct dirent	DIRENTRY;
X#else
X#include <sys/dir.h>
Xtypedef struct direct	DIRENTRY;
X#endif	/* defined(USE_DIRENT) */
X
X#if	!defined(S_ISDIR)
X#define S_ISDIR(m)		(((m) & S_IFMT) == S_IFDIR)
X#endif	/* !defined(S_ISDIR) */
END_OF_FILE
  if test 432 -ne `wc -c <'unix.h'`; then
    echo shar: \"'unix.h'\" unpacked with wrong size!
  fi
  # end of 'unix.h'
fi
if test -f 'editline.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'editline.c'\"
else
  echo shar: Extracting \"'editline.c'\" \(22788 characters\)
  sed "s/^X//" >'editline.c' <<'END_OF_FILE'
X/*  $Revision: 1.4 $
X**
X**  Main editing routines for editline library.
X*/
X#include "editline.h"
X#include <signal.h>
X#include <ctype.h>
X
X/*
X**  Manifest constants.
X*/
X#define SCREEN_WIDTH	80
X#define SCREEN_ROWS	24
X#define NO_ARG		(-1)
X#define DEL		127
X#define CTL(x)		((x) & 0x1F)
X#define ISCTL(x)	((x) && (x) < ' ')
X#define UNCTL(x)	((x) + 64)
X#define META(x)		((x) | 0x80)
X#define ISMETA(x)	((x) & 0x80)
X#define UNMETA(x)	((x) & 0x7F)
X#if	!defined(HIST_SIZE)
X#define HIST_SIZE	20
X#endif	/* !defined(HIST_SIZE) */
X
X/*
X**  Command status codes.
X*/
Xtypedef enum _STATUS {
X    CSdone, CSeof, CSmove, CSdispatch, CSstay,
X} STATUS;
X
X/*
X**  The type of case-changing to perform.
X*/
Xtypedef enum _CASE {
X    TOupper, TOlower
X} CASE;
X
X/*
X**  Key to command mapping.
X*/
Xtypedef struct _KEYMAP {
X    CHAR	Key;
X    STATUS	(*Function)();
X} KEYMAP;
X
X/*
X**  Command history structure.
X*/
Xtypedef struct _HISTORY {
X    int		Size;
X    int		Pos;
X    CHAR	*Lines[HIST_SIZE];
X} HISTORY;
X
X/*
X**  Globals.
X*/
Xint		rl_eof;
Xint		rl_erase;
Xint		rl_intr;
Xint		rl_kill;
Xint		rl_quit;
X
XSTATIC CHAR		NIL[] = "";
XSTATIC CONST CHAR	*Input = NIL;
XSTATIC CHAR		*Line;
XSTATIC CONST char	*Prompt;
XSTATIC CHAR		*Yanked;
XSTATIC char		*Screen;
XSTATIC char		NEWLINE[]= CRLF;
XSTATIC HISTORY		H;
XSTATIC int		Repeat;
XSTATIC int		End;
XSTATIC int		Mark;
XSTATIC int		OldPoint;
XSTATIC int		Point;
XSTATIC int		PushBack;
XSTATIC int		Pushed;
XSTATIC int		Signal;
XFORWARD KEYMAP		Map[33];
XFORWARD KEYMAP		MetaMap[16];
XSTATIC SIZE_T		Length;
XSTATIC SIZE_T		ScreenCount;
XSTATIC SIZE_T		ScreenSize;
XSTATIC char		*backspace;
XSTATIC int		TTYwidth;
XSTATIC int		TTYrows;
X
X/* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
Xint		rl_meta_chars = 1;
X
X/*
X**  Declarations.
X*/
XSTATIC CHAR	*editinput();
Xextern int	read();
Xextern int	write();
X#if	defined(USE_TERMCAP)
Xextern char	*getenv();
Xextern char	*tgetstr();
Xextern int	tgetent();
X#endif	/* defined(USE_TERMCAP) */
X
X/*
X**  TTY input/output functions.
X*/
X
XSTATIC void
XTTYflush()
X{
X    if (ScreenCount) {
X	(void)write(1, Screen, ScreenCount);
X	ScreenCount = 0;
X    }
X}
X
XSTATIC void
XTTYput(c)
X    CHAR	c;
X{
X    Screen[ScreenCount] = c;
X    if (++ScreenCount >= ScreenSize - 1) {
X	ScreenSize += SCREEN_INC;
X	RENEW(Screen, char, ScreenSize);
X    }
X}
X
XSTATIC void
XTTYputs(p)
X    CHAR	*p;
X{
X    while (*p)
X	TTYput(*p++);
X}
X
XSTATIC void
XTTYshow(c)
X    CHAR	c;
X{
X    if (c == DEL) {
X	TTYput('^');
X	TTYput('?');
X    }
X    else if (ISCTL(c)) {
X	TTYput('^');
X	TTYput(UNCTL(c));
X    }
X    else if (rl_meta_chars && ISMETA(c)) {
X	TTYput('M');
X	TTYput('-');
X	TTYput(UNMETA(c));
X    }
X    else
X	TTYput(c);
X}
X
XSTATIC void
XTTYstring(p)
X    CHAR	*p;
X{
X    while (*p)
X	TTYshow(*p++);
X}
X
XSTATIC unsigned int
XTTYget()
X{
X    CHAR	c;
X
X    TTYflush();
X    if (Pushed) {
X	Pushed = 0;
X	return PushBack;
X    }
X    if (*Input)
X	return *Input++;
X    return read(0, &c, (SIZE_T)1) == 1 ? c : EOF;
X}
X
X#define TTYback()	(backspace ? TTYputs((CHAR *)backspace) : TTYput('\b'))
X
XSTATIC void
XTTYbackn(n)
X    int		n;
X{
X    while (--n >= 0)
X	TTYback();
X}
X
XSTATIC void
XTTYinfo()
X{
X    static int		init;
X#if	defined(USE_TERMCAP)
X    char		*term;
X    char		buff[2048];
X    char		*bp;
X#endif	/* defined(USE_TERMCAP) */
X#if	defined(TIOCGWINSZ)
X    struct winsize	W;
X#endif	/* defined(TIOCGWINSZ) */
X
X    if (init) {
X#if	defined(TIOCGWINSZ)
X	/* Perhaps we got resized. */
X	if (ioctl(0, TIOCGWINSZ, &W) >= 0
X	 && W.ws_col > 0 && W.ws_row > 0) {
X	    TTYwidth = (int)W.ws_col;
X	    TTYrows = (int)W.ws_row;
X	}
X#endif	/* defined(TIOCGWINSZ) */
X	return;
X    }
X    init++;
X
X    TTYwidth = TTYrows = 0;
X#if	defined(USE_TERMCAP)
X    bp = &buff[0];
X    if ((term = getenv("TERM")) == NULL)
X	term = "dumb";
X    if (tgetent(buff, term) < 0) {
X       TTYwidth = SCREEN_WIDTH;
X       TTYrows = SCREEN_ROWS;
X       return;
X    }
X    backspace = strdup(tgetstr("le", &bp));
X    TTYwidth = tgetnum("co");
X    TTYrows = tgetnum("li");
X#endif	/* defined(USE_TERMCAP) */
X
X#if	defined(TIOCGWINSZ)
X    if (ioctl(0, TIOCGWINSZ, &W) >= 0) {
X	TTYwidth = (int)W.ws_col;
X	TTYrows = (int)W.ws_row;
X    }
X#endif	/* defined(TIOCGWINSZ) */
X
X    if (TTYwidth <= 0 || TTYrows <= 0) {
X	TTYwidth = SCREEN_WIDTH;
X	TTYrows = SCREEN_ROWS;
X    }
X}
X
X
X/*
X**  Print an array of words in columns.
X*/
XSTATIC void
Xcolumns(ac, av)
X    int		ac;
X    CHAR	**av;
X{
X    CHAR	*p;
X    int		i;
X    int		j;
X    int		k;
X    int		len;
X    int		skip;
X    int		longest;
X    int		cols;
X
X    /* Find longest name, determine column count from that. */
X    for (longest = 0, i = 0; i < ac; i++)
X	if ((j = strlen((char *)av[i])) > longest)
X	    longest = j;
X    cols = TTYwidth / (longest + 3);
X
X    TTYputs((CHAR *)NEWLINE);
X    for (skip = ac / cols + 1, i = 0; i < skip; i++) {
X	for (j = i; j < ac; j += skip) {
X	    for (p = av[j], len = strlen((char *)p), k = len; --k >= 0; p++)
X		TTYput(*p);
X	    if (j + skip < ac)
X		while (++len < longest + 3)
X		    TTYput(' ');
X	}
X	TTYputs((CHAR *)NEWLINE);
X    }
X}
X
XSTATIC void
Xreposition()
X{
X    int		i;
X    CHAR	*p;
X
X    TTYput('\r');
X    TTYputs((CHAR *)Prompt);
X    for (i = Point, p = Line; --i >= 0; p++)
X	TTYshow(*p);
X}
X
XSTATIC void
Xleft(Change)
X    STATUS	Change;
X{
X    TTYback();
X    if (Point) {
X	if (ISCTL(Line[Point - 1]))
X	    TTYback();
X        else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
X	    TTYback();
X	    TTYback();
X	}
X    }
X    if (Change == CSmove)
X	Point--;
X}
X
XSTATIC void
Xright(Change)
X    STATUS	Change;
X{
X    TTYshow(Line[Point]);
X    if (Change == CSmove)
X	Point++;
X}
X
XSTATIC STATUS
Xring_bell()
X{
X    TTYput('\07');
X    TTYflush();
X    return CSstay;
X}
X
XSTATIC STATUS
Xdo_macro(c)
X    unsigned int	c;
X{
X    CHAR		name[4];
X
X    name[0] = '_';
X    name[1] = c;
X    name[2] = '_';
X    name[3] = '\0';
X
X    if ((Input = (CHAR *)getenv((char *)name)) == NULL) {
X	Input = NIL;
X	return ring_bell();
X    }
X    return CSstay;
X}
X
XSTATIC STATUS
Xdo_forward(move)
X    STATUS	move;
X{
X    int		i;
X    CHAR	*p;
X
X    i = 0;
X    do {
X	p = &Line[Point];
X	for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++)
X	    if (move == CSmove)
X		right(CSstay);
X
X	for (; Point < End && isalnum(*p); Point++, p++)
X	    if (move == CSmove)
X		right(CSstay);
X
X	if (Point == End)
X	    break;
X    } while (++i < Repeat);
X
X    return CSstay;
X}
X
XSTATIC STATUS
Xdo_case(type)
X    CASE	type;
X{
X    int		i;
X    int		end;
X    int		count;
X    CHAR	*p;
X
X    (void)do_forward(CSstay);
X    if (OldPoint != Point) {
X	if ((count = Point - OldPoint) < 0)
X	    count = -count;
X	Point = OldPoint;
X	if ((end = Point + count) > End)
X	    end = End;
X	for (i = Point, p = &Line[i]; i < end; i++, p++) {
X	    if (type == TOupper) {
X		if (islower(*p))
X		    *p = toupper(*p);
X	    }
X	    else if (isupper(*p))
X		*p = tolower(*p);
X	    right(CSmove);
X	}
X    }
X    return CSstay;
X}
X
XSTATIC STATUS
Xcase_down_word()
X{
X    return do_case(TOlower);
X}
X
XSTATIC STATUS
Xcase_up_word()
X{
X    return do_case(TOupper);
X}
X
XSTATIC void
Xceol()
X{
X    int		extras;
X    int		i;
X    CHAR	*p;
X
X    for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
X	TTYput(' ');
X	if (ISCTL(*p)) {
X	    TTYput(' ');
X	    extras++;
X	}
X	else if (rl_meta_chars && ISMETA(*p)) {
X	    TTYput(' ');
X	    TTYput(' ');
X	    extras += 2;
X	}
X    }
X
X    for (i += extras; i > Point; i--)
X	TTYback();
X}
X
XSTATIC void
Xclear_line()
X{
X    Point = -strlen(Prompt);
X    TTYput('\r');
X    ceol();
X    Point = 0;
X    End = 0;
X    Line[0] = '\0';
X}
X
XSTATIC STATUS
Xinsert_string(p)
X    CHAR	*p;
X{
X    SIZE_T	len;
X    int		i;
X    CHAR	*new;
X    CHAR	*q;
X
X    len = strlen((char *)p);
X    if (End + len >= Length) {
X	if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL)
X	    return CSstay;
X	if (Length) {
X	    COPYFROMTO(new, Line, Length);
X	    DISPOSE(Line);
X	}
X	Line = new;
X	Length += len + MEM_INC;
X    }
X
X    for (q = &Line[Point], i = End - Point; --i >= 0; )
X	q[len + i] = q[i];
X    COPYFROMTO(&Line[Point], p, len);
X    End += len;
X    Line[End] = '\0';
X    TTYstring(&Line[Point]);
X    Point += len;
X
X    return Point == End ? CSstay : CSmove;
X}
X
XSTATIC STATUS
Xredisplay()
X{
X    TTYputs((CHAR *)NEWLINE);
X    TTYputs((CHAR *)Prompt);
X    TTYstring(Line);
X    return CSmove;
X}
X
X
XSTATIC CHAR *
Xnext_hist()
X{
X    return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
X}
X
XSTATIC CHAR *
Xprev_hist()
X{
X    return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
X}
X
XSTATIC STATUS
Xdo_insert_hist(p)
X    CHAR	*p;
X{
X    if (p == NULL)
X	return ring_bell();
X    Point = 0;
X    reposition();
X    ceol();
X    End = 0;
X    return insert_string(p);
X}
X
XSTATIC STATUS
Xdo_hist(move)
X    CHAR	*(*move)();
X{
X    CHAR	*p;
X    int		i;
X
X    i = 0;
X    do {
X	if ((p = (*move)()) == NULL)
X	    return ring_bell();
X    } while (++i < Repeat);
X    return do_insert_hist(p);
X}
X
XSTATIC STATUS
Xh_next()
X{
X    return do_hist(next_hist);
X}
X
XSTATIC STATUS
Xh_prev()
X{
X    return do_hist(prev_hist);
X}
X
XSTATIC STATUS
Xh_first()
X{
X    return do_insert_hist(H.Lines[H.Pos = 0]);
X}
X
XSTATIC STATUS
Xh_last()
X{
X    return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
X}
X
X/*
X**  Return zero if pat appears as a substring in text.
X*/
XSTATIC int
Xsubstrcmp(text, pat, len)
X    char	*text;
X    char	*pat;
X    int		len;
X{
X    CHAR	c;
X
X    if ((c = *pat) == '\0')
X        return *text == '\0';
X    for ( ; *text; text++)
X        if (*text == c && strncmp(text, pat, len) == 0)
X            return 0;
X    return 1;
X}
X
XSTATIC CHAR *
Xsearch_hist(search, move)
X    CHAR	*search;
X    CHAR	*(*move)();
X{
X    static CHAR	*old_search;
X    int		len;
X    int		pos;
X    int		(*match)();
X    char	*pat;
X
X    /* Save or get remembered search pattern. */
X    if (search && *search) {
X	if (old_search)
X	    DISPOSE(old_search);
X	old_search = (CHAR *)strdup((char *)search);
X    }
X    else {
X	if (old_search == NULL || *old_search == '\0')
X            return NULL;
X	search = old_search;
X    }
X
X    /* Set up pattern-finder. */
X    if (*search == '^') {
X	match = strncmp;
X	pat = (char *)(search + 1);
X    }
X    else {
X	match = substrcmp;
X	pat = (char *)search;
X    }
X    len = strlen(pat);
X
X    for (pos = H.Pos; (*move)() != NULL; )
X	if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
X            return H.Lines[H.Pos];
X    H.Pos = pos;
X    return NULL;
X}
X
XSTATIC STATUS
Xh_search()
X{
X    static int	Searching;
X    CONST char	*old_prompt;
X    CHAR	*(*move)();
X    CHAR	*p;
X
X    if (Searching)
X	return ring_bell();
X    Searching = 1;
X
X    clear_line();
X    old_prompt = Prompt;
X    Prompt = "Search: ";
X    TTYputs((CHAR *)Prompt);
X    move = Repeat == NO_ARG ? prev_hist : next_hist;
X    p = editinput();
X    Prompt = old_prompt;
X    Searching = 0;
X    clear_line();
X    TTYputs((CHAR *)Prompt);
X    if (p == NULL && Signal > 0) {
X	Signal = 0;
X	return redisplay();
X    }
X    p = search_hist(p, move);
X    return do_insert_hist(p);
X}
X
XSTATIC STATUS
Xfd_char()
X{
X    int		i;
X
X    i = 0;
X    do {
X	if (Point >= End)
X	    break;
X	right(CSmove);
X    } while (++i < Repeat);
X    return CSstay;
X}
X
XSTATIC void
Xsave_yank(begin, i)
X    int		begin;
X    int		i;
X{
X    if (Yanked) {
X	DISPOSE(Yanked);
X	Yanked = NULL;
X    }
X
X    if (i < 1)
X	return;
X
X    if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) {
X	COPYFROMTO(Yanked, &Line[begin], i);
X	Yanked[i] = '\0';
X    }
X}
X
XSTATIC STATUS
Xdelete_string(count)
X    int		count;
X{
X    int		i;
X    CHAR	*p;
X
X    if (count <= 0 || End == Point)
X	return ring_bell();
X
X    if (count == 1 && Point == End - 1) {
X	/* Optimize common case of delete at end of line. */
X	End--;
X	p = &Line[Point];
X	i = 1;
X	TTYput(' ');
X	if (ISCTL(*p)) {
X	    i = 2;
X	    TTYput(' ');
X	}
X	else if (rl_meta_chars && ISMETA(*p)) {
X	    i = 3;
X	    TTYput(' ');
X	    TTYput(' ');
X	}
X	TTYbackn(i);
X	*p = '\0';
X	return CSmove;
X    }
X    if (Point + count > End && (count = End - Point) <= 0)
X	return CSstay;
X
X    if (count > 1)
X	save_yank(Point, count);
X
X    for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++)
X	p[0] = p[count];
X    ceol();
X    End -= count;
X    TTYstring(&Line[Point]);
X    return CSmove;
X}
X
XSTATIC STATUS
Xbk_char()
X{
X    int		i;
X
X    i = 0;
X    do {
X	if (Point == 0)
X	    break;
X	left(CSmove);
X    } while (++i < Repeat);
X
X    return CSstay;
X}
X
XSTATIC STATUS
Xbk_del_char()
X{
X    int		i;
X
X    i = 0;
X    do {
X	if (Point == 0)
X	    break;
X	left(CSmove);
X    } while (++i < Repeat);
X
X    return delete_string(i);
X}
X
XSTATIC STATUS
Xkill_line()
X{
X    int		i;
X
X    if (Repeat != NO_ARG) {
X	if (Repeat < Point) {
X	    i = Point;
X	    Point = Repeat;
X	    reposition();
X	    (void)delete_string(i - Point);
X	}
X	else if (Repeat > Point) {
X	    right(CSmove);
X	    (void)delete_string(Repeat - Point - 1);
X	}
X	return CSmove;
X    }
X
X    save_yank(Point, End - Point);
X    Line[Point] = '\0';
X    ceol();
X    End = Point;
X    return CSstay;
X}
X
XSTATIC STATUS
Xinsert_char(c)
X    int		c;
X{
X    STATUS	s;
X    CHAR	buff[2];
X    CHAR	*p;
X    CHAR	*q;
X    int		i;
X
X    if (Repeat == NO_ARG || Repeat < 2) {
X	buff[0] = c;
X	buff[1] = '\0';
X	return insert_string(buff);
X    }
X
X    if ((p = NEW(CHAR, Repeat + 1)) == NULL)
X	return CSstay;
X    for (i = Repeat, q = p; --i >= 0; )
X	*q++ = c;
X    *q = '\0';
X    Repeat = 0;
X    s = insert_string(p);
X    DISPOSE(p);
X    return s;
X}
X
XSTATIC STATUS
Xmeta()
X{
X    unsigned int	c;
X    KEYMAP		*kp;
X
X    if ((c = TTYget()) == EOF)
X	return CSeof;
X#if	defined(ANSI_ARROWS)
X    /* Also include VT-100 arrows. */
X    if (c == '[' || c == 'O')
X	switch (c = TTYget()) {
X	default:	return ring_bell();
X	case EOF:	return CSeof;
X	case 'A':	return h_prev();
X	case 'B':	return h_next();
X	case 'C':	return fd_char();
X	case 'D':	return bk_char();
X	}
X#endif	/* defined(ANSI_ARROWS) */
X
X    if (isdigit(c)) {
X	for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c); )
X	    Repeat = Repeat * 10 + c - '0';
X	Pushed = 1;
X	PushBack = c;
X	return CSstay;
X    }
X
X    if (isupper(c))
X	return do_macro(c);
X    for (OldPoint = Point, kp = MetaMap; kp->Function; kp++)
X	if (kp->Key == c)
X	    return (*kp->Function)();
X
X    return ring_bell();
X}
X
XSTATIC STATUS
Xemacs(c)
X    unsigned int	c;
X{
X    STATUS		s;
X    KEYMAP		*kp;
X
X    if (ISMETA(c)) {
X	Pushed = 1;
X	PushBack = UNMETA(c);
X	return meta();
X    }
X    for (kp = Map; kp->Function; kp++)
X	if (kp->Key == c)
X	    break;
X    s = kp->Function ? (*kp->Function)() : insert_char((int)c);
X    if (!Pushed)
X	/* No pushback means no repeat count; hacky, but true. */
X	Repeat = NO_ARG;
X    return s;
X}
X
XSTATIC STATUS
XTTYspecial(c)
X    unsigned int	c;
X{
X    if (ISMETA(c))
X	return CSdispatch;
X
X    if (c == rl_erase || c == DEL)
X	return bk_del_char();
X    if (c == rl_kill) {
X	if (Point != 0) {
X	    Point = 0;
X	    reposition();
X	}
X	Repeat = NO_ARG;
X	return kill_line();
X    }
X    if (c == rl_eof && Point == 0 && End == 0)
X	return CSeof;
X    if (c == rl_intr) {
X	Signal = SIGINT;
X	return CSeof;
X    }
X    if (c == rl_quit) {
X	Signal = SIGQUIT;
X	return CSeof;
X    }
X
X    return CSdispatch;
X}
X
XSTATIC CHAR *
Xeditinput()
X{
X    unsigned int	c;
X
X    Repeat = NO_ARG;
X    OldPoint = Point = Mark = End = 0;
X    Line[0] = '\0';
X
X    Signal = -1;
X    while ((c = TTYget()) != EOF)
X	switch (TTYspecial(c)) {
X	case CSdone:
X	    return Line;
X	case CSeof:
X	    return NULL;
X	case CSmove:
X	    reposition();
X	    break;
X	case CSdispatch:
X	    switch (emacs(c)) {
X	    case CSdone:
X		return Line;
X	    case CSeof:
X		return NULL;
X	    case CSmove:
X		reposition();
X		break;
X	    case CSdispatch:
X	    case CSstay:
X		break;
X	    }
X	    break;
X	case CSstay:
X	    break;
X	}
X    return NULL;
X}
X
XSTATIC void
Xhist_add(p)
X    CHAR	*p;
X{
X    int		i;
X
X    if ((p = (CHAR *)strdup((char *)p)) == NULL)
X	return;
X    if (H.Size < HIST_SIZE)
X	H.Lines[H.Size++] = p;
X    else {
X	DISPOSE(H.Lines[0]);
X	for (i = 0; i < HIST_SIZE - 1; i++)
X	    H.Lines[i] = H.Lines[i + 1];
X	H.Lines[i] = p;
X    }
X    H.Pos = H.Size - 1;
X}
X
X/*
X**  For compatibility with FSF readline.
X*/
X/* ARGSUSED0 */
Xvoid
Xrl_reset_terminal(p)
X    char	*p;
X{
X}
X
Xvoid
Xrl_initialize()
X{
X}
X
Xchar *
Xreadline(prompt)
X    CONST char	*prompt;
X{
X    CHAR	*line;
X    int		s;
X
X    if (Line == NULL) {
X	Length = MEM_INC;
X	if ((Line = NEW(CHAR, Length)) == NULL)
X	    return NULL;
X    }
X
X    TTYinfo();
X    rl_ttyset(0);
X    hist_add(NIL);
X    ScreenSize = SCREEN_INC;
X    Screen = NEW(char, ScreenSize);
X    Prompt = prompt ? prompt : (char *)NIL;
X    TTYputs((CHAR *)Prompt);
X    if ((line = editinput()) != NULL) {
X	line = (CHAR *)strdup((char *)line);
X	TTYputs((CHAR *)NEWLINE);
X	TTYflush();
X    }
X    rl_ttyset(1);
X    DISPOSE(Screen);
X    DISPOSE(H.Lines[--H.Size]);
X    if (Signal > 0) {
X	s = Signal;
X	Signal = 0;
X	(void)kill(getpid(), s);
X    }
X    return (char *)line;
X}
X
Xvoid
Xadd_history(p)
X    char	*p;
X{
X    if (p == NULL || *p == '\0')
X	return;
X
X#if	defined(UNIQUE_HISTORY)
X    if (H.Pos && strcmp(p, H.Lines[H.Pos - 1]) == 0)
X        return;
X#endif	/* defined(UNIQUE_HISTORY) */
X    hist_add((CHAR *)p);
X}
X
X
XSTATIC STATUS
Xbeg_line()
X{
X    if (Point) {
X	Point = 0;
X	return CSmove;
X    }
X    return CSstay;
X}
X
XSTATIC STATUS
Xdel_char()
X{
X    return delete_string(Repeat == NO_ARG ? 1 : Repeat);
X}
X
XSTATIC STATUS
Xend_line()
X{
X    if (Point != End) {
X	Point = End;
X	return CSmove;
X    }
X    return CSstay;
X}
X
X/*
X**  Move back to the beginning of the current word and return an
X**  allocated copy of it.
X*/
XSTATIC CHAR *
Xfind_word()
X{
X    static char	SEPS[] = "#;&|^$=`'{}()<>\n\t ";
X    CHAR	*p;
X    CHAR	*new;
X    SIZE_T	len;
X
X    for (p = &Line[Point]; p > Line && strchr(SEPS, (char)p[-1]) == NULL; p--)
X	continue;
X    len = Point - (p - Line) + 1;
X    if ((new = NEW(CHAR, len)) == NULL)
X	return NULL;
X    COPYFROMTO(new, p, len);
X    new[len - 1] = '\0';
X    return new;
X}
X
XSTATIC STATUS
Xc_complete()
X{
X    CHAR	*p;
X    CHAR	*word;
X    int		unique;
X    STATUS	s;
X
X    word = find_word();
X    p = (CHAR *)rl_complete((char *)word, &unique);
X    if (word)
X	DISPOSE(word);
X    if (p && *p) {
X	s = insert_string(p);
X	if (!unique)
X	    (void)ring_bell();
X	DISPOSE(p);
X	return s;
X    }
X    return ring_bell();
X}
X
XSTATIC STATUS
Xc_possible()
X{
X    CHAR	**av;
X    CHAR	*word;
X    int		ac;
X
X    word = find_word();
X    ac = rl_list_possib((char *)word, (char ***)&av);
X    if (word)
X	DISPOSE(word);
X    if (ac) {
X	columns(ac, av);
X	while (--ac >= 0)
X	    DISPOSE(av[ac]);
X	DISPOSE(av);
X	return CSmove;
X    }
X    return ring_bell();
X}
X
XSTATIC STATUS
Xaccept_line()
X{
X    Line[End] = '\0';
X    return CSdone;
X}
X
XSTATIC STATUS
Xtranspose()
X{
X    CHAR	c;
X
X    if (Point) {
X	if (Point == End)
X	    left(CSmove);
X	c = Line[Point - 1];
X	left(CSstay);
X	Line[Point - 1] = Line[Point];
X	TTYshow(Line[Point - 1]);
X	Line[Point++] = c;
X	TTYshow(c);
X    }
X    return CSstay;
X}
X
XSTATIC STATUS
Xquote()
X{
X    unsigned int	c;
X
X    return (c = TTYget()) == EOF ? CSeof : insert_char((int)c);
X}
X
XSTATIC STATUS
Xwipe()
X{
X    int		i;
X
X    if (Mark > End)
X	return ring_bell();
X
X    if (Point > Mark) {
X	i = Point;
X	Point = Mark;
X	Mark = i;
X	reposition();
X    }
X
X    return delete_string(Mark - Point);
X}
X
XSTATIC STATUS
Xmk_set()
X{
X    Mark = Point;
X    return CSstay;
X}
X
XSTATIC STATUS
Xexchange()
X{
X    unsigned int	c;
X
X    if ((c = TTYget()) != CTL('X'))
X	return c == EOF ? CSeof : ring_bell();
X
X    if ((c = Mark) <= End) {
X	Mark = Point;
X	Point = c;
X	return CSmove;
X    }
X    return CSstay;
X}
X
XSTATIC STATUS
Xyank()
X{
X    if (Yanked && *Yanked)
X	return insert_string(Yanked);
X    return CSstay;
X}
X
XSTATIC STATUS
Xcopy_region()
X{
X    if (Mark > End)
X	return ring_bell();
X
X    if (Point > Mark)
X	save_yank(Mark, Point - Mark);
X    else
X	save_yank(Point, Mark - Point);
X
X    return CSstay;
X}
X
XSTATIC STATUS
Xmove_to_char()
X{
X    unsigned int	c;
X    int			i;
X    CHAR		*p;
X
X    if ((c = TTYget()) == EOF)
X	return CSeof;
X    for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
X	if (*p == c) {
X	    Point = i;
X	    return CSmove;
X	}
X    return CSstay;
X}
X
XSTATIC STATUS
Xfd_word()
X{
X    return do_forward(CSmove);
X}
X
XSTATIC STATUS
Xfd_kill_word()
X{
X    int		i;
X
X    (void)do_forward(CSstay);
X    if (OldPoint != Point) {
X	i = Point - OldPoint;
X	Point = OldPoint;
X	return delete_string(i);
X    }
X    return CSstay;
X}
X
XSTATIC STATUS
Xbk_word()
X{
X    int		i;
X    CHAR	*p;
X
X    i = 0;
X    do {
X	for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--)
X	    left(CSmove);
X
X	for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--)
X	    left(CSmove);
X
X	if (Point == 0)
X	    break;
X    } while (++i < Repeat);
X
X    return CSstay;
X}
X
XSTATIC STATUS
Xbk_kill_word()
X{
X    (void)bk_word();
X    if (OldPoint != Point)
X	return delete_string(OldPoint - Point);
X    return CSstay;
X}
X
XSTATIC int
Xargify(line, avp)
X    CHAR	*line;
X    CHAR	***avp;
X{
X    CHAR	*c;
X    CHAR	**p;
X    CHAR	**new;
X    int		ac;
X    int		i;
X
X    i = MEM_INC;
X    if ((*avp = p = NEW(CHAR*, i))== NULL)
X	 return 0;
X
X    for (c = line; isspace(*c); c++)
X	continue;
X    if (*c == '\n' || *c == '\0')
X	return 0;
X
X    for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
X	if (isspace(*c)) {
X	    *c++ = '\0';
X	    if (*c && *c != '\n') {
X		if (ac + 1 == i) {
X		    new = NEW(CHAR*, i + MEM_INC);
X		    if (new == NULL) {
X			p[ac] = NULL;
X			return ac;
X		    }
X		    COPYFROMTO(new, p, i * sizeof (char **));
X		    i += MEM_INC;
X		    DISPOSE(p);
X		    *avp = p = new;
X		}
X		p[ac++] = c;
X	    }
X	}
X	else
X	    c++;
X    }
X    *c = '\0';
X    p[ac] = NULL;
X    return ac;
X}
X
XSTATIC STATUS
Xlast_argument()
X{
X    CHAR	**av;
X    CHAR	*p;
X    STATUS	s;
X    int		ac;
X
X    if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL)
X	return ring_bell();
X
X    if ((p = (CHAR *)strdup((char *)p)) == NULL)
X	return CSstay;
X    ac = argify(p, &av);
X
X    if (Repeat != NO_ARG)
X	s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
X    else
X	s = ac ? insert_string(av[ac - 1]) : CSstay;
X
X    if (ac)
X	DISPOSE(av);
X    DISPOSE(p);
X    return s;
X}
X
XSTATIC KEYMAP	Map[33] = {
X    {	CTL('@'),	ring_bell	},
X    {	CTL('A'),	beg_line	},
X    {	CTL('B'),	bk_char		},
X    {	CTL('D'),	del_char	},
X    {	CTL('E'),	end_line	},
X    {	CTL('F'),	fd_char		},
X    {	CTL('G'),	ring_bell	},
X    {	CTL('H'),	bk_del_char	},
X    {	CTL('I'),	c_complete	},
X    {	CTL('J'),	accept_line	},
X    {	CTL('K'),	kill_line	},
X    {	CTL('L'),	redisplay	},
X    {	CTL('M'),	accept_line	},
X    {	CTL('N'),	h_next		},
X    {	CTL('O'),	ring_bell	},
X    {	CTL('P'),	h_prev		},
X    {	CTL('Q'),	ring_bell	},
X    {	CTL('R'),	h_search	},
X    {	CTL('S'),	ring_bell	},
X    {	CTL('T'),	transpose	},
X    {	CTL('U'),	ring_bell	},
X    {	CTL('V'),	quote		},
X    {	CTL('W'),	wipe		},
X    {	CTL('X'),	exchange	},
X    {	CTL('Y'),	yank		},
X    {	CTL('Z'),	ring_bell	},
X    {	CTL('['),	meta		},
X    {	CTL(']'),	move_to_char	},
X    {	CTL('^'),	ring_bell	},
X    {	CTL('_'),	ring_bell	},
X    {	0,		NULL		}
X};
X
XSTATIC KEYMAP	MetaMap[16]= {
X    {	CTL('H'),	bk_kill_word	},
X    {	DEL,		bk_kill_word	},
X    {	' ',		mk_set	},
X    {	'.',		last_argument	},
X    {	'<',		h_first		},
X    {	'>',		h_last		},
X    {	'?',		c_possible	},
X    {	'b',		bk_word		},
X    {	'd',		fd_kill_word	},
X    {	'f',		fd_word		},
X    {	'l',		case_down_word	},
X    {	'u',		case_up_word	},
X    {	'y',		yank		},
X    {	'w',		copy_region	},
X    {	0,		NULL		}
X};
END_OF_FILE
  if test 22788 -ne `wc -c <'editline.c'`; then
    echo shar: \"'editline.c'\" unpacked with wrong size!
  fi
  # end of 'editline.c'
fi
if test -f 'complete.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'complete.c'\"
else
  echo shar: Extracting \"'complete.c'\" \(4113 characters\)
  sed "s/^X//" >'complete.c' <<'END_OF_FILE'
X/*  $Revision: 1.3 $
X**
X**  History and file completion functions for editline library.
X*/
X#include "editline.h"
X
X
X#if	defined(NEED_STRDUP)
X/*
X**  Return an allocated copy of a string.
X*/
Xchar *
Xstrdup(p)
X    char	*p;
X{
X    char	*new;
X
X    if ((new = NEW(char, strlen(p) + 1)) != NULL)
X	(void)strcpy(new, p);
X    return new;
X}
X#endif	/* defined(NEED_STRDUP) */
X
X/*
X**  strcmp-like sorting predicate for qsort.
X*/
XSTATIC int
Xcompare(p1, p2)
X    CONST void	*p1;
X    CONST void	*p2;
X{
X    CONST char	**v1;
X    CONST char	**v2;
X
X    v1 = (CONST char **)p1;
X    v2 = (CONST char **)p2;
X    return strcmp(*v1, *v2);
X}
X
X/*
X**  Fill in *avp with an array of names that match file, up to its length.
X**  Ignore . and .. .
X*/
XSTATIC int
XFindMatches(dir, file, avp)
X    char	*dir;
X    char	*file;
X    char	***avp;
X{
X    char	**av;
X    char	**new;
X    char	*p;
X    DIR		*dp;
X    DIRENTRY	*ep;
X    SIZE_T	ac;
X    SIZE_T	len;
X
X    if ((dp = opendir(dir)) == NULL)
X	return 0;
X
X    av = NULL;
X    ac = 0;
X    len = strlen(file);
X    while ((ep = readdir(dp)) != NULL) {
X	p = ep->d_name;
X	if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')))
X	    continue;
X	if (len && strncmp(p, file, len) != 0)
X	    continue;
X
X	if ((ac % MEM_INC) == 0) {
X	    if ((new = NEW(char*, ac + MEM_INC)) == NULL)
X		break;
X	    if (ac) {
X		COPYFROMTO(new, av, ac * sizeof (char **));
X		DISPOSE(av);
X	    }
X	    *avp = av = new;
X	}
X
X	if ((av[ac] = strdup(p)) == NULL) {
X	    if (ac == 0)
X		DISPOSE(av);
X	    break;
X	}
X	ac++;
X    }
X
X    /* Clean up and return. */
X    (void)closedir(dp);
X    if (ac)
X	qsort(av, ac, sizeof (char **), compare);
X    return ac;
X}
X
X/*
X**  Split a pathname into allocated directory and trailing filename parts.
X*/
XSTATIC int
XSplitPath(path, dirpart, filepart)
X    char	*path;
X    char	**dirpart;
X    char	**filepart;
X{
X    static char	DOT[] = ".";
X    char	*dpart;
X    char	*fpart;
X
X    if ((fpart = strrchr(path, '/')) == NULL) {
X	if ((dpart = strdup(DOT)) == NULL)
X	    return -1;
X	if ((fpart = strdup(path)) == NULL) {
X	    DISPOSE(dpart);
X	    return -1;
X	}
X    }
X    else {
X	if ((dpart = strdup(path)) == NULL)
X	    return -1;
X	dpart[fpart - path + 1] = '\0';
X	if ((fpart = strdup(++fpart)) == NULL) {
X	    DISPOSE(dpart);
X	    return -1;
X	}
X    }
X    *dirpart = dpart;
X    *filepart = fpart;
X    return 0;
X}
X
X/*
X**  Attempt to complete the pathname, returning an allocated copy.
X**  Fill in *unique if we completed it, or set it to 0 if ambiguous.
X*/
Xchar *
Xrl_complete(pathname, unique)
X    char	*pathname;
X    int		*unique;
X{
X    char	**av;
X    char	*dir;
X    char	*file;
X    char	*new;
X    char	*p;
X    SIZE_T	ac;
X    SIZE_T	end;
X    SIZE_T	i;
X    SIZE_T	j;
X    SIZE_T	len;
X
X    if (SplitPath(pathname, &dir, &file) < 0)
X	return NULL;
X    if ((ac = FindMatches(dir, file, &av)) == 0) {
X	DISPOSE(dir);
X	DISPOSE(file);
X	return NULL;
X    }
X
X    p = NULL;
X    len = strlen(file);
X    if (ac == 1) {
X	/* Exactly one match -- finish it off. */
X	*unique = 1;
X	j = strlen(av[0]) - len + 2;
X	if ((p = NEW(char, j + 1)) != NULL) {
X	    COPYFROMTO(p, av[0] + len, j);
X	    if ((new = NEW(char, strlen(dir) + strlen(av[0]) + 2)) != NULL) {
X		(void)strcpy(new, dir);
X		(void)strcat(new, "/");
X		(void)strcat(new, av[0]);
X		rl_add_slash(new, p);
X		DISPOSE(new);
X	    }
X	}
X    }
X    else {
X	*unique = 0;
X	if (len) {
X	    /* Find largest matching substring. */
X	    for (i = len, end = strlen(av[0]); i < end; i++)
X		for (j = 1; j < ac; j++)
X		    if (av[0][i] != av[j][i])
X			goto breakout;
X  breakout:
X	    if (i > len) {
X		j = i - len + 1;
X		if ((p = NEW(char, j)) != NULL) {
X		    COPYFROMTO(p, av[0] + len, j);
X		    p[j - 1] = '\0';
X		}
X	    }
X	}
X    }
X
X    /* Clean up and return. */
X    DISPOSE(dir);
X    DISPOSE(file);
X    for (i = 0; i < ac; i++)
X	DISPOSE(av[i]);
X    DISPOSE(av);
X    return p;
X}
X
X/*
X**  Return all possible completions.
X*/
Xint
Xrl_list_possib(pathname, avp)
X    char	*pathname;
X    char	***avp;
X{
X    char	*dir;
X    char	*file;
X    int		ac;
X
X    if (SplitPath(pathname, &dir, &file) < 0)
X	return 0;
X    ac = FindMatches(dir, file, avp);
X    DISPOSE(dir);
X    DISPOSE(file);
X    return ac;
X}
END_OF_FILE
  if test 4113 -ne `wc -c <'complete.c'`; then
    echo shar: \"'complete.c'\" unpacked with wrong size!
  fi
  # end of 'complete.c'
fi
if test -f 'sysunix.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sysunix.c'\"
else
  echo shar: Extracting \"'sysunix.c'\" \(1798 characters\)
  sed "s/^X//" >'sysunix.c' <<'END_OF_FILE'
X/*  $Revision: 1.1 $
X**
X**  Unix system-dependant routines for editline library.
X*/
X#include "editline.h"
X
X#if	defined(HAVE_TCGETATTR)
X#include <termios.h>
X
Xvoid
Xrl_ttyset(Reset)
X    int				Reset;
X{
X    static struct termios	old;
X    struct termios		new;
X
X    if (Reset == 0) {
X	(void)tcgetattr(0, &old);
X	rl_erase = old.c_cc[VERASE];
X	rl_kill = old.c_cc[VKILL];
X	rl_eof = old.c_cc[VEOF];
X	rl_intr = old.c_cc[VINTR];
X	rl_quit = old.c_cc[VQUIT];
X
X	new = old;
X	new.c_cc[VINTR] = -1;
X	new.c_cc[VQUIT] = -1;
X	new.c_lflag &= ~(ECHO | ICANON);
X	new.c_iflag &= ~(ISTRIP | INPCK);
X	new.c_cc[VMIN] = 1;
X	new.c_cc[VTIME] = 0;
X	(void)tcsetattr(0, TCSANOW, &new);
X    }
X    else
X	(void)tcsetattr(0, TCSANOW, &old);
X}
X
X#else
X#include <sgtty.h>
X
Xvoid
Xrl_ttyset(Reset)
X    int				Reset;
X{
X    static struct sgttyb	old_sgttyb;
X    static struct tchars	old_tchars;
X    struct sgttyb		new_sgttyb;
X    struct tchars		new_tchars;
X
X    if (Reset == 0) {
X	(void)ioctl(0, TIOCGETP, &old_sgttyb);
X	rl_erase = old_sgttyb.sg_erase;
X	rl_kill = old_sgttyb.sg_kill;
X
X	(void)ioctl(0, TIOCGETC, &old_tchars);
X	rl_eof = old_tchars.t_eofc;
X	rl_intr = old_tchars.t_intrc;
X	rl_quit = old_tchars.t_quitc;
X
X	new_sgttyb = old_sgttyb;
X	new_sgttyb.sg_flags &= ~ECHO;
X	new_sgttyb.sg_flags |= RAW;
X#if	defined(PASS8)
X	new_sgttyb.sg_flags |= PASS8;
X#endif	/* defined(PASS8) */
X	(void)ioctl(0, TIOCSETP, &new_sgttyb);
X
X	new_tchars = old_tchars;
X	new_tchars.t_intrc = -1;
X	new_tchars.t_quitc = -1;
X	(void)ioctl(0, TIOCSETC, &new_tchars);
X    }
X    else {
X	(void)ioctl(0, TIOCSETP, &old_sgttyb);
X	(void)ioctl(0, TIOCSETC, &old_tchars);
X    }
X}
X#endif	/* defined(HAVE_TCGETATTR) */
X
Xvoid
Xrl_add_slash(path, p)
X    char	*path;
X    char	*p;
X{
X    struct stat	Sb;
X
X    if (stat(path, &Sb) >= 0)
X	(void)strcat(p, S_ISDIR(Sb.st_mode) ? "/" : " ");
X}
END_OF_FILE
  if test 1798 -ne `wc -c <'sysunix.c'`; then
    echo shar: \"'sysunix.c'\" unpacked with wrong size!
  fi
  # end of 'sysunix.c'
fi
if test -f 'testit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'testit.c'\"
else
  echo shar: Extracting \"'testit.c'\" \(1024 characters\)
  sed "s/^X//" >'testit.c' <<'END_OF_FILE'
X/*  $Revision: 1.2 $
X**
X**  A "micro-shell" to test editline library.
X**  If given any arguments, commands aren't executed.
X*/
X#include <stdio.h>
X#if	defined(HAVE_STDLIB)
X#include <stdlib.h>
X#endif	/* defined(HAVE_STDLIB) */
X
Xextern char	*readline();
Xextern void	add_history();
X
X#if	!defined(HAVE_STDLIB)
Xextern int	chdir();
Xextern int	free();
Xextern int	strncmp();
Xextern int	system();
Xextern void	exit();
X#endif	/* !defined(HAVE_STDLIB) */
X
X
X#if	defined(NEED_PERROR)
Xvoid
Xperror(s)
X    char	*s;
X{
X    extern int	errno;
X
X    (voidf)printf(stderr, "%s: error %d\n", s, errno);
X}
X#endif	/* defined(NEED_PERROR) */
X
X
X/* ARGSUSED1 */
Xint
Xmain(ac, av)
X    int		ac;
X    char	*av[];
X{
X    char	*p;
X    int		doit;
X
X    doit = ac == 1;
X    while ((p = readline("testit> ")) != NULL) {
X	(void)printf("\t\t\t|%s|\n", p);
X	if (doit)
X	    if (strncmp(p, "cd ", 3) == 0) {
X		if (chdir(&p[3]) < 0)
X		    perror(&p[3]);
X	    }
X	    else if (system(p) != 0)
X		perror(p);
X	add_history(p);
X	free(p);
X    }
X    exit(0);
X    /* NOTREACHED */
X}
END_OF_FILE
  if test 1024 -ne `wc -c <'testit.c'`; then
    echo shar: \"'testit.c'\" unpacked with wrong size!
  fi
  # end of 'testit.c'
fi
if test -f 'Make.os9' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Make.os9'\"
else
  echo shar: Extracting \"'Make.os9'\" \(348 characters\)
  sed "s/^X//" >'Make.os9' <<'END_OF_FILE'
X##  $Revision: 1.2 $
X##
X##  OS-9 makefile for editline library.
X##
X
X.SUFFIXES:
X
XRFILES	= editline.r complete.r sysos9.r
X
X%.r:		%.c
X	cc68 -r -Dstrchr=index -Dstrrchr=rindex -DNEED_STRDUP -DSYS_OS9 $*.c
X
Xtestit:		testit.r editline.lib
X	cc68 -f=testit testit.r -l=editline.lib
X
X$(RFILES):	$(RFILES:%.r=%.c)
X
Xeditline.lib:	$(RFILES)
X	cat $(RFILES) >$@
END_OF_FILE
  if test 348 -ne `wc -c <'Make.os9'`; then
    echo shar: \"'Make.os9'\" unpacked with wrong size!
  fi
  # end of 'Make.os9'
fi
if test -f 'os9.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'os9.h'\"
else
  echo shar: Extracting \"'os9.h'\" \(174 characters\)
  sed "s/^X//" >'os9.h' <<'END_OF_FILE'
X/*  $Revision: 1.1 $
X**
X**  Editline system header file for OS-9 (on 68k).
X*/
X
X#define CRLF		"\r\l"
X#define FORWARD		extern
X
X#include <dir.h>
Xtypedef struct direct	DIRENTRY;
END_OF_FILE
  if test 174 -ne `wc -c <'os9.h'`; then
    echo shar: \"'os9.h'\" unpacked with wrong size!
  fi
  # end of 'os9.h'
fi
if test -f 'sysos9.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sysos9.c'\"
else
  echo shar: Extracting \"'sysos9.c'\" \(1120 characters\)
  sed "s/^X//" >'sysos9.c' <<'END_OF_FILE'
X/*  $Revision: 1.1 $
X**
X**  OS-9 system-dependant routines for editline library.
X*/
X#include "editline.h"
X#include <sgstat.h>
X#include <modes.h>
X
X
Xvoid
Xrl_ttyset(Reset)
X    int			Reset;
X{
X    static struct sgbuf	old;
X    struct sgbuf	new;
X
X
X    if (Reset == 0) {
X        _gs_opt(0, &old);
X        _gs_opt(0, &new);
X        new.sg_backsp = 0;	new.sg_delete = 0;	new.sg_echo = 0;
X        new.sg_alf = 0;		new.sg_nulls = 0;	new.sg_pause = 0;
X        new.sg_page = 0;	new.sg_bspch = 0;	new.sg_dlnch = 0;
X        new.sg_eorch = 0;	new.sg_eofch = 0;	new.sg_rlnch = 0;
X        new.sg_dulnch = 0;	new.sg_psch = 0;	new.sg_kbich = 0;
X        new.sg_kbach = 0;	new.sg_bsech = 0;	new.sg_bellch = 0;
X        new.sg_xon = 0;		new.sg_xoff = 0;	new.sg_tabcr = 0;
X        new.sg_tabsiz = 0;
X        _ss_opt(0, &new);
X        rl_erase = old.sg_bspch;
X        rl_kill = old.sg_dlnch;
X        rl_eof = old.sg_eofch;
X        rl_intr = old.sg_kbich;
X        rl_quit = -1;
X    }
X    else
X        _ss_opt(0, &old);
X}
X
Xvoid
Xrl_add_slash(path, p)
X    char	*path;
X    char	*p;
X{
X    (void)strcat(p, access(path, S_IREAD | S_IFDIR) ? " " : "/");
X}
END_OF_FILE
  if test 1120 -ne `wc -c <'sysos9.c'`; then
    echo shar: \"'sysos9.c'\" unpacked with wrong size!
  fi
  # end of 'sysos9.c'
fi
echo shar: End of archive.
exit 0
