[comp.sources.misc] v09i036: "ed" clone

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (11/27/89)

Posting-number: Volume 9, Issue 36
Submitted-by: unknown@unknown.UUCP (Ian Phillipps)
Archive-name: ed_ip

[Attributions and addresses lost -- please don't send compressed tar files
without prior arrangement!  ++bsa]

#! /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:  ed.c ed.man read.me
# Wrapped by allbery@uunet on Sun Nov 26 15:38:36 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'ed.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ed.c'\"
else
echo shar: Extracting \"'ed.c'\" \(27843 characters\)
sed "s/^X//" >'ed.c' <<'END_OF_FILE'
X/*
X *  ed - standard editor
X *  ~~
X *	Authors: Brian Beattie, Kees Bot, and others
X *
X * Copyright 1987 Brian Beattie Rights Reserved.
X * Permission to copy or distribute granted under the following conditions:
X * 1). No charge may be made other than reasonable charges for reproduction.
X * 2). This notice must remain intact.
X * 3). No further restrictions may be added.
X * 4). Except meaningless ones.
X *
X * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
X *  TurboC mods and cleanup 8/17/88 RAMontante.
X *  Further information (posting headers, etc.) at end of file.
X *  RE stuff replaced with Spencerian version, sundry other bugfix+speedups
X *  Ian Phillipps. Version incremented to "5".
X * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
X */
X
Xint	version = 5;	/* used only in the "set" function, for i.d. */
X
X#include <stdio.h>
X/* Regexp is Henry Spencer's package. WARNING: regsub is modified to return
X * a pointer to the \0 after the destination string, and this program refers
X * to the "private" reganch field in the struct regexp.
X */
X#include <regexp.h>
X
X#ifdef __TURBOC__
X#include <stdlib.h>
X#include <signal.h>
X#include <string.h>
X#include <ctype.h>
X#include <io.h>
X
X#define	_cleanup()	;
X
X#endif  /* ifdef __TURBOC__  */
X
X
X/*
X *	#defines for non-printing ASCII characters
X */
X#define NUL	0x00	/* ^@ */
X#define EOS	0x00	/* end of string */
X#define SOH	0x01	/* ^A */
X#define STX	0x02	/* ^B */
X#define ETX	0x03	/* ^C */
X#define EOT	0x04	/* ^D */
X#define ENQ	0x05	/* ^E */
X#define ACK	0x06	/* ^F */
X#define BEL	0x07	/* ^G */
X#define BS	0x08	/* ^H */
X#define HT	0x09	/* ^I */
X#define LF	0x0a	/* ^J */
X#define NL	'\n'
X#define VT	0x0b	/* ^K */
X#define FF	0x0c	/* ^L */
X#define CR	0x0d	/* ^M */
X#define SO	0x0e	/* ^N */
X#define SI	0x0f	/* ^O */
X#define DLE	0x10	/* ^P */
X#define DC1	0x11	/* ^Q */
X#define DC2	0x12	/* ^R */
X#define DC3	0x13	/* ^S */
X#define DC4	0x14	/* ^T */
X#define NAK	0x15	/* ^U */
X#define SYN	0x16	/* ^V */
X#define ETB	0x17	/* ^W */
X#define CAN	0x18	/* ^X */
X#define EM	0x19	/* ^Y */
X#define SUB	0x1a	/* ^Z */
X#define ESC	0x1b	/* ^[ */
X#define FS	0x1c	/* ^\ */
X#define GS	0x1d	/* ^] */
X#define RS	0x1e	/* ^^ */
X#define US	0x1f	/* ^_ */
X#define SP	0x20	/* space */
X#define DEL	0x7f	/* DEL*/
X#define ESCAPE  '\\'
X
X
X#define TRUE	1
X#define FALSE	0
X#define ERR		-2
X#define FATAL		(ERR-1)
X#define CHANGED		(ERR-2)
X#define SET_FAIL	(ERR-3)
X#define SUB_FAIL	(ERR-4)
X#define MEM_FAIL	(ERR-5)
X
X
X#define	BUFFER_SIZE	2048	/* stream-buffer size:  == 1 hd cluster */
X
X#define LINFREE	1	/* entry not in use */
X#define LGLOB	2       /* line marked global */
X
X#define MAXLINE	512	/* max number of chars per line */
X#define MAXPAT	256	/* max number of chars per replacement pattern */
X#define MAXFNAME 256	/* max file name size */
X
X
X/**  Global variables  **/
X
Xstruct	line {
X	int		l_stat;		/* empty, mark */
X	struct line	*l_prev;
X	struct line	*l_next;
X	char		l_buff[1];
X};
Xtypedef struct line	LINE;
X
X
Xint	diag = 1;		/* diagnostic-output? flag */
Xint	truncflg = 1;	/* truncate long line flag */
Xint	eightbit = 1;	/* save eighth bit */
Xint	nonascii;	/* count of non-ascii chars read */
Xint	nullchar;	/* count of null chars read */
Xint	truncated;	/* count of lines truncated */
Xchar	fname[MAXFNAME];
Xint	fchanged;	/* file-changed? flag */
Xint	nofname;
Xint	mark['z'-'a'+1];
Xregexp	*oldpat;
X
XLINE	Line0;
Xint	CurLn = 0;
XLINE	*CurPtr = &Line0;	/* CurLn and CurPtr must be kept in step */
Xint	LastLn = 0;
Xchar	inlin[MAXLINE];
Xint	pflag;
Xint	Line1, Line2, nlines;
Xint	nflg;		/* print line number flag */
Xint	lflg;		/* print line in verbose mode */
Xint	pflg;		/* print current line after each command */
Xchar	*inptr;		/* tty input buffer */
X
Xstruct tbl {
X	char	*t_str;
X	int	*t_ptr;
X	int	t_val;
X} *t, tbl[] = {
X	"number",	&nflg,		TRUE,
X	"nonumber",	&nflg,		FALSE,
X	"list",		&lflg,		TRUE,
X	"nolist",	&lflg,		FALSE,
X	"eightbit",	&eightbit,	TRUE,
X	"noeightbit",	&eightbit,	FALSE,
X	0
X};
X
X
X/*-------------------------------------------------------------------------*/
X
X#ifdef __TURBOC__		/*  prototypes (unneeded?)  */
X
Xvoid	prntln(char *, int, int);
Xvoid	putcntl(char , FILE *);
Xint	doprnt(int, int);
XLINE	*getptr(int);
Xint	del(int, int);
Xint	ins(char *);
Xint	append(int, int);
Xint	deflt(int, int);
Xregexp	*optpat(void);
Xint	egets( char*, int, FILE* );
Xint	ckglob( void );
Xint	doglob( void );
Xint	docmd( int );
Xint	dolst( int, int );
Xint	doread( int, char* );
Xint	dowrite( int, int, char*, int );
Xint	find( regexp*, int );
Xchar*	getfn( void );
Xint	getlst( void );
Xint	getnum( int );
Xint	getone( void );
Xint	getrhs( char* );
Xint	join( int, int );
Xint	move( int );
Xint	set( void );
Xint	subst( regexp*, char*, int, int );
Xint	transfer( int );
X
X#else	/* !__TURBOC__ */
X
Xextern	char	*strcpy();
Xextern	int	*malloc();
Xextern	LINE	*getptr();
Xextern	char	*gettxt();
Xextern	char	*gettxtl();
Xextern	char	*catsub();
Xregexp	*optpat();
X
X#endif  /* __TURBOC__ */
X
X
X/*________  Macros  ________________________________________________________*/
X
X#ifndef max
X#  define max(a,b)	((a) > (b) ? (a) : (b))
X#endif
X
X#ifndef min
X#  define min(a,b)	((a) < (b) ? (a) : (b))
X#endif
X
X#ifndef toupper
X#  define toupper(c)	((c >= 'a' && c <= 'z') ? c-32 : c )
X#endif
X
X#define nextln(l)	((l)+1 > LastLn ? 0 : (l)+1)
X#define prevln(l)	((l)-1 < 0 ? LastLn : (l)-1)
X
X#define gettxtl(lin)	((lin)->l_buff)
X#define gettxt(num)	(gettxtl( getptr(num) ))
X
X#define getnextptr(p)	((p)->l_next)
X#define getprevptr(p)	((p)->l_prev)
X
X#define setCurLn( lin )	( CurPtr = getptr( CurLn = (lin) ) )
X#define nextCurLn()	( CurLn = nextln(CurLn), CurPtr = getnextptr( CurPtr ) )
X#define prevCurLn()	( CurLn = prevln(CurLn), CurPtr = getprevptr( CurPtr ) )
X
X#define clrbuf()	del(1, LastLn)
X
X#define	Skip_White_Space	{while (*inptr==SP || *inptr==HT) inptr++;}
X
X#define relink(a, x, y, b) { (x)->l_prev = (a); (y)->l_next = (b); }
X
X
X/*________  functions  ______________________________________________________*/
X
X
X/*	append.c	*/
X
Xappend(line, glob)
Xint	line, glob;
X{
X	char	lin[MAXLINE];
X
X	if(glob)
X		return(ERR);
X	setCurLn( line );
X	for (;;) {
X		char *p = lin;
X		if(nflg)
X			printf("%6d. ",CurLn+1);
X		while( p < &lin[ MAXLINE ] ) {
X			int ch = getchar();
X			if( ch == EOF )
X				return EOF;
X			if( ch == '\n' ) {
X				*p = EOS; break;
X			}
X			*p++ = ch;
X		}
X		if(lin[0] == '.' && lin[1] == '\0')
X			return(0);
X		if( ins(lin) < 0)
X			return( MEM_FAIL );
X	}
X}
X
X/*	ckglob.c	*/
X
Xckglob()
X{
X	regexp	*glbpat;
X	char	c, delim, *lin;
X	int	num;
X	LINE	*ptr;
X
X	c = *inptr;
X
X	if(c != 'g' && c != 'v')
X		return(0);
X	if (deflt(1, LastLn) < 0)
X		return(ERR);
X
X	delim = *++inptr;
X	if(delim <= ' ')
X		return(ERR);
X
X	glbpat = optpat();
X	if(*inptr == delim)
X		inptr++;
X	ptr = getptr(1);
X	for (num=1; num<=LastLn; num++) {
X		ptr->l_stat &= ~LGLOB;
X		if (Line1 <= num && num <= Line2) {
X			lin = gettxtl(ptr);
X			if(regexec(glbpat, lin )) {
X				if (c=='g') ptr->l_stat |= LGLOB;
X			} else {
X				if (c=='v') ptr->l_stat |= LGLOB;
X			}
X		ptr = getnextptr(ptr);
X		}
X	}
X	return(1);
X}
X
X
X/*  deflt.c
X *	Set Line1 & Line2 (the command-range delimiters) if the file is
X *	empty; Test whether they have valid values.
X */
X
Xint deflt(def1, def2)
Xint	def1, def2;
X{
X	if(nlines == 0) {
X		Line1 = def1;
X		Line2 = def2;
X	}
X	return ( (Line1>Line2 || Line1<=0) ? ERR : 0 );
X}
X
X
X/*	del.c	*/
X
X/* One of the calls to this function tests its return value for an error
X * condition.  But del doesn't return any error value, and it isn't obvious
X * to me what errors might be detectable/reportable.  To silence a warning
X * message, I've added a constant return statement. -- RAM
X * ... It could check to<=LastLn ... igp
X */
X
Xdel(from, to)
Xint	from, to;
X{
X	LINE	*first, *last, *next, *tmp;
X
X	if(from < 1)
X		from = 1;
X	first = getprevptr( getptr( from ) );
X	last = getnextptr( getptr( to ) );
X	next = first->l_next;
X	while(next != last && next != &Line0) {
X		tmp = next->l_next;
X		free(next);
X		next = tmp;
X	}
X	relink(first, last, first, last);
X	LastLn -= (to - from)+1;
X	setCurLn( prevln(from) );
X	return(0);
X}
X
X
Xint dolst(line1, line2)
Xint line1, line2;
X{
X	int oldlflg=lflg, p;
X
X	lflg = 1;
X	p = doprnt(line1, line2);
X	lflg = oldlflg;
X	return p;
X}
X
X
X/*	esc.c
X * Map escape sequences into their equivalent symbols.  Returns the
X * correct ASCII character.  If no escape prefix is present then s
X * is untouched and *s is returned, otherwise **s is advanced to point
X * at the escaped character and the translated character is returned.
X */
Xesc(s)
Xchar	**s;
X{
X	register int	rval;
X
X	if (**s != ESCAPE) {
X		rval = **s;
X	} else {
X		(*s)++;
X		switch(toupper(**s)) {
X		case '\000':
X			rval = ESCAPE;	break;
X		case 'S':
X			rval = ' ';	break;
X		case 'N':
X			rval = '\n';	break;
X		case 'T':
X			rval = '\t';	break;
X		case 'B':
X			rval = '\b';	break;
X		case 'R':
X			rval = '\r';	break;
X		default:
X			rval = **s;	break;
X		}
X	}
X	return (rval);
X}
X
X
X/*	doprnt.c	*/
X
Xint doprnt(from, to)
Xint	from, to;
X{
X	from = (from < 1) ? 1 : from;
X	to = (to > LastLn) ? LastLn : to;
X
X	if(to != 0) {
X		setCurLn( from );
X		while( CurLn <= to ) {
X			prntln( gettxtl( CurPtr ), lflg, (nflg ? CurLn : 0));
X			if( CurLn == to )
X				break;
X			nextCurLn();
X		}
X	}
X	return(0);
X}
X
X
Xvoid prntln(str, vflg, lin)
Xchar	*str;
Xint	vflg, lin;
X{
X	if(lin)
X		printf("%7d ",lin);
X	while(*str && *str != NL) {
X		if(*str < ' ' || *str >= 0x7f) {
X			switch(*str) {
X			case '\t':
X				if(vflg)
X					putcntl(*str, stdout);
X				else
X					putc(*str, stdout);
X				break;
X
X			case DEL:
X				putc('^', stdout);
X				putc('?', stdout);
X				break;
X
X			default:
X				putcntl(*str, stdout);
X				break;
X			}
X		} else
X			putc(*str, stdout);
X		str++;
X	}
X	if(vflg)
X		putchar('$');
X	putchar('\n');
X}
X
X
Xvoid putcntl(c, stream)
Xchar	c;
XFILE	*stream;
X{
X	putc('^', stream);
X	putc((c&31)|'@', stream);
X}
X
X
X/*	egets.c	*/
X
Xegets(str,size,stream)
Xchar	*str;
Xint	size;
XFILE	*stream;
X{
X	int	c, count;
X	char	*cp;
X
X	for(count = 0, cp = str; size > count;) {
X		c = getc(stream);
X		if(c == EOF) {
X			*cp = EOS;
X			if(count)
X				puts("[Incomplete last line]");
X			return(count);
X		}
X		else if(c == NL) {
X			*cp = EOS;
X			return(++count);
X		}
X		else if (c == 0)
X			nullchar++;	/* count nulls */
X		else {
X			if(c > 127) {
X				if(!eightbit)		/* if not saving eighth bit */
X					c = c&127;	/* strip eigth bit */
X				nonascii++;		/* count it */
X			}
X			*cp++ = c;	/* not null, keep it */
X			count++;
X		}
X	}
X	str[count-1] = EOS;
X	if(c != NL) {
X		puts("truncating line");
X		truncated++;
X		while((c = getc(stream)) != EOF)
X			if(c == NL)
X				break;
X	}
X	return(count);
X}  /* egets */
X
X
Xdoread(lin, fname)
Xint	lin;
Xchar	*fname;
X{
X	FILE	*fp;
X	int	err;
X	unsigned long	bytes;
X	unsigned int	lines;
X	static char	str[MAXLINE];
X
X	err = 0;
X	nonascii = nullchar = truncated = 0;
X
X	if (diag) printf("\"%s\" ",fname);
X	if( (fp = fopen(fname, "r")) == NULL ) {
X		puts(" isn't readable.");
X		return( ERR );
X	}
X	setvbuf(fp, NULL, _IOFBF, BUFFER_SIZE);
X	setCurLn( lin );
X	for(lines = 0, bytes = 0;(err = egets(str,MAXLINE,fp)) > 0;) {
X		bytes += err;
X		if(ins(str) < 0) {
X			err = MEM_FAIL;
X			break;
X		}
X		lines++;
X	}
X	fclose(fp);
X	if(err < 0)
X		return(err);
X	if (diag) {
X		printf("%u lines %u bytes",lines,bytes);
X		if(nonascii)
X			printf(" [%d non-ascii]",nonascii);
X		if(nullchar)
X			printf(" [%d nul]",nullchar);
X		if(truncated)
X			printf(" [%d lines truncated]",truncated);
X		putchar('\n');
X	}
X	return( err );
X}  /* doread */
X
X
Xint dowrite(from, to, fname, apflg)
Xint	from, to;
Xchar	*fname;
Xint	apflg;
X{
X	FILE	*fp;
X	int	lin, err;
X	unsigned int	lines;
X	unsigned long	bytes;
X	char	*str;
X	LINE	*lptr;
X
X	err = 0;
X	lines = bytes = 0;
X
X	printf("\"%s\" ",fname);
X	if((fp = fopen(fname,(apflg?"a":"w"))) == NULL) {
X		puts(" can't be opened for writing!");
X		return( ERR );
X	}
X
X	setvbuf(fp, NULL, _IOFBF, BUFFER_SIZE);
X	lptr = getptr(from);
X	for(lin = from; lin <= to; lin++) {
X		str = lptr->l_buff;
X		lines++;
X		bytes += strlen(str) + 1;	/* str + '\n' */
X		if(fputs(str, fp) == EOF) {
X			puts("file write error");
X			err++;
X			break;
X		}
X		fputc('\n', fp);
X		lptr = lptr->l_next;
X	}
X	printf("%u lines %lu bytes\n",lines,bytes);
X	fclose(fp);
X	return( err );
X}  /* dowrite */
X
X
X/*	find.c	*/
X
Xfind(pat, dir)
Xregexp	*pat;
Xint	dir;
X{
X	int	i, num;
X	LINE	*lin;
X
X	num = CurLn;
X	lin = CurPtr;
X	for(i=0; i<LastLn; i++ ) {
X		if(regexec( pat, gettxtl( lin ) ))
X			return(num);
X		if( dir )
X			num = nextln(num), lin = getnextptr(lin);
X		else
X			num = prevln(num), lin = getprevptr(lin);
X	}
X	return ( ERR );
X}
X
X
X/*	getfn.c	*/
X
Xchar *getfn()
X{
X	static char	file[256];
X	char	*cp;
X
X	if(*inptr == NL) {
X		nofname=TRUE;
X		strcpy(file, fname);
X	} else {
X		nofname=FALSE;
X		Skip_White_Space;
X
X		cp = file;
X		while(*inptr && *inptr != NL && *inptr != SP && *inptr != HT)
X			*cp++ = *inptr++;
X		*cp = '\0';
X
X		if(strlen(file) == 0) {
X			puts("bad file name");
X			return( NULL );
X		}
X	}
X
X	if(strlen(file) == 0) {
X		puts("no file name");
X		return(NULL);
X	}
X	return( file );
X}  /* getfn */
X
X
Xint getnum(first)
Xint first;
X{
X	regexp	*srchpat;
X	int	num;
X	char	c;
X
X	Skip_White_Space;
X
X	if(*inptr >= '0' && *inptr <= '9') {	/* line number */
X		for(num = 0; *inptr >= '0' && *inptr <= '9'; ++inptr) {
X			num = (num * 10) + (*inptr - '0');
X		}
X		return num;
X	}
X
X	switch(c = *inptr) {
X	case '.':
X		inptr++;
X		return (CurLn);
X
X	case '$':
X		inptr++;
X		return (LastLn);
X
X	case '/':
X	case '?':
X		srchpat = optpat();
X		if(*inptr == c)
X			*inptr++;
X		return(find(srchpat,c == '/'?1:0));
X
X	case '-':
X	case '+':
X		return(first ? CurLn : 1);
X
X	case '\'':
X		inptr++;
X		if (*inptr < 'a' || *inptr > 'z')
X			return(EOF);
X		return mark[ *inptr++ - 'a' ];
X
X	default:
X		return ( first ? EOF : 1 );	/* unknown address */
X	}
X}  /* getnum */
X
X
X/*  getone.c
X *	Parse a number (or arithmetic expression) off the command line.
X */
X#define FIRST 1
X#define NOTFIRST 0
X
Xint getone()
X{
X	int	c, i, num;
X
X	if((num = getnum(FIRST)) >= 0) {
X		for (;;) {
X			Skip_White_Space;
X			if(*inptr != '+' && *inptr != '-')
X				break;	/* exit infinite loop */
X
X                        c = *inptr++;
X			if((i = getnum(NOTFIRST)) < 0)
X				return ( i );
X			if(c == '+')
X				num += i;
X			else
X				num -= i;
X		}
X	}
X	return ( num>LastLn ? ERR : num );
X}  /* getone */
X
X
Xgetlst()
X{
X	int	num;
X
X	Line2 = 0;
X	for(nlines = 0; (num = getone()) >= 0;)
X	{
X		Line1 = Line2;
X		Line2 = num;
X		nlines++;
X		if(*inptr != ',' && *inptr != ';')
X			break;
X		if(*inptr == ';')
X			setCurLn( num );
X		inptr++;
X	}
X	nlines = min(nlines, 2);
X	if(nlines == 0)
X		Line2 = CurLn;
X	if(nlines <= 1)
X		Line1 = Line2;
X
X	return ( (num == ERR) ? num : nlines );
X}  /* getlst */
X
X
X/*	getptr.c	*/
X
XLINE *getptr(num)
Xint	num;
X{
X	LINE	*ptr;
X	int	j;
X
X	if (2*num>LastLn && num<=LastLn) {	/* high line numbers */
X		ptr = Line0.l_prev;
X		for (j = LastLn; j>num; j--)
X			ptr = ptr->l_prev;
X	} else {				/* low line numbers */
X		ptr = &Line0;
X		for(j = 0; j < num; j++)
X			ptr = ptr->l_next;
X	}
X	return(ptr);
X}
X
X
X/*	getrhs.c	*/
X
Xint getrhs(sub)
Xchar	*sub;
X{
X	char delim = *inptr++;
X	char *outmax = sub + MAXPAT;
X	if( delim == NL || *inptr == NL)	/* check for eol */
X		return( ERR );
X	while( *inptr != delim && *inptr != NL ) {
X		if ( sub > outmax )
X			return ERR;
X		if ( *inptr == ESCAPE ) {
X			switch ( *++inptr ) {
X			case 'r':
X				*sub++ = '\r';
X				inptr++;
X				break;
X			case ESCAPE:
X				*sub++ = ESCAPE;
X				*sub++ = ESCAPE;
X				inptr++;
X			case 'n':
X				*sub++ = '\n';
X				inptr++;
X				break;
X			case 'b':
X				*sub++ = '\b';
X				inptr++;
X				break;
X			case '0': {
X				int i=3;
X				*sub = 0;
X				do {
X					if (*++inptr<'0' || *inptr >'7')
X						break;
X					*sub = (*sub<<3) | (*inptr-'0');
X				} while (--i!=0);
X				sub++;
X				} break;
X			default:
X				if ( *inptr != delim )
X					*sub++ = ESCAPE;
X				*sub++ = *inptr;
X				if ( *inptr != NL )
X					inptr++;
X			}
X		}
X		else *sub++ = *inptr++;
X	}
X	*sub = '\0';
X
X	inptr++;		/* skip over delimter */
X	Skip_White_Space;
X	if(*inptr == 'g') {
X		*inptr++;
X		return( 1 );
X	}
X	return( 0 );
X}
X
X/*	ins.c	*/
X
Xins(str)
Xchar	*str;
X{
X	char	*cp;
X	LINE	*new, *nxt;
X	int	len;
X
X	do {
X		for ( cp = str; *cp && *cp != NL; cp++ )
X			;
X		len = cp - str;
X		/* cp now points to end of first or only line */
X
X		if((new = (LINE *)malloc(sizeof(LINE)+len)) == NULL)
X			return( MEM_FAIL ); 	/* no memory */
X
X		new->l_stat=0;
X		strncpy(new->l_buff,str,len);	/* build new line */
X		new->l_buff[len] = EOS;
X		nxt = getnextptr(CurPtr);	/* get next line */
X		relink(CurPtr, new, new, nxt);	/* add to linked list */
X		relink(new, nxt, CurPtr, new);
X		LastLn++;
X		CurLn++;
X		CurPtr = new;
X		str = cp + 1;
X	}
X		while( *cp != EOS );
X	return 1;
X}
X
X
X/*	join.c	*/
X
Xint join(first, last)
Xint first, last;
X{
X	char buf[MAXLINE];
X	char *cp=buf, *str;
X	LINE *lin;
X	int num;
X
X	if (first<=0 || first>last || last>LastLn)
X		return(ERR);
X	if (first==last) {
X		setCurLn( first );
X		return 0;
X	}
X	lin = getptr(first);
X	for (num=first; num<=last; num++) {
X		str=gettxtl(lin);
X		while ( *str ) {
X			if (cp >= buf + MAXLINE-1 ) {
X				puts("line too long");
X				return(ERR);
X			}
X			*cp++ = *str++;
X		}
X		lin = getnextptr(lin);
X	}
X	*cp = EOS;
X	del(first, last);
X	if( ins(buf) < 0 )
X		return MEM_FAIL;
X	fchanged = TRUE;
X	return 0;
X}
X
X
X/*  move.c
X *	Unlink the block of lines from Line1 to Line2, and relink them
X *	after line "num".
X */
X
Xint move(num)
Xint	num;
X{
X	int	range;
X	LINE	*before, *first, *last, *after;
X
X	if( Line1 <= num && num <= Line2 )
X		return( ERR );
X	range = Line2 - Line1 + 1;
X	before = getptr(prevln(Line1));
X	first = getptr(Line1);
X	last = getptr(Line2);
X	after = getptr(nextln(Line2));
X
X	relink(before, after, before, after);
X	LastLn -= range;	/* per AST's posted patch 2/2/88 */
X	if (num > Line1)
X		num -= range;
X
X	before = getptr(num);
X	after = getptr(nextln(num));
X	relink(before, first, last, after);
X	relink(last, after, before, first);
X	LastLn += range;	/* per AST's posted patch 2/2/88 */
X	setCurLn( num + range );
X	return( 1 );
X}
X
X
Xint transfer(num)
Xint num;
X{
X	int mid, lin, ntrans;
X
X	if (Line1<=0 || Line1>Line2)
X		return(ERR);
X
X	mid= num<Line2 ? num : Line2;
X
X	setCurLn( num );
X	ntrans=0;
X
X	for (lin=Line1; lin<=mid; lin++) {
X		if( ins(gettxt(lin)) < 0 )
X			return MEM_FAIL;
X		ntrans++;
X	}
X	lin+=ntrans;
X	Line2+=ntrans;
X
X	for ( ; lin <= Line2; lin += 2 ) {
X		if( ins(gettxt(lin)) < 0 )
X			return MEM_FAIL;
X		Line2++;
X	}
X	return(1);
X}
X
X
X/*	optpat.c	*/
X
Xregexp *optpat(void)
X{
X	char	delim, str[MAXPAT], *cp;
X
X	delim = *inptr++;
X	cp = str;
X	while(*inptr != delim && *inptr != NL) {
X		if(*inptr == ESCAPE && inptr[1] != NL)
X			*cp++ = *inptr++;
X		*cp++ = *inptr++;
X	}
X
X	*cp = EOS;
X	if(*str == EOS)
X		return(oldpat);
X	if(oldpat)
X		free(oldpat);
X	return oldpat = regcomp(str);
X}
X
X/* regerror.c */
Xvoid regerror( char *s )
X{
X	fprintf( stderr, "ed: %s\n", s );
X}
X
X
Xset()
X{
X	char	word[16];
X	int	i;
X
X	if(*(++inptr) != 't') {
X		if(*inptr != SP && *inptr != HT && *inptr != NL)
X			return(ERR);
X	} else
X		inptr++;
X
X	if ( (*inptr == NL)
X#ifdef __TURBOC__
X	    || (*inptr == CR)
X#endif
X	   )
X	{
X		printf("ed version %d.%d\n", version/100, version%100);
X		printf(	"number %s, list %s\n",
X			nflg?"ON":"OFF",
X			lflg?"ON":"OFF");
X		return(0);
X	}
X
X	Skip_White_Space;
X	for(i = 0; *inptr != SP && *inptr != HT && *inptr != NL;)
X		word[i++] = *inptr++;
X	word[i] = EOS;
X	for(t = tbl; t->t_str; t++) {
X		if(strcmp(word,t->t_str) == 0) {
X			*t->t_ptr = t->t_val;
X			return(0);
X		}
X	}
X	return SET_FAIL;
X}
X
X#ifndef relink
Xvoid relink(a, x, y, b)
XLINE	*a, *x, *y, *b;
X{
X	x->l_prev = a;
X	y->l_next = b;
X}
X#endif
X
X
Xvoid set_ed_buf(void)
X{
X	relink(&Line0, &Line0, &Line0, &Line0);
X	CurLn = LastLn = 0;
X	CurPtr = &Line0;
X}
X
X
X/*	subst.c	*/
X
Xint subst(pat, sub, gflg, pflag)
Xregexp	*pat;
Xchar	*sub;
Xint	gflg, pflag;
X{
X	int	nchngd = 0;
X	char	*txtptr;
X	char	*new, buf[MAXLINE];
X	int	still_running = 1;
X	LINE	*lastline = getptr( Line2 );
X
X	if(Line1 <= 0)
X		return( SUB_FAIL );
X	nchngd = 0;		/* reset count of lines changed */
X
X	for( setCurLn( prevln( Line1 ) ); still_running; ) {
X		nextCurLn();
X		new = buf;
X		if ( CurPtr == lastline )
X			still_running = 0;
X		if ( regexec( pat, txtptr = gettxtl( CurPtr ) ) ) {
X			do
X				{
X				/* Copy leading text */
X				int diff = pat->startp[0] - txtptr;
X				strncpy( new, txtptr, diff );
X				new += diff;
X				/* Do substitution */
X				new = regsub( pat, sub, new );
X				txtptr = pat->endp[0];
X				}
X			while( gflg && !pat->reganch && regexec( pat, txtptr ));
X
X			/* Copy trailing chars */
X			while( *txtptr ) *new++ = *txtptr++;
X
X			if(new >= buf+MAXLINE)
X				return( SUB_FAIL );
X			*new++ = EOS;
X			del(CurLn,CurLn);
X			if( ins(buf) < 0 )
X				return MEM_FAIL;
X			nchngd++;
X			if(pflag)
X				doprnt(CurLn, CurLn);
X		}
X        }
X	return (( nchngd == 0 && !gflg ) ? SUB_FAIL : nchngd);
X}
X
X
X/*	system.c	*/
X#ifndef __TURBOC__
X
X#define SHELL	"/bin/sh"
X
Xsystem(c)
Xchar *c; {
X	int pid, status;
X
X	switch (pid = fork()) {
X	case -1:
X		return -1;
X	case 0:
X		execl(SHELL, "sh", "-c", c, (char *) 0);
X		exit(-1);
X	default:
X		while (wait(&status) != pid)
X			;
X	}
X	return status;
X}
X#endif  /* ifndef __TURBOC__ */
X
X
X/*  docmd.c
X *	Perform the command specified in the input buffer, as pointed to
X *	by inptr.  Actually, this finds the command letter first.
X */
X
Xint docmd(glob)
Xint	glob;
X{
X	static char	rhs[MAXPAT];
X	regexp	*subpat;
X	int	c, err, line3;
X	int	apflg, pflag, gflag;
X	int	nchng;
X	char	*fptr;
X
X	pflag = FALSE;
X	Skip_White_Space;
X
X	c = *inptr++;
X	switch(c) {
X	case NL:
X		if( nlines == 0 && (Line2 = nextln(CurLn)) == 0 )
X			return(ERR);
X		setCurLn( Line2 );
X		return (1);
X
X	case '=':
X		printf("%d\n",Line2);
X		break;
X
X	case 'a':
X		if(*inptr != NL || nlines > 1)
X			return(ERR);
X
X		if(append(Line1, glob) < 0)
X			return(ERR);
X		fchanged = TRUE;
X		break;
X
X	case 'c':
X		if(*inptr != NL)
X			return(ERR);
X
X		if(deflt(CurLn, CurLn) < 0)
X			return(ERR);
X
X		if(del(Line1, Line2) < 0)
X			return(ERR);
X		if(append(CurLn, glob) < 0)
X			return(ERR);
X		fchanged = TRUE;
X		break;
X
X	case 'd':
X		if(*inptr != NL)
X			return(ERR);
X
X		if(deflt(CurLn, CurLn) < 0)
X			return(ERR);
X
X		if(del(Line1, Line2) < 0)
X			return(ERR);
X		if(nextln(CurLn) != 0)
X			nextCurLn();
X		fchanged = TRUE;
X		break;
X
X	case 'e':
X		if(nlines > 0)
X			return(ERR);
X		if(fchanged)
X			return CHANGED;
X		/*FALL THROUGH*/
X	case 'E':
X		if(nlines > 0)
X			return(ERR);
X
X		if(*inptr != ' ' && *inptr != HT && *inptr != NL)
X			return(ERR);
X
X		if((fptr = getfn()) == NULL)
X			return(ERR);
X
X		clrbuf();
X		if((err = doread(0, fptr)) < 0)
X			return(err);
X
X		strcpy(fname, fptr);
X		fchanged = FALSE;
X		break;
X
X	case 'f':
X		if(nlines > 0)
X			return(ERR);
X
X		if(*inptr != ' ' && *inptr != HT && *inptr != NL)
X			return(ERR);
X
X		if((fptr = getfn()) == NULL)
X			return(ERR);
X
X		if (nofname)
X			printf("%s\n", fname);
X		else
X			strcpy(fname, fptr);
X		break;
X
X	case 'i':
X		if(*inptr != NL || nlines > 1)
X			return(ERR);
X
X		if(append(prevln(Line1), glob) < 0)
X			return(ERR);
X		fchanged = TRUE;
X		break;
X
X	case 'j':
X		if (*inptr != NL || deflt(CurLn, CurLn+1)<0)
X			return(ERR);
X
X		if (join(Line1, Line2) < 0)
X			return(ERR);
X		break;
X
X	case 'k':
X		Skip_White_Space;
X
X		if (*inptr < 'a' || *inptr > 'z')
X			return ERR;
X		c= *inptr++;
X
X		if(*inptr != ' ' && *inptr != HT && *inptr != NL)
X			return(ERR);
X
X		mark[c-'a'] = Line1;
X		break;
X
X	case 'l':
X		if(*inptr != NL)
X			return(ERR);
X		if(deflt(CurLn,CurLn) < 0)
X			return(ERR);
X		if (dolst(Line1,Line2) < 0)
X			return(ERR);
X		break;
X
X	case 'm':
X		if((line3 = getone()) < 0)
X			return(ERR);
X		if(deflt(CurLn,CurLn) < 0)
X			return(ERR);
X		if(move(line3) < 0)
X			return(ERR);
X		fchanged = TRUE;
X		break;
X
X	case 'P':
X	case 'p':
X		if(*inptr != NL)
X			return(ERR);
X		if(deflt(CurLn,CurLn) < 0)
X			return(ERR);
X		if(doprnt(Line1,Line2) < 0)
X			return(ERR);
X		break;
X
X	case 'q':
X		if(fchanged)
X			return CHANGED;
X		/*FALL THROUGH*/
X	case 'Q':
X		if(*inptr == NL && nlines == 0 && !glob)
X			return(EOF);
X		else
X			return(ERR);
X
X	case 'r':
X		if(nlines > 1)
X			return(ERR);
X
X		if(nlines == 0)			/* The original code tested */
X			Line2 = LastLn;		/*	if(nlines = 0)	    */
X						/* which looks wrong.  RAM  */
X
X		if(*inptr != ' ' && *inptr != HT && *inptr != NL)
X			return(ERR);
X
X		if((fptr = getfn()) == NULL)
X			return(ERR);
X
X		if((err = doread(Line2, fptr)) < 0)
X			return(err);
X		fchanged = TRUE;
X		break;
X
X	case 's':
X		if(*inptr == 'e')
X			return(set());
X		Skip_White_Space;
X		if((subpat = optpat()) == NULL)
X			return(ERR);
X		if((gflag = getrhs(rhs)) < 0)
X			return(ERR);
X		if(*inptr == 'p')
X			pflag++;
X		if(deflt(CurLn, CurLn) < 0)
X			return(ERR);
X		if((nchng = subst(subpat, rhs, gflag, pflag)) < 0)
X			return(ERR);
X		if(nchng)
X			fchanged = TRUE;
X		break;
X
X	case 't':
X		if((line3 = getone()) < 0)
X			return(ERR);
X		if(deflt(CurLn,CurLn) < 0)
X			return(ERR);
X		if(transfer(line3) < 0)
X			return(ERR);
X		fchanged = TRUE;
X		break;
X
X	case 'W':
X	case 'w':
X		apflg = (c=='W');
X
X		if(*inptr != ' ' && *inptr != HT && *inptr != NL)
X			return(ERR);
X
X		if((fptr = getfn()) == NULL)
X			return(ERR);
X
X		if(deflt(1, LastLn) < 0)
X			return(ERR);
X		if(dowrite(Line1, Line2, fptr, apflg) < 0)
X			return(ERR);
X		fchanged = FALSE;
X		break;
X
X	case 'x':
X		if(*inptr == NL && nlines == 0 && !glob) {
X			if((fptr = getfn()) == NULL)
X				return(ERR);
X			if(dowrite(1, LastLn, fptr, 0) >= 0)
X				return(EOF);
X		}
X		return(ERR);
X
X	case 'z':
X		if(deflt(CurLn,CurLn) < 0)
X			return(ERR);
X
X		switch(*inptr) {
X		case '-':
X			if(doprnt(Line1-21,Line1) < 0)
X				return(ERR);
X			break;
X
X		case '.':
X			if(doprnt(Line1-11,Line1+10) < 0)
X				return(ERR);
X			break;
X
X		case '+':
X		case '\n':
X			if(doprnt(Line1,Line1+21) < 0)
X				return(ERR);
X			break;
X		}
X		break;
X
X	default:
X		return(ERR);
X	}
X	return (0);
X}  /* docmd */
X
X
X/*	doglob.c	*/
Xdoglob()
X{
X	int	lin, stat;
X	char	*cmd;
X	LINE	*ptr;
X
X	cmd = inptr;
X
X	for (;;) {
X		ptr = getptr(1);
X		for (lin=1; lin<=LastLn; lin++) {
X			if (ptr->l_stat & LGLOB)
X				break;
X			ptr = getnextptr(ptr);
X		}
X		if (lin > LastLn)
X			break;
X
X		ptr->l_stat &= ~LGLOB;
X		CurLn = lin; CurPtr = ptr;
X		inptr = cmd;
X		if((stat = getlst()) < 0)
X			return(stat);
X		if((stat = docmd(1)) < 0)
X			return(stat);
X	}
X	return(CurLn);
X}  /* doglob */
X
X
X/*
X *  Software signal 2 or SIGINT is caught and the result is to resume
X *  the main loop at a command prompt.
X */
X#include <setjmp.h>
Xjmp_buf	env;
X
X#ifndef __TURBOC__
Xintr()
X{
X	puts("intr()?");
X	longjmp(env, 1);
X}
X
X#else
X
Xvoid Catcher(void)
X{
X	longjmp(env, 1);
X}
X#endif	/* !__TURBOC__ */
X
X
X/*--------  main  ---------------------------------------------------------*/
X#ifndef _Cdecl
X# define _Cdecl
X#endif
X
Xvoid _Cdecl main(argc,argv)
Xint	argc;
Xchar	**argv;
X{
X	int	stat, i, doflush;
X
X	set_ed_buf();
X	doflush=isatty(1);
X
X	if (argc>1 && argv[1][0]=='-' && argv[1][1]==0) {
X		diag = 0;
X		argc--;
X		argv++;
X	}
X	if(argc > 1) {
X		for(i = 1; i < argc; i++) {
X			if(doread(0,argv[i])==0) {
X				setCurLn( 1 );
X				strcpy(fname, argv[i]);
X				break;
X			}
X		}
X	}
X
X	for (;;) {
X
X		setjmp(env);
X		putchar(':');		/*  The command-line prompt  */
X
X#ifndef __TURBOC__
X		signal(2, intr);
X#else	/* __TURBOC__ */
X		signal(SIGINT, Catcher);
X#endif	/* !__TURBOC__ */
X
X		if (doflush)
X			fflush(stdout);
X		if (fgets(inlin, sizeof(inlin),stdin) == NULL) {
X			puts("Null input.");
X			break;
X		}
X		if(*inlin == '!') {
X			for(inptr = inlin; *inptr != NL; inptr++)
X				;
X			*inptr = EOS;
X			system(inlin+1);
X			continue;
X		}
X		inptr = inlin;
X		if(getlst() >= 0)
X			if((stat = ckglob()) != 0) {
X				if(stat >= 0 && (stat = doglob()) >= 0) {
X					setCurLn( stat );
X					continue;
X				}
X			} else {
X				if((stat = docmd(0)) >= 0) {
X					if(stat == 1)
X						doprnt(CurLn, CurLn);
X					continue;
X				}
X			}
X		switch (stat) {
X		case EOF:
X			_cleanup(); exit(0);
X		case FATAL:
X			fputs("FATAL ERROR\n",stderr);
X			_cleanup(); exit(1);
X		case CHANGED:
X			puts("File has been changed.");
X			break;
X		case SET_FAIL:
X			puts("`set' command failed.");
X			break;
X		case SUB_FAIL:
X			puts("string substitution failed.");
X			break;
X		case MEM_FAIL:
X			puts("Out of memory: text may have been lost." );
X			break;
X		default:
X			puts("Oops?");
X			/*  Unrecognized or failed command (this  */
X			/*  is SOOOO much better than "?" :-)	  */
X		}
X	}
X}  /* main */
X/*________  end of source code  ____________________________________________*/
END_OF_FILE
if test 27843 -ne `wc -c <'ed.c'`; then
    echo shar: \"'ed.c'\" unpacked with wrong size!
