/* Copyright 1987 Brian Beattie Rights Reserved.
 *
 * Permission to copy and/or distribute granted under the
 * following conditions:
 *
 * 1). No charge may be made other than resonable charges
 *	for reproduction.
 *
 * 2). This notice must remain intact.
 *
 * 3). No further restrictions may be added.
 *
 */

/*	This program used to be in many little pieces, with this makefile:
.SUFFIXES:	.c .s

CFLAGS = -F

OBJS =	append.s catsub.s ckglob.s deflt.s del.s docmd.s doglob.s\
  doprnt.s doread.s dowrite.s ed.s egets.s find.s getfn.s getlst.s\
  getnum.s getone.s getptr.s getrhs.s gettxt.s ins.s join.s maksub.s\
  move.s optpat.s set.s setbuf.s subst.s getpat.s matchs.s amatch.s\
  unmkpat.s omatch.s makepat.s bitmap.s dodash.s esc.s System.s

ed:	$(OBJS)
  cc -T. -i -o ed $(OBJS)
*/

#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>

/****************************/

/*	tools.h	*/
/*
 *	#defines for non-printing ASCII characters
 */

#define NUL	0x00		/* ^@ */
#define EOS	0x00		/* end of string */
#define SOH	0x01		/* ^A */
#define STX	0x02		/* ^B */
#define ETX	0x03		/* ^C */
#define EOT	0x04		/* ^D */
#define ENQ	0x05		/* ^E */
#define ACK	0x06		/* ^F */
#define BEL	0x07		/* ^G */
#define BS	0x08		/* ^H */
#define HT	0x09		/* ^I */
#define LF	0x0a		/* ^J */
#define NL	'\n'
#define VT	0x0b		/* ^K */
#define FF	0x0c		/* ^L */
#define CR	0x0d		/* ^M */
#define SO	0x0e		/* ^N */
#define SI	0x0f		/* ^O */
#define DLE	0x10		/* ^P */
#define DC1	0x11		/* ^Q */
#define DC2	0x12		/* ^R */
#define DC3	0x13		/* ^S */
#define DC4	0x14		/* ^T */
#define NAK	0x15		/* ^U */
#define SYN	0x16		/* ^V */
#define ETB	0x17		/* ^W */
#define CAN	0x18		/* ^X */
#define EM	0x19		/* ^Y */
#define SUB	0x1a		/* ^Z */
#define ESC	0x1b		/* ^[ */
#define FS	0x1c		/* ^\ */
#define GS	0x1d		/* ^] */
#define RS	0x1e		/* ^^ */
#define US	0x1f		/* ^_ */
#define SP	0x20		/* space */
#define DEL	0x7f		/* DEL */


#define TRUE	1
#define FALSE	0
#define ERR	-2


/*	Definitions of meta-characters used in pattern matching
 *	routines.  LITCHAR & NCCL are only used as token identifiers;
 *	all the others are also both token identifier and actual symbol
 *	used in the regular expression.
 */


#define BOL	'^'
#define EOL	'$'
#define ANY	'.'
#define LITCHAR	'L'
#define	ESCAPE	'\\'
#define CCL	'['		/* Character class: [...] */
#define CCLEND	']'
#define NEGATE	'~'
#define NCCL	'!'		/* Negative character class [^...] */
#define CLOSURE	'*'
#define OR_SYM	'|'
#define DITTO	'&'
#define OPEN	'('
#define CLOSE	')'

/* Largest permitted size for an expanded character class.  (i.e. the class
 * [a-z] will expand into 26 symbols; [a-z0-9] will expand into 36.)
 */
#define CLS_SIZE	128

/*
 *	Tokens are used to hold pattern templates. (see makepat())
 */
typedef char BITMAP;

typedef struct token {
  char tok;
  char lchar;
  BITMAP *bitmap;
  struct token *next;
} TOKEN;

#define TOKSIZE sizeof (TOKEN)

/*
 *	An absolute maximun for strings.
 */

#define MAXSTR	132		/* Maximum numbers of characters in a line */


/* Macros */
#define max(a,b)	((a>b)?a:b)
#define min(a,b)	((a<b)?a:b)
#define toupper(c)	(c>='a'&&c<='z'?c-32:c)

/*	ed.h	*/
#define FATAL	(ERR-1)
struct line {
  int l_stat;			/* empty, mark */
  struct line *l_prev;
  struct line *l_next;
  char l_buff[1];
};

typedef struct line LINE;

#define LINFREE	1		/* entry not in use */
#define LGLOB	2		/* line marked global */

				/* max number of chars per line */
#define MAXLINE	(sizeof(int) == 2 ? 256 : 8192)
#define MAXPAT	256		/* max number of chars per replacement
				 * pattern */
				/* max file name size */
#define MAXFNAME (sizeof(int) == 2 ? 256 : 1024)

extern LINE line0;
extern int curln, lastln, line1, line2, nlines;
extern int nflg;		/* print line number flag */
extern int lflg;		/* print line in verbose mode */
extern char *inptr;		/* tty input buffer */
extern char linbuf[], *linptr;	/* current line */
extern int truncflg;		/* truncate long line flag */
extern int eightbit;		/* save eighth bit */
extern int nonascii;		/* count of non-ascii chars read */
extern int nullchar;		/* count of null chars read */
extern int truncated;		/* count of lines truncated */
extern int fchanged;		/* file changed */

#define nextln(l)	((l)+1 > lastln ? 0 : (l)+1)
#define prevln(l)	((l)-1 < 0 ? lastln : (l)-1)

/*	amatch.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */

_PROTOTYPE(int main, (int argc, char **argv));
_PROTOTYPE(static char *match, (char *lin, TOKEN *pat, char *boln));
_PROTOTYPE(char *amatch, (char *lin, TOKEN *pat, char *boln));
_PROTOTYPE(int append, (int line, int glob));
_PROTOTYPE(BITMAP *makebitmap, (unsigned size));
_PROTOTYPE(int setbit, (unsigned c, char *map, unsigned val));
_PROTOTYPE(int testbit, (unsigned c, char *map));
_PROTOTYPE(char *catsub, (char *from, char *to, char *sub, char *new, char *newend));
_PROTOTYPE(int ckglob, (void));
_PROTOTYPE(int deflt, (int def1, int def2));
_PROTOTYPE(int del, (int from, int to));
_PROTOTYPE(int docmd, (int glob));
_PROTOTYPE(int dolst, (int line1, int line2));
_PROTOTYPE(char *dodash, (int delim, char *src, char *map));
_PROTOTYPE(int doglob, (void));
_PROTOTYPE(int doprnt, (int from, int to));
_PROTOTYPE(void prntln, (char *str, int vflg, int lin));
_PROTOTYPE(void putcntl, (int c, FILE *stream));
_PROTOTYPE(int doread, (int lin, char *fname));
_PROTOTYPE(int dowrite, (int from, int to, char *fname, int apflg));
_PROTOTYPE(void intr, (int sig));
_PROTOTYPE(int egets, (char *str, int size, FILE *stream));
_PROTOTYPE(int esc, (char **s));
_PROTOTYPE(int find, (TOKEN *pat, int dir));
_PROTOTYPE(char *getfn, (void));
_PROTOTYPE(int getlst, (void));
_PROTOTYPE(int getnum, (int first));
_PROTOTYPE(int getone, (void));
_PROTOTYPE(TOKEN *getpat, (char *arg));
_PROTOTYPE(LINE *getptr, (int num));
_PROTOTYPE(int getrhs, (char *sub));
_PROTOTYPE(char *gettxt, (int num));
_PROTOTYPE(int ins, (char *str));
_PROTOTYPE(int System, (char *c));
_PROTOTYPE(int join, (int first, int last));
_PROTOTYPE(TOKEN *makepat, (char *arg, int delim));
_PROTOTYPE(char *maksub, (char *sub, int subsz));
_PROTOTYPE(char *matchs, (char *line, TOKEN *pat, int ret_endp));
_PROTOTYPE(int move, (int num));
_PROTOTYPE(int transfer, (int num));
_PROTOTYPE(int omatch, (char **linp, TOKEN *pat, char *boln));
_PROTOTYPE(TOKEN *optpat, (void));
_PROTOTYPE(int set, (void));
_PROTOTYPE(int show, (void));
_PROTOTYPE(void relink, (LINE *a, LINE *x, LINE *y, LINE *b));
_PROTOTYPE(void clrbuf, (void));
_PROTOTYPE(void set_buf, (void));
_PROTOTYPE(int subst, (TOKEN *pat, char *sub, int gflg, int pflag));
_PROTOTYPE(void unmakepat, (TOKEN *head));

/*     Scans throught the pattern template looking for a match
 * with lin.  Each element of lin is compared with the template
 * until either a mis-match is found or the end of the template
 * is reached.  In the former case a 0 is returned; in the latter,
 * a pointer into lin (pointing to the character following the
 * matched pattern) is returned.
 *
 *	"lin"	is a pointer to the line being searched.
 *	"pat"	is a pointer to a template made by makepat().
 *	"boln"	is a pointer into "lin" which points at the
 *			character at the beginning of the line.
 */

char *paropen[9], *parclose[9];
int between, parnum;

char *amatch(lin, pat, boln)
char *lin;
TOKEN *pat;
char *boln;
{
  between = 0;
  parnum = 0;

  lin = match(lin, pat, boln);

  if (between) return 0;

  while (parnum < 9) {
	paropen[parnum] = parclose[parnum] = "";
	parnum++;
  }
  return lin;
}

static char *match(lin, pat, boln)
char *lin;
TOKEN *pat;
char *boln;
{
  register char *bocl, *rval, *strstart;

  if (pat == 0) return 0;

  strstart = lin;

  while (pat) {
	if (pat->tok == CLOSURE && pat->next) {
		/* Process a closure: first skip over the closure
		 * token to the object to be repeated.  This object
		 * can be a character class. */

		pat = pat->next;

		/* Now match as many occurrences of the closure
		 * pattern as possible. */
		bocl = lin;

		while (*lin && omatch(&lin, pat, boln));

		/* 'Lin' now points to the character that made made
		 * us fail.  Now go on to process the rest of the
		 * string.  A problem here is a character following
		 * the closure which could have been in the closure.
		 * For example, in the pattern "[a-z]*t" (which
		 * matches any lower-case word ending in a t), the
		 * final 't' will be sucked up in the while loop.
		 * So, if the match fails, we back up a notch and try
		 * to match the rest of the string again, repeating
		 * this process recursively until we get back to the
		 * beginning of the closure.  The recursion goes, at
		 * most two levels deep. */

		if (pat = pat->next) {
			int savbtwn = between;
			int savprnm = parnum;

			while (bocl <= lin) {
				if (rval = match(lin, pat, boln)) {
					/* Success */
					return(rval);
				} else {
					--lin;
					between = savbtwn;
					parnum = savprnm;
				}
			}
			return(0);	/* match failed */
		}
	} else if (pat->tok == OPEN) {
		if (between || parnum >= 9) return 0;
		paropen[parnum] = lin;
		between = 1;
		pat = pat->next;
	} else if (pat->tok == CLOSE) {
		if (!between) return 0;
		parclose[parnum++] = lin;
		between = 0;
		pat = pat->next;
	} else if (omatch(&lin, pat, boln)) {
		pat = pat->next;
	} else {
		return(0);
	}
  }

  /* Note that omatch() advances lin to point at the next character to
   * be matched.  Consequently, when we reach the end of the template,
   * lin will be pointing at the character following the last character
   * matched.  The exceptions are templates containing only a BOLN or
   * EOLN token.  In these cases omatch doesn't advance.
   * 
   * A philosophical point should be mentioned here.  Is $ a position or a
   * character? (i.e. does $ mean the EOL character itself or does it
   * mean the character at the end of the line.)  I decided here to
   * make it mean the former, in order to make the behavior of match()
   * consistent.  If you give match the pattern ^$ (match all lines
   * consisting only of an end of line) then, since something has to be
   * returned, a pointer to the end of line character itself is
   * returned. */

  return((char *) max(strstart, lin));
}

/*	append.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int append(line, glob)
int line, glob;
{
  int stat;
  char lin[MAXLINE];

  if (glob) return(ERR);
  curln = line;
  while (1) {
	if (nflg) printf("%6d. ", curln + 1);

	if (fgets(lin, MAXLINE, stdin) == NULL) return(EOF);
	if (lin[0] == '.' && lin[1] == '\n') return (0);
	stat = ins(lin);
	if (stat < 0) return(ERR);

  }
}

/*	bitmap.c	*/
/*
 *	BITMAP.C -	makebitmap, setbit, testbit
 *			bit-map manipulation routines.
 *
 *	Copyright (c) Allen I. Holub, all rights reserved.  This program may
 *		for copied for personal, non-profit use only.
 *
 */

#ifdef DEBUG
/* #include <stdio.h> */
#endif

/* #include "tools.h" */