fi
# end of 'ed.c'
fi
if test -f 'ed.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ed.man'\"
else
echo shar: Extracting \"'ed.man'\" \(6675 characters\)
sed "s/^X//" >'ed.man' <<'END_OF_FILE'
Xed - line-oriented text editor, similar to UN*X V7 ed
X
Xsyntax:
X	ed [existing-file]
X
X	existing-file  Only an existing file can be opened.  Otherwise
X		a nameless edit buffer is opened; a name can be used with
X		the `f' or `w' commands.
X
XAs a line editor, ed operates in one of two modes: COMMAND mode, in which
Xa colon is displayed at the screen's bottom to prompt for a command; and
XINPUT mode, in which all keyboard input is added to the file (edit buffer).
XFrom COMMAND mode, INPUT mode is entered by either the `i' or `a' commands.
XFrom INPUT mode, the COMMAND mode is restored by entering a line consisting
Xof a single period by itself.  If such a line is desired in the file, it
Xcan be created by entering (for instance) two periods, then using the `s'
Xcommand to change these to only one period.
X
XA command consists of an optional line-range specification, a single char-
Xacter indicating the command, and for some commands an optional third
Xargument.  The line-range specification is either a single line number or
Xa first-line number and a last-line number separated by a comma.  The
Xcharacter `^' means the first line of the file; `$' means the last line
Xof the file.
X
XCommands:
X    <newline>
X	If a line is specified, make that the new current line.
X	Otherwise advance the current-line-pointer by one line.
X    =	Print the line number of the current line.
X    .	(by itself)  Print the current line.
X    -	(by itself)  Move the current-line-pointer back one, and print
X	the new current line.
X    +	(by itself)  Move the current-line-pointer forward one, and
X	print the new current line.
X    !	Execute a shell command and return.
X    a	Go into INPUT mode with a new line following the current line.
X	(INPUT mode is terminated by an input line containing only a
X	period in the first column.)
X    i	Go into INPUT mode with a new line preceding the current line.
X	(INPUT mode is terminated by an input line containing only a
X	period in the first column.)
X    c	Delete the specified lines (or the current line) and then
X	add new lines in their place.  This is equivalent to a `d'
X	command followed by an `i' command.
X    d	Delete the specified range of lines (or the current line).
X	Leave the current-line-pointer at the following line.
X    e	Clear the edit buffer and begin editing a new file.  This
X	command fails if the buffer contains changes (or new lines)
X	which have not been written out.  To discard these changes
X	and edit a new file, use `E' instead of `e'.
X    E	Clear the edit buffer and begin editing a new file, regardless
X	of any changes to the current edit buffer.
X    f	Print the filename, or set it to a new name if specified.
X    g	Perform the following command on all matching lines in range
X    j	Join the addressed lines together (or the current line to the
X	previous line).
X    k	Mark the addressed line with the specified letter.  Example:
X	`17ka' puts mark "a" on line 17.
X    l	List the addressed lines, showing all non-printing characters
X	and indicating the end-of-line.
X    m	Move the specified range of lines to follow the line number
X	given.  Example:  `5,7m3' moves lines 5 through 7 "up", to
X	follow line 3.
X  p,P	Print the specified lines.
X    q	Quit the editor.  This fails if the edit buffer contains any
X	changes.  If so, use `Q' instead.
X    Q	Quit the editor absolutely.  Any changes are discarded.
X    r	Read in a file, adding it after the current line.
X    s	Substitute text on the current line.  Example:  `s/alpha/beta/'
X	finds the string "alpha" and replaces it with "beta".
X    t	Transfer (copy) the specified range of lines to follow the line
X	number given.  Example:  `5,7t7' puts a copy of lines 5 through
X	7 after line 7.
X    v	Perform the following command on all non-matching lines in range
X  w,W	Write the edit buffer out.  If a filename is given, it is used
X	and becomes the current filename.  If a range of lines is
X	specified, only those lines are written.
X    x	Write the entire buffer out to its file, and terminate.	
X    z	Print 21 lines.  `-', `.', or `+' may be given, and mean
X	"start 21 lines previous, end at current line", 
X	"start 11 lines previous, end 10 lines hence", or
X	"start at current line, end 21 lines from here", respectively.
X
XThe syntax of g and v is  <addrs> g /re/ <command>
X
XBUGS and COMMENTS
X
XThere's no "u" command. Reading files is still pretty slow. IGP.
X
X[Here's an example of how to split a line:  .s/BUGS/BU\nGS/   
XThe backslash n (\n) ends the current line after the U and starts another
Xline beginning with G.  -Ed L]
X
XThe code started out as many small files.  It is now one file, but the
Xresult isn't as clean as it could be.  I've cleaned it up some, but in
Xconverting it to TurboC compliancy I've probably lost generic C compliancy.
XHeaders describing its distribution history are appended to the code.
X
X[The i variable in function doprnt() has been eliminated and replaced by
Xthe global variable CurLn.  MSDOS signal code has been added to intercept 
XCTRL C and CTRL [BREAK] so that the program does not abort your work when 
Xyou press either of these key sequences.  -Ed L]
X
XAUTHORS
X
XBrian Beattie seems to be the original author.  Kees Bot is associated
Xwith it.  Andy Tanenbaum ported it to MINIX, and posted it to Usenet.
XBob Montante ported it to MSDOS and did some minor dressing-up.
XSomebody called Ed L added some comments above.
X
XThis version is derived from the one posted in comp.binaries.ibm.pc in ?early
X1988.
XIan Phillipps replaced the regexp stuff with Henry Spencer's version, and
Xdid some other speed-ups and tinkerings. See below.
XLewis Carroll wrote Alice's Adventures in Wonderland;
XJames Joyce wrote Finnegans Wake; they are clearly the spiritual wellsprings.
X[I think Dennis Ritchie deserves a mention here, too. IGP]
X
X[The ed.c source contains an untoggled switch for displaying line
Xnumbers and another for stripping high bits off characters.  If you
Xtype "se" at the ":" prompt, you will get a decimal version number.
X-Ed L]
X
XChanges by IGP since the last comp.binaries.ibm.pc posting:
X
XRegular expression code replaced with Henry Spencer's (lightly hacked).
XUnneccessary text copying eliminated, and the remaining quadratic-time
Xalgorithms to track line numbers removed. The program now remembers a pointer
Xto the current line.
XAll this has speeded up "g" by a factor of 20-50, so now I've documented it :-).
XLogic fixed in egets to eliminate redundant tests.
XA bug in <multiline> s/.../\n/ has been fixed.
XAll out-of-memory errors should now be reported (many were ignored before).
XA Turbo-C dependency ("cdecl") has been #ifdefed.
XAll routines now have prototypes.
XIan Phillipps (probably contactable via ex-igp@camcon.co.uk,
Xbut don't bank on it)
END_OF_FILE
if test 6675 -ne `wc -c <'ed.man'`; then
    echo shar: \"'ed.man'\" unpacked with wrong size!
fi
# end of 'ed.man'
fi
if test -f 'read.me' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'read.me'\"
else
echo shar: Extracting \"'read.me'\" \(3023 characters\)
sed "s/^X//" >'read.me' <<'END_OF_FILE'
XHere is a version of the unix "ed" editor.
XFor its history, please see the manual page.
X
XThis source uses a very slightly hacked version of Henry Spencer's
XRegexp package. The only difference from Henry's original is that regsub
Xnow returns a value, so it should be compatible with other uses of this
Xpackage.
X
XI'm sure that Henry's package is in an archive server somewhere, but I
Xjust mailed him (for his address, look in /usr/spool/news/.... :-)
Xand was sent them by return. Don't you all do the same, though!
X
XHere are the diffs from Henry's original:
X
X*** regexp.h.orig	Fri Sep 29 11:23:29 1989
X--- regexp.h	Wed Nov 22 12:31:41 1989
X***************
X*** 15,21 ****
X  	char program[1];	/* Unwarranted chumminess with compiler. */
X  } regexp;
X  
X  extern regexp *regcomp();
X  extern int regexec();
X! extern void regsub();
X  extern void regerror();
X--- 15,29 ----
X  	char program[1];	/* Unwarranted chumminess with compiler. */
X  } regexp;
X  
X+ #ifdef __STDC__
X+ extern regexp *regcomp( char * );
X+ extern int regexec( regexp*, char* );
X+ extern char *regsub( regexp*, char*, char* );
X+ extern void regerror( char* );
X+ #else
X  extern regexp *regcomp();
X  extern int regexec();
X! extern char *regsub();
X  extern void regerror();
X+ #endif
X*** regsub.c.orig	Fri Sep 29 11:23:33 1989
X--- regsub.c	Wed Nov 22 12:18:52 1989
X***************
X*** 18,23 ****
X--- 18,27 ----
X   *
X   *	3. Altered versions must be plainly marked as such, and must not
X   *		be misrepresented as being the original software.
X+  *
X+  * This version modified by Ian Phillipps to return pointer to terminating
X+  * NUL on substitution string. [ Temp mail address ex-igp@camcon.co.uk ]
X+  *
X   */
X  #include <stdio.h>
X  #include <regexp.h>
X***************
X*** 32,38 ****
X  /*
X   - regsub - perform substitutions after a regexp match
X   */
X! void
X  regsub(prog, source, dest)
X  regexp *prog;
X  char *source;
X--- 36,43 ----
X  /*
X   - regsub - perform substitutions after a regexp match
X   */
X! char *
X  regsub(prog, source, dest)
X  regexp *prog;
X  char *source;
X***************
X*** 47,57 ****
X  
X  	if (prog == NULL || source == NULL || dest == NULL) {
X  		regerror("NULL parm to regsub");
X! 		return;
X  	}
X  	if (UCHARAT(prog->program) != MAGIC) {
X  		regerror("damaged regexp fed to regsub");
X! 		return;
X  	}
X  
X  	src = source;
X--- 52,62 ----
X  
X  	if (prog == NULL || source == NULL || dest == NULL) {
X  		regerror("NULL parm to regsub");
X! 		return NULL;
X  	}
X  	if (UCHARAT(prog->program) != MAGIC) {
X  		regerror("damaged regexp fed to regsub");
X! 		return NULL;
X  	}
X  
X  	src = source;
X***************
X*** 74,82 ****
X  			dst += len;
X  			if (len != 0 && *(dst-1) == '\0') {	/* strncpy hit NUL. */
X  				regerror("damaged match string");
X! 				return;
X  			}
X  		}
X  	}
X! 	*dst++ = '\0';
X  }
X--- 79,89 ----
X  			dst += len;
X  			if (len != 0 && *(dst-1) == '\0') {	/* strncpy hit NUL. */
X  				regerror("damaged match string");
X! 				return NULL;
X  			}
X  		}
X  	}
X! 	*dst = '\0';
X! 	return dst;
X  }
X*************** end of diffs *****************
END_OF_FILE
if test 3023 -ne `wc -c <'read.me'`; then
    echo shar: \"'read.me'\" unpacked with wrong size!
fi
# end of 'read.me'
fi
echo shar: End of shell archive.
exit 0