BITMAP *makebitmap(size)
unsigned size;
{
  /* Make a bit map with "size" bits.  The first entry in the map is an
   * "unsigned int" representing the maximum bit.  The map itself is
   * concatenated to this integer. Return a pointer to a map on
   * success, 0 if there's not enough memory. */

  unsigned *map, numbytes;

  numbytes = (size >> 3) + ((size & 0x07) ? 1 : 0);

#ifdef DEBUG
  printf("Making a %d bit map (%d bytes required)\n", size, numbytes);
#endif

  if (map = (unsigned *) malloc(numbytes + sizeof(unsigned))) *map = size;

  return((BITMAP *) map);
}

int setbit(c, map, val)
unsigned c, val;
char *map;
{
  /* Set bit c in the map to val. If c > map-size, 0 is returned, else
   * 1 is returned. */

  if (c >= *(unsigned *) map)	/* if c >= map size */
	return 0;

  map += sizeof(unsigned);	/* skip past size */

  if (val)
	map[c >> 3] |= 1 << (c & 0x07);
  else
	map[c >> 3] &= ~(1 << (c & 0x07));

  return 1;
}

int testbit(c, map)
unsigned c;
char *map;
{
  /* Return 1 if the bit corresponding to c in map is set. 0 if it is not. */

  if (c >= *(unsigned *) map) return 0;

  map += sizeof(unsigned);

  return(map[c >> 3] & (1 << (c & 0x07)));
}

/*	catsub.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

extern char *paropen[9], *parclose[9];

char *catsub(from, to, sub, new, newend)
char *from, *to, *sub, *new, *newend;
{
  char *cp, *cp2;

  for (cp = new; *sub != EOS && cp < newend;) {
	if (*sub == DITTO) for (cp2 = from; cp2 < to;) {
			*cp++ = *cp2++;
			if (cp >= newend) break;
		}
	else if (*sub == ESCAPE) {
		sub++;
		if ('1' <= *sub && *sub <= '9') {
			char *parcl = parclose[*sub - '1'];

			for (cp2 = paropen[*sub - '1']; cp2 < parcl;) {
				*cp++ = *cp2++;
				if (cp >= newend) break;
			}
		} else
			*cp++ = *sub;
	} else
		*cp++ = *sub;

	sub++;
  }

  return(cp);
}

/*	ckglob.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int ckglob()
{
  TOKEN *glbpat;
  char c, delim;
  char lin[MAXLINE];
  int num;
  LINE *ptr;

  c = *inptr;

  if (c != 'g' && c != 'v') return(0);

  if (deflt(1, lastln) < 0) return(ERR);

  delim = *++inptr;
  if (delim <= ' ') return(ERR);

  glbpat = optpat();

  if (*inptr == delim) inptr++;

  ptr = getptr(1);
  for (num = 1; num <= lastln; num++) {
	ptr->l_stat &= ~LGLOB;
	if (line1 <= num && num <= line2) {
		strcpy(lin, ptr->l_buff);
		strcat(lin, "\n");
		if (matchs(lin, glbpat, 0)) {
			if (c == 'g') ptr->l_stat |= LGLOB;
		} else {
			if (c == 'v') ptr->l_stat |= LGLOB;
		}
	}
	ptr = ptr->l_next;
  }
  return(1);
}

/*	deflt.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int deflt(def1, def2)
int def1, def2;
{
  if (nlines == 0) {
	line1 = def1;
	line2 = def2;
  }
  if (line1 > line2 || line1 <= 0) return(ERR);
  return(0);
}

/*	del.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int del(from, to)
int from, to;
{
  LINE *first, *last, *next, *tmp;

  if (from < 1) from = 1;
  first = getptr(prevln(from));
  last = getptr(nextln(to));
  next = first->l_next;
  while (next != last && next != &line0) {
	tmp = next->l_next;
	free((char *) next);
	next = tmp;
  }
  relink(first, last, first, last);
  lastln -= (to - from) + 1;
  curln = prevln(from);
  return(0);
}

/*	docmd.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

char fname[MAXFNAME];
int fchanged;
extern int nofname;

extern int mark[];

int docmd(glob)
int glob;
{
  static char rhs[MAXPAT];
  TOKEN *subpat;
  int c, err, line3;
  int apflg, pflag, gflag;
  int nchng;
  char *fptr;

  pflag = FALSE;
  while (*inptr == SP && *inptr == HT) inptr++;

  c = *inptr++;

  switch (c) {
      case NL:
	if (nlines == 0) {
		if ((line2 = nextln(curln)) == 0) return(ERR);
	}
	curln = line2;
	return(1);
	break;

      case '=':	printf("%d\n", line2);	break;

      case 'a':
	if (*inptr != NL || nlines > 1) return(ERR);

	if (append(line1, glob) < 0) return(ERR);;
	fchanged = TRUE;
	break;

      case 'c':
	if (*inptr != NL) return(ERR);

	if (deflt(curln, curln) < 0) return(ERR);

	if (del(line1, line2) < 0) return(ERR);
	if (append(curln, glob) < 0) return (ERR);
	fchanged = TRUE;
	break;

      case 'd':
	if (*inptr != NL) return(ERR);

	if (deflt(curln, curln) < 0) return(ERR);

	if (del(line1, line2) < 0) return(ERR);
	if (nextln(curln) != 0) curln = nextln(curln);
	fchanged = TRUE;
	break;

      case 'e':
	if (nlines > 0) return(ERR);
	if (fchanged) {
		fchanged = FALSE;
		return(ERR);
	}

	/* FALL THROUGH */
      case 'E':
	if (nlines > 0) return(ERR);

	if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);

	if ((fptr = getfn()) == NULL) return(ERR);

	clrbuf();
	if ((err = doread(0, fptr)) < 0) return(err);

	strcpy(fname, fptr);
	fchanged = FALSE;
	break;

      case 'f':
	if (nlines > 0) return(ERR);

	if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);

	if ((fptr = getfn()) == NULL) return(ERR);

	if (nofname)
		printf("%s\n", fname);
	else
		strcpy(fname, fptr);
	break;

      case 'i':
	if (*inptr != NL || nlines > 1) return(ERR);

	if (append(prevln(line1), glob) < 0) return(ERR);
	fchanged = TRUE;
	break;

      case 'j':
	if (*inptr != NL || deflt(curln, curln + 1) < 0) return(ERR);

	if (join(line1, line2) < 0) return(ERR);
	break;

      case 'k':
	while (*inptr == ' ' || *inptr == HT) inptr++;

	if (*inptr < 'a' || *inptr > 'z') return ERR;
	c = *inptr++;

	if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);

	mark[c - 'a'] = line1;
	break;

      case 'l':
	if (*inptr != NL) return(ERR);
	if (deflt(curln, curln) < 0) return (ERR);
	if (dolst(line1, line2) < 0) return (ERR);
	break;

      case 'm':
	if ((line3 = getone()) < 0) return(ERR);
	if (deflt(curln, curln) < 0) return (ERR);
	if (move(line3) < 0) return (ERR);
	fchanged = TRUE;
	break;

      case 'P':
      case 'p':
	if (*inptr != NL) return(ERR);
	if (deflt(curln, curln) < 0) return (ERR);
	if (doprnt(line1, line2) < 0) return (ERR);
	break;

      case 'q':
	if (fchanged) {
		fchanged = FALSE;
		return(ERR);
	}

	/* FALL THROUGH */
      case 'Q':
	if (*inptr == NL && nlines == 0 && !glob)
		return(EOF);
	else
		return(ERR);

      case 'r':
	if (nlines > 1) return(ERR);

	if (nlines == 0) line2 = lastln;

	if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);

	if ((fptr = getfn()) == NULL) return(ERR);

	if ((err = doread(line2, fptr)) < 0) return(err);
	fchanged = TRUE;
	break;

      case 's':
	if (*inptr == 'e') return(set());
	while (*inptr == SP || *inptr == HT) inptr++;
	if ((subpat = optpat()) == NULL) return (ERR);
	if ((gflag = getrhs(rhs)) < 0) return (ERR);
	if (*inptr == 'p') pflag++;
	if (deflt(curln, curln) < 0) return (ERR);
	if ((nchng = subst(subpat, rhs, gflag, pflag)) < 0) return (ERR);
	if (nchng) fchanged = TRUE;
	break;

      case 't':
	if ((line3 = getone()) < 0) return(ERR);
	if (deflt(curln, curln) < 0) return (ERR);
	if (transfer(line3) < 0) return (ERR);
	fchanged = TRUE;
	break;

      case 'W':
      case 'w':
	apflg = (c == 'W');

	if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);

	if ((fptr = getfn()) == NULL) return(ERR);

	if (deflt(1, lastln) < 0) return(ERR);
	if (dowrite(line1, line2, fptr, apflg) < 0) return (ERR);
	fchanged = FALSE;
	break;

      case 'x':
	if (*inptr == NL && nlines == 0 && !glob) {
		if ((fptr = getfn()) == NULL) return(ERR);
		if (dowrite(1, lastln, fptr, 0) >= 0) return (EOF);
	}
	return(ERR);

      case 'z':
	if (deflt(curln, curln) < 0) return(ERR);

	switch (*inptr) {
	    case '-':
		if (doprnt(line1 - 21, line1) < 0) return(ERR);
		break;

	    case '.':
		if (doprnt(line1 - 11, line1 + 10) < 0) return(ERR);
		break;

	    case '+':
	    case '\n':
		if (doprnt(line1, line1 + 21) < 0) return(ERR);
		break;
	}
	break;

      default:	return(ERR);
}
  return(0);
}

int dolst(line1, line2)
int line1, line2;
{
  int oldlflg = lflg, p;

  lflg = 1;
  p = doprnt(line1, line2);
  lflg = oldlflg;

  return p;
}

/*	dodash.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */

/*	Expand the set pointed to by *src into dest.
 *	Stop at delim.  Return 0 on error or size of
 *	character class on success.  Update *src to
 *	point at delim.  A set can have one element
 *	{x} or several elements ( {abcdefghijklmnopqrstuvwxyz}
 *	and {a-z} are equivalent ).  Note that the dash
 *	notation is expanded as sequential numbers.
 *	This means (since we are using the ASCII character
 *	set) that a-Z will contain the entire alphabet
 *	plus the symbols: [\]^_`.  The maximum number of
 *	characters in a character class is defined by maxccl.
 */
char *dodash(delim, src, map)
int delim;
char *src, *map;
{

  register int first, last;
  char *start;

  start = src;

  while (*src && *src != delim) {
	if (*src != '-') setbit(esc(&src), map, 1);

	else if (src == start || *(src + 1) == delim)
		setbit('-', map, 1);
	else {
		src++;

		if (*src < *(src - 2)) {
			first = *src;
			last = *(src - 2);
		} else {
			first = *(src - 2);
			last = *src;
		}

		while (++first <= last) setbit(first, map, 1);

	}
	src++;
  }
  return(src);
}

/*	doglob.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int doglob()
{
  int lin, stat;
  char *cmd;
  LINE *ptr;

  cmd = inptr;

  while (1) {
	ptr = getptr(1);
	for (lin = 1; lin <= lastln; lin++) {
		if (ptr->l_stat & LGLOB) break;
		ptr = ptr->l_next;
	}
	if (lin > lastln) break;

	ptr->l_stat &= ~LGLOB;
	curln = lin;
	inptr = cmd;
	if ((stat = getlst()) < 0) return(stat);
	if ((stat = docmd(1)) < 0) return (stat);
  }
  return(curln);
}

/*	doprnt.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int doprnt(from, to)
int from, to;
{
  int i;
  LINE *lptr;

  from = from < 1 ? 1 : from;
  to = to > lastln ? lastln : to;

  if (to != 0) {
	lptr = getptr(from);
	for (i = from; i <= to; i++) {
		prntln(lptr->l_buff, lflg, (nflg ? i : 0));
		lptr = lptr->l_next;
	}
	curln = to;
  }
  return(0);
}

void prntln(str, vflg, lin)
char *str;
int vflg, lin;
{
  if (lin) printf("%7d ", lin);
  while (*str && *str != NL) {
	if (*str < ' ' || *str >= 0x7f) {
		switch (*str) {
		    case '\t':
			if (vflg)
				putcntl(*str, stdout);
			else
				putc(*str, stdout);
			break;

		    case DEL:
			putc('^', stdout);
			putc('?', stdout);
			break;

		    default:
			putcntl(*str, stdout);
			break;
		}
	} else
		putc(*str, stdout);
	str++;
  }
  if (vflg) putc('$', stdout);
  putc('\n', stdout);
}

void putcntl(c, stream)
char c;
FILE *stream;
{
  putc('^', stream);
  putc((c & 31) | '@', stream);
}

/*	doread.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

extern int diag;

int doread(lin, fname)
int lin;
char *fname;
{
  FILE *fp;
  int err;
  long bytes;
  int lines;
  static char str[MAXLINE];

  err = 0;
  nonascii = nullchar = truncated = 0;

  if (diag) printf("\"%s\" ", fname);
  if ((fp = fopen(fname, "r")) == NULL) {
	printf("file open err\n");
	return(ERR);
  }
  curln = lin;
  for (lines = 0, bytes = 0; (err = egets(str, MAXLINE, fp)) > 0;) {
	bytes += strlen(str);
	if (ins(str) < 0) {
		printf("file insert error\n");
		err++;
		break;
	}
	lines++;
  }
  fclose(fp);
  if (err < 0) return(err);
  if (diag) {
	printf("%d lines %ld bytes", lines, bytes);
	if (nonascii) printf(" [%d non-ascii]", nonascii);
	if (nullchar) printf(" [%d nul]", nullchar);
	if (truncated) printf(" [%d lines truncated]", truncated);
	printf("\n");
  }
  return(err);
}

/*	dowrite.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int dowrite(from, to, fname, apflg)
int from, to;
char *fname;
int apflg;
{
  FILE *fp;
  int lin, err;
  int lines;
  long bytes;
  char *str;
  LINE *lptr;

  err = 0;

  lines = bytes = 0;
  if (diag) printf("\"%s\" ", fname);
  if ((fp = fopen(fname, (apflg ? "a" : "w"))) == NULL) {
	printf("file open error\n");
	return(ERR);
  }
  lptr = getptr(from);
  for (lin = from; lin <= to; lin++) {
	str = lptr->l_buff;
	lines++;
	bytes += strlen(str) + 1;
	if (fputs(str, fp) == EOF) {
		printf("file write error\n");
		err++;
		break;
	}
	fputc('\n', fp);
	lptr = lptr->l_next;
  }
  if (diag) printf("%d lines %ld bytes\n", lines, bytes);
  fclose(fp);
  return(err);
}

/*	ed.c	*/
/* Copyright 1987 Brian Beattie Rights Reserved.
 *
 * Permission to copy and/or distribute granted under the
 * following conditions:
 *
 * 1). No charge may be made other than resonable charges
 *	for reproduction.
 *
 * 2). This notice must remain intact.
 *
 * 3). No further restrictions may be added.
 *
 */
/* #include <stdio.h> */
/* #include <signal.h> */
/* #include "tools.h" */
/* #include "ed.h" */
#include <setjmp.h>
jmp_buf env;

LINE line0;
int curln = 0;
int lastln = 0;
char *inptr;
static char inlin[MAXLINE];
int nflg, lflg;
int line1, line2, nlines;
extern char fname[];
int version = 1;
int diag = 1;

void intr(sig)
int sig;
{
  printf("?\n");
  longjmp(env, 1);
}

int main(argc, argv)
int argc;
char **argv;
{
  int stat, i, doflush;

  set_buf();
  doflush = isatty(1);

  if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 0) {
	diag = 0;
	argc--;
	argv++;
  }
  if (argc > 1) {
	for (i = 1; i < argc; i++) {
		if (doread(0, argv[i]) == 0) {
			curln = 1;
			strcpy(fname, argv[i]);
			break;
		}
	}
  }
  while (1) {
	setjmp(env);
	if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, intr);

	if (doflush) fflush(stdout);

	if (fgets(inlin, sizeof(inlin), stdin) == NULL) {
		break;
	}
	if (*inlin == '!') {
		for (inptr = inlin; *inptr != NL; inptr++);
		*inptr = EOS;
		System(inlin + 1);
		continue;
	}
	inptr = inlin;
	if (getlst() >= 0)
		if ((stat = ckglob()) != 0) {
			if (stat >= 0 && (stat = doglob()) >= 0) {
				curln = stat;
				continue;
			}
		} else {
			if ((stat = docmd(0)) >= 0) {
				if (stat == 1) doprnt(curln, curln);
				continue;
			}
		}
	if (stat == EOF) {
		exit(0);
	}
	if (stat == FATAL) {
		fputs("FATAL ERROR\n", stderr);
		exit(1);
	}
	printf("?\n");
  }
  return(0);
}

/*	egets.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int eightbit = 1;		/* save eight bit */
int nonascii, nullchar, truncated;
int egets(str, size, stream)
char *str;
int size;
FILE *stream;
{
  int c, count;
  char *cp;

  for (count = 0, cp = str; size > count;) {
	c = getc(stream);
	if (c == EOF) {
		*cp++ = '\n';
		*cp = EOS;
		if (count) {
			printf("[Incomplete last line]\n");
		}
		return(count);
	}
	if (c == NL) {
		*cp++ = c;
		*cp = EOS;
		return(++count);
	}
	if (c > 127) {
		if (!eightbit)	/* if not saving eighth bit */
			c = c & 127;	/* strip eigth bit */
		nonascii++;	/* count it */
	}
	if (c) {
		*cp++ = c;	/* not null, keep it */
		count++;
	} else
		nullchar++;	/* count nulls */
  }
  str[count - 1] = EOS;
  if (c != NL) {
	printf("truncating line\n");
	truncated++;
	while ((c = getc(stream)) != EOF)
		if (c == NL) break;
  }
  return(count);
}

/*	esc.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */

/* Map escape sequences into their equivalent symbols.  Returns the
 * correct ASCII character.  If no escape prefix is present then s
 * is untouched and *s is returned, otherwise **s is advanced to point
 * at the escaped character and the translated character is returned.
 */
int esc(s)
char **s;
{
  register int rval;


  if (**s != ESCAPE) {
	rval = **s;
  } else {
	(*s)++;

	switch (toupper(**s)) {
	    case '\000':	rval = ESCAPE;	break;
	    case 'S':	rval = ' ';	break;
	    case 'N':	rval = '\n';	break;
	    case 'T':	rval = '\t';	break;
	    case 'B':	rval = '\b';	break;
	    case 'R':	rval = '\r';	break;
	    default:	rval = **s;	break;
	}
  }

  return(rval);
}

/*	find.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int find(pat, dir)
TOKEN *pat;
int dir;
{
  int i, num;
  char lin[MAXLINE];
  LINE *ptr;

  num = curln;
  ptr = getptr(curln);
  num = (dir ? nextln(num) : prevln(num));
  ptr = (dir ? ptr->l_next : ptr->l_prev);
  for (i = 0; i < lastln; i++) {
	if (num == 0) {
		num = (dir ? nextln(num) : prevln(num));
		ptr = (dir ? ptr->l_next : ptr->l_prev);
	}
	strcpy(lin, ptr->l_buff);
	strcat(lin, "\n");
	if (matchs(lin, pat, 0)) {
		return(num);
	}
	num = (dir ? nextln(num) : prevln(num));
	ptr = (dir ? ptr->l_next : ptr->l_prev);
  }
  return(ERR);
}

/*	getfn.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

extern char fname[MAXFNAME];
int nofname;

char *getfn()
{
  static char file[256];
  char *cp;

  if (*inptr == NL) {
	nofname = TRUE;
	strcpy(file, fname);
  } else {
	nofname = FALSE;
	while (*inptr == SP || *inptr == HT) inptr++;

	cp = file;
	while (*inptr && *inptr != NL && *inptr != SP && *inptr != HT) {
		*cp++ = *inptr++;
	}
	*cp = '\0';

	if (strlen(file) == 0) {
		printf("bad file name\n");
		return(NULL);
	}
  }

  if (strlen(file) == 0) {
	printf("no file name\n");
	return(NULL);
  }
  return(file);
}

/*	getlst.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int getlst()
{
  int num;

  line2 = 0;
  for (nlines = 0; (num = getone()) >= 0;) {
	line1 = line2;
	line2 = num;
	nlines++;
	if (*inptr != ',' && *inptr != ';') break;
	if (*inptr == ';') curln = num;
	inptr++;
  }
  nlines = min(nlines, 2);
  if (nlines == 0) line2 = curln;
  if (nlines <= 1) line1 = line2;

  if (num == ERR)
	return(num);
  else
	return(nlines);
}

/*	getnum.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int mark['z' - 'a' + 1];

int getnum(first)
int first;
{
  TOKEN *srchpat;
  int num;
  char c;

  while (*inptr == SP || *inptr == HT) inptr++;

  if (*inptr >= '0' && *inptr <= '9') {	/* line number */
	for (num = 0; *inptr >= '0' && *inptr <= '9';) {
		num = (num * 10) + *inptr - '0';
		inptr++;
	}
	return num;
  }
  switch (c = *inptr) {
      case '.':
	inptr++;
	return(curln);

      case '$':
	inptr++;
	return(lastln);

      case '/':
      case '?':
	srchpat = optpat();
	if (*inptr == c) inptr++;
	return(find(srchpat, c == '/' ? 1 : 0));

      case '-':
      case '+':
	return(first ? curln : 1);

      case '\'':
	inptr++;
	if (*inptr < 'a' || *inptr > 'z') return(EOF);

	return mark[*inptr++ - 'a'];

      default:
	return(first ? EOF : 1);/* unknown address */
  }
}

/*	getone.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

#define FIRST 1
#define NOTFIRST 0

int getone()
{
  int c, i, num;

  if ((num = getnum(FIRST)) >= 0) {
	while (1) {
		while (*inptr == SP || *inptr == HT) inptr++;

		if (*inptr != '+' && *inptr != '-') break;
		c = *inptr++;

		if ((i = getnum(NOTFIRST)) < 0) return(i);

		if (c == '+') {
			num += i;
		} else {
			num -= i;
		}
	}
  }
  return(num > lastln ? ERR : num);
}

/*	getpat.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */

/* Translate arg into a TOKEN string */
TOKEN *
 getpat(arg)
char *arg;
{

  return(makepat(arg, '\000'));
}

/*	getptr.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

LINE *
 getptr(num)
int num;
{
  LINE *ptr;
  int j;

  if (2 * num > lastln && num <= lastln) {	/* high line numbers */
	ptr = line0.l_prev;
	for (j = lastln; j > num; j--) ptr = ptr->l_prev;
  } else {			/* low line numbers */
	ptr = &line0;
	for (j = 0; j < num; j++) ptr = ptr->l_next;
  }
  return(ptr);
}

/*	getrhs.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int getrhs(sub)
char *sub;
{
  if (inptr[0] == NL || inptr[1] == NL)	/* check for eol */
	return(ERR);

  if (maksub(sub, MAXPAT) == NULL) return(ERR);

  inptr++;			/* skip over delimter */
  while (*inptr == SP || *inptr == HT) inptr++;
  if (*inptr == 'g') {
	inptr++;
	return(1);
  }
  return(0);
}

/*	gettxt.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

char *
 gettxt(num)
int num;
{
  LINE *lin;
  static char txtbuf[MAXLINE];

  lin = getptr(num);
  strcpy(txtbuf, lin->l_buff);
  strcat(txtbuf, "\n");
  return(txtbuf);
}

/*	ins.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int ins(str)
char *str;
{
  char buf[MAXLINE], *cp;
  LINE *new, *cur, *nxt;

  cp = buf;
  while (1) {
	if ((*cp = *str++) == NL) *cp = EOS;
	if (*cp) {
		cp++;
		continue;
	}
	if ((new = (LINE *) malloc(sizeof(LINE) + strlen(buf))) == NULL)
		return(ERR);	/* no memory */

	new->l_stat = 0;
	strcpy(new->l_buff, buf);	/* build new line */
	cur = getptr(curln);	/* get current line */
	nxt = cur->l_next;	/* get next line */
	relink(cur, new, new, nxt);	/* add to linked list */
	relink(new, nxt, cur, new);
	lastln++;
	curln++;

	if (*str == EOS)	/* end of line ? */
		return(1);

	cp = buf;
  }
}

/*	join.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

extern int fchanged;

int join(first, last)
int first, last;
{
  char buf[MAXLINE];
  char *cp = buf, *str;
  int num;

  if (first <= 0 || first > last || last > lastln) return(ERR);
  if (first == last) {
	curln = first;
	return 0;
  }
  for (num = first; num <= last; num++) {
	str = gettxt(num);

	while (*str != NL && cp < buf + MAXLINE - 1) *cp++ = *str++;

	if (cp == buf + MAXLINE - 1) {
		printf("line too long\n");
		return(ERR);
	}
  }
  *cp++ = NL;
  *cp = EOS;
  del(first, last);
  curln = first - 1;
  ins(buf);
  fchanged = TRUE;
  return 0;
}

/*	makepat.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */

/* Make a pattern template from the strinng pointed to by arg.  Stop
 * when delim or '\000' or '\n' is found in arg.  Return a pointer to
 * the pattern template.
 *
 * The pattern template used here are somewhat different than those
 * used in the "Software Tools" book; each token is a structure of
 * the form TOKEN (see tools.h).  A token consists of an identifier,
 * a pointer to a string, a literal character and a pointer to another
 * token.  This last is 0 if there is no subsequent token.
 *
 * The one strangeness here is caused (again) by CLOSURE which has
 * to be put in front of the previous token.  To make this insertion a
 * little easier, the 'next' field of the last to point at the chain
 * (the one pointed to by 'tail) is made to point at the previous node.
 * When we are finished, tail->next is set to 0.
 */
TOKEN *
 makepat(arg, delim)
char *arg;
int delim;
{
  TOKEN *head, *tail, *ntok;
  int error;

  /* Check for characters that aren't legal at the beginning of a template. */

  if (*arg == '\0' || *arg == delim || *arg == '\n' || *arg == CLOSURE)
	return(0);

  error = 0;
  tail = head = NULL;

  while (*arg && *arg != delim && *arg != '\n' && !error) {
	ntok = (TOKEN *) malloc(TOKSIZE);
	ntok->lchar = '\000';
	ntok->next = 0;

	switch (*arg) {
	    case ANY:	ntok->tok = ANY;	break;

	    case BOL:
		if (head == 0)	/* then this is the first symbol */
			ntok->tok = BOL;
		else
			ntok->tok = LITCHAR;
		ntok->lchar = BOL;
		break;

	    case EOL:
		if (*(arg + 1) == delim || *(arg + 1) == '\000' ||
		    *(arg + 1) == '\n') {
			ntok->tok = EOL;
		} else {
			ntok->tok = LITCHAR;
			ntok->lchar = EOL;
		}
		break;

	    case CLOSURE:
		if (head != 0) {
			switch (tail->tok) {
			    case BOL:
			    case EOL:
			    case CLOSURE:
				return(0);

			    default:
				ntok->tok = CLOSURE;
			}
		}
		break;

	    case CCL:

		if (*(arg + 1) == NEGATE) {
			ntok->tok = NCCL;
			arg += 2;
		} else {
			ntok->tok = CCL;
			arg++;
		}

		if (ntok->bitmap = makebitmap(CLS_SIZE))
			arg = dodash(CCLEND, arg, ntok->bitmap);
		else {
			fprintf(stderr, "Not enough memory for pat\n");
			error = 1;
		}
		break;

	    default:
		if (*arg == ESCAPE && *(arg + 1) == OPEN) {
			ntok->tok = OPEN;
			arg++;
		} else if (*arg == ESCAPE && *(arg + 1) == CLOSE) {
			ntok->tok = CLOSE;
			arg++;
		} else {
			ntok->tok = LITCHAR;
			ntok->lchar = esc(&arg);
		}
	}

	if (error || ntok == 0) {
		unmakepat(head);
		return(0);
	} else if (head == 0) {
		/* This is the first node in the chain. */

		ntok->next = 0;
		head = tail = ntok;
	} else if (ntok->tok != CLOSURE) {
		/* Insert at end of list (after tail) */

		tail->next = ntok;
		ntok->next = tail;
		tail = ntok;
	} else if (head != tail) {
		/* More than one node in the chain.  Insert the
		 * CLOSURE node immediately in front of tail. */

		(tail->next)->next = ntok;
		ntok->next = tail;
	} else {
		/* Only one node in the chain,  Insert the CLOSURE
		 * node at the head of the linked list. */

		ntok->next = head;
		tail->next = ntok;
		head = ntok;
	}
	arg++;
  }

  tail->next = 0;
  return(head);
}

/*	maksub.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

char *
 maksub(sub, subsz)
char *sub;
int subsz;
{
  int size;
  char delim, *cp;

  size = 0;
  cp = sub;

  delim = *inptr++;
  for (size = 0; *inptr != delim && *inptr != NL && size < subsz; size++) {
	if (*inptr == '&') {
		*cp++ = DITTO;
		inptr++;
	} else if ((*cp++ = *inptr++) == ESCAPE) {
		if (size >= subsz) return(NULL);

		switch (toupper(*inptr)) {
		    case NL:	*cp++ = ESCAPE;		break;
			break;
		    case 'S':
			*cp++ = SP;
			inptr++;
			break;
		    case 'N':
			*cp++ = NL;
			inptr++;
			break;
		    case 'T':
			*cp++ = HT;
			inptr++;
			break;
		    case 'B':
			*cp++ = BS;
			inptr++;
			break;
		    case 'R':
			*cp++ = CR;
			inptr++;
			break;
		    case '0':{
				int i = 3;
				*cp = 0;
				do {
					if (*++inptr < '0' || *inptr > '7')
						break;

					*cp = (*cp << 3) | (*inptr - '0');
				} while (--i != 0);
				cp++;
			} break;
		    default:	*cp++ = *inptr++;	break;
		}
	}
  }
  if (size >= subsz) return(NULL);

  *cp = EOS;
  return(sub);
}

/*	matchs.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */

/* Compares line and pattern.  Line is a character string while pat
 * is a pattern template made by getpat().
 * Returns:
 *	1. A zero if no match was found.
 *
 *	2. A pointer to the last character satisfing the match
 *	   if ret_endp is non-zero.
 *
 *	3. A pointer to the beginning of the matched string if
 *	   ret_endp is zero.
 *
 * e.g.:
 *
 *	matchs ("1234567890", getpat("4[0-9]*7), 0);
 * will return a pointer to the '4', while:
 *
 *	matchs ("1234567890", getpat("4[0-9]*7), 1);
 * will return a pointer to the '7'.
 */
char *
 matchs(line, pat, ret_endp)
char *line;
TOKEN *pat;
int ret_endp;
{

  char *rval, *bptr;
  char *line2;
  TOKEN *pat2;
  char c;
  short ok;

  bptr = line;

  while (*line) {

	if (pat && pat->tok == LITCHAR) {
		while (*line) {
			pat2 = pat;
			line2 = line;
			if (*line2 != pat2->lchar) {
				c = pat2->lchar;
				while (*line2 && *line2 != c) ++line2;
				line = line2;
				if (*line2 == '\0') break;
			}
			ok = 1;
			++line2;
			pat2 = pat2->next;
			while (pat2 && pat2->tok == LITCHAR) {
				if (*line2 != pat2->lchar) {
					ok = 0;
					break;
				}
				++line2;
				pat2 = pat2->next;
			}
			if (!pat2) {
				if (ret_endp)
					return(--line2);
				else
					return(line);
			} else if (ok)
				break;
			++line;
		}
		if (*line == '\0') return(0);
	} else {
		line2 = line;
		pat2 = pat;
	}
	if ((rval = amatch(line2, pat2, bptr)) == 0) {
		if (pat && pat->tok == BOL) break;
		line++;
	} else {
		if (rval > bptr && rval > line)
			rval--;	/* point to last char matched */
		rval = ret_endp ? rval : line;
		break;
	}
  }
  return(rval);
}

/*	move.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int move(num)
int num;
{
  LINE *k0, *k1, *k2, *k3;

  if (line1 <= 0 || line2 < line1 || (line1 <= num && num <= line2))
	return(ERR);
  k0 = getptr(prevln(line1));
  k1 = getptr(line1);
  k2 = getptr(line2);
  k3 = getptr(nextln(line2));

  relink(k0, k3, k0, k3);
  lastln -= line2 - line1 + 1;

  if (num > line1) num -= line2 - line1 + 1;

  curln = num + (line2 - line1 + 1);

  k0 = getptr(num);
  k3 = getptr(nextln(num));

  relink(k0, k1, k2, k3);
  relink(k2, k3, k0, k1);
  lastln += line2 - line1 + 1;

  return(1);
}

int transfer(num)
int num;
{
  int mid, lin, ntrans;

  if (line1 <= 0 || line1 > line2) return(ERR);

  mid = num < line2 ? num : line2;

  curln = num;
  ntrans = 0;

  for (lin = line1; lin <= mid; lin++) {
	ins(gettxt(lin));
	ntrans++;
  }
  lin += ntrans;
  line2 += ntrans;

  for (; lin <= line2; lin += 2) {
	ins(gettxt(lin));
	line2++;
  }
  return(1);
}

/*	omatch.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */

/* Match one pattern element, pointed at by pat, with the character at
 * **linp.  Return non-zero on match.  Otherwise, return 0.  *Linp is
 * advanced to skip over the matched character; it is not advanced on
 * failure.  The amount of advance is 0 for patterns that match null
 * strings, 1 otherwise.  "boln" should point at the position that will
 * match a BOL token.
 */
int omatch(linp, pat, boln)
char **linp;
TOKEN *pat;
char *boln;
{

  register int advance;

  advance = -1;

  if (**linp) {
	switch (pat->tok) {
	    case LITCHAR:
		if (**linp == pat->lchar) advance = 1;
		break;

	    case BOL:
		if (*linp == boln) advance = 0;
		break;

	    case ANY:
		if (**linp != '\n') advance = 1;
		break;

	    case EOL:
		if (**linp == '\n') advance = 0;
		break;

	    case CCL:
		if (testbit(**linp, pat->bitmap)) advance = 1;
		break;

	    case NCCL:
		if (!testbit(**linp, pat->bitmap)) advance = 1;
		break;
	}
  }
  if (advance >= 0) *linp += advance;

  return(++advance);
}

/*	optpat.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

TOKEN *oldpat;

TOKEN *
 optpat()
{
  char delim, str[MAXPAT], *cp;

  delim = *inptr++;
  cp = str;
  while (*inptr != delim && *inptr != NL) {
	if (*inptr == ESCAPE && inptr[1] != NL) *cp++ = *inptr++;
	*cp++ = *inptr++;
  }

  *cp = EOS;
  if (*str == EOS) return(oldpat);
  if (oldpat) unmakepat(oldpat);
  oldpat = getpat(str);
  return(oldpat);
}

/*	set.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

struct tbl {
  char *t_str;
  int *t_ptr;
  int t_val;
} *t, tbl[] = {

  "number", &nflg, TRUE,
  "nonumber", &nflg, FALSE,
  "list", &lflg, TRUE,
  "nolist", &lflg, FALSE,
  "eightbit", &eightbit, TRUE,
  "noeightbit", &eightbit, FALSE,
  0
};

int set()
{
  char word[16];
  int i;

  inptr++;
  if (*inptr != 't') {
	if (*inptr != SP && *inptr != HT && *inptr != NL) return(ERR);
  } else
	inptr++;

  if (*inptr == NL) return(show());
  /* Skip white space */
  while (*inptr == SP || *inptr == HT) inptr++;

  for (i = 0; *inptr != SP && *inptr != HT && *inptr != NL;)
	word[i++] = *inptr++;
  word[i] = EOS;
  for (t = tbl; t->t_str; t++) {
	if (strcmp(word, t->t_str) == 0) {
		*t->t_ptr = t->t_val;
		return(0);
	}
  }
  return(0);
}

int show()
{
  extern int version;

  printf("ed version %d.%d\n", version / 100, version % 100);
  printf("number %s, list %s\n", nflg ? "ON" : "OFF", lflg ? "ON" : "OFF");
  return(0);
}

/*	setbuf.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

void relink(a, x, y, b)
LINE *a, *x, *y, *b;
{
  x->l_prev = a;
  y->l_next = b;
}

void clrbuf()
{
  del(1, lastln);
}

void set_buf()
{
  relink(&line0, &line0, &line0, &line0);
  curln = lastln = 0;
}

/*	subst.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */
/* #include "ed.h" */

int subst(pat, sub, gflg, pflag)
TOKEN *pat;
char *sub;
int gflg, pflag;
{
  int lin, chngd, nchngd;
  char *txtptr, *txt;
  char *lastm, *m, *new, buf[MAXLINE];

  if (line1 <= 0) return(ERR);
  nchngd = 0;			/* reset count of lines changed */
  for (lin = line1; lin <= line2; lin++) {
	txt = txtptr = gettxt(lin);
	new = buf;
	chngd = 0;
	lastm = NULL;
	while (*txtptr) {
		if (gflg || !chngd)
			m = amatch(txtptr, pat, txt);
		else
			m = NULL;
		if (m != NULL && lastm != m) {
			chngd++;
			new = catsub(txtptr, m, sub, new,
				     buf + MAXLINE);
			lastm = m;
		}
		if (m == NULL || m == txtptr) {
			*new++ = *txtptr++;
		} else {
			txtptr = m;
		}
	}
	if (chngd) {
		if (new >= buf + MAXLINE) return(ERR);
		*new++ = EOS;
		del(lin, lin);
		ins(buf);
		nchngd++;
		if (pflag) doprnt(curln, curln);
	}
  }
  if (nchngd == 0 && !gflg) {
	return(ERR);
  }
  return(nchngd);
}

/*	System.c	*/
#define SHELL	"/bin/sh"
#define SHELL2	"/usr/bin/sh"

int System(c)
char *c;
{
  int pid, status;

  switch (pid = fork()) {
      case -1:
	return -1;
      case 0:
	execl(SHELL, "sh", "-c", c, (char *) 0);
	execl(SHELL2, "sh", "-c", c, (char *) 0);
	exit(-1);
      default:	while (wait(&status) != pid);
}
  return status;
}

/*	unmkpat.c	*/
/* #include <stdio.h> */
/* #include "tools.h" */

/* Free up the memory usde for token string */
void unmakepat(head)
TOKEN *head;
{

  register TOKEN *old_head;

  while (head) {
	switch (head->tok) {
	    case CCL:
	    case NCCL:
		free(head->bitmap);
		/* Fall through to default */

	    default:
		old_head = head;
		head = head->next;
		free((char *) old_head);
		break;
	}
  }
}
