edL: Add a cleaned up port of the MWC ed
authorAlan Cox <alan@linux.intel.com>
Sat, 4 Nov 2017 01:43:29 +0000 (01:43 +0000)
committerAlan Cox <alan@linux.intel.com>
Sat, 4 Nov 2017 01:43:29 +0000 (01:43 +0000)
It's a bit bigger than the utils ed but seems to work rather better. Not
sure which ed will be best - next experiment may be the v7 one 8)

Applications/MWC/cmd/Makefile.68000
Applications/MWC/cmd/Makefile.6809
Applications/MWC/cmd/Makefile.z80
Applications/MWC/cmd/ed.c [new file with mode: 0644]
Applications/MWC/cmd/ed.h [new file with mode: 0644]

index bfb25b0..7927538 100644 (file)
@@ -14,7 +14,7 @@ CRT0NS = ../../../Library/libs/crt0nostdio_68000.o
 ELF2FUZIX = elf2flt
 .SUFFIXES: .c .o
 
-SRCS  = ac.c almanac.c at.c calendar.c col.c cron.c deroff.c expr.c find.c m4.c make.c moo.c pr.c tar.c test.c ttt.c units.c
+SRCS  = ac.c almanac.c at.c calendar.c col.c cron.c deroff.c ed.c expr.c find.c m4.c make.c moo.c pr.c tar.c test.c ttt.c units.c
 
 OBJS = $(SRCS:.c=.o)
 
index eebb127..4d51583 100644 (file)
@@ -14,7 +14,7 @@ CRT0 = ../../../Library/libs/crt0_6809.o
 .SUFFIXES: .c .o
 
 
-SRCS  = ac.c almanac.c at.c calendar.c col.c cron.c deroff.c expr.c find.c m4.c make.c moo.c pr.c tar.c test.c ttt.c units.c
+SRCS  = ac.c almanac.c at.c calendar.c col.c cron.c deroff.c ed.c expr.c find.c m4.c make.c moo.c pr.c tar.c test.c ttt.c units.c
 
 OBJS = $(SRCS:.c=.o)
 
index 6e55d73..03a412c 100644 (file)
@@ -13,7 +13,7 @@ PROGLOAD=`(cat ../../Kernel/platform/config.h; echo PROGLOAD) | cpp -E | tail -n
 
 SRCSNS = expr.c test.c
 
-SRCS  = ac.c almanac.c at.c col.c cron.c deroff.c find.c make.c moo.c pr.c tar.c ttt.c units.c
+SRCS  = ac.c almanac.c at.c col.c cron.c deroff.c ed.c find.c make.c moo.c pr.c tar.c ttt.c units.c
 
 SRCSBAD = calendar.c m4.c
 
diff --git a/Applications/MWC/cmd/ed.c b/Applications/MWC/cmd/ed.c
new file mode 100644 (file)
index 0000000..bf298fd
--- /dev/null
@@ -0,0 +1,2227 @@
+/*
+ * An editor.
+ * External variables.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include "ed.h"
+
+int    cflag;                          /* Print character counts */
+int    mflag;                          /* Allow multiple commands per line */
+int    pflag;                          /* Editor prompts */
+int    oflag;                          /* Behaves like old editor */
+int    sflag;                          /* Match patterns in single case */
+int    tflag;                          /* Set up for screen editor */
+int    vflag;                          /* Verbose error messages */
+int    intflag;                        /* Interrupt has been hit */
+FILE   *tmp;                           /* Temp file pointer */
+long   tmpseek;                        /* Free space seek ptr in tmp file */
+int    rcurbno;                        /* Current read block number */
+int    wcurbno;                        /* Current write block number */
+LINE   *line;                          /* Pointer to line table */
+int    lnsize;                         /* Current size of line table */
+int    savechr;                        /* Character that was ungetx'ed */
+int    lastchr;                        /* Last character we read */
+char   *gcp;                           /* Global input pointer */
+char   linebuf[LBSIZE];                /* Line buffer */
+char   codebuf[CBSIZE];                /* Code buffer */
+char   tempbuf[TBSIZE];                /* Temporary buffer */
+char   subsbuf[SBSIZE];                /* Substitute buffer */
+char   globbuf[GBSIZE];                /* Buffer for global command */
+char   rdbcbuf[DBSIZE];                /* Disk buffer cache */
+char   wdbcbuf[DBSIZE];                /* Write buffer cache */
+BRACE  brace[1+BRSIZE];                /* For remembering \( \) */
+char   file[FNSIZE+1];                 /* Filename */
+LINE   marklin[MKSIZE];                /* Mark line table */
+int    dotadd;                         /* Address of the current line */
+int    doladd;                         /* Address of last line */
+char   vcom;                           /* Verify command */
+int    saved;                          /* File saved since last written */
+FILE   *fp;                            /* File pointer for readfil */
+long   cct;                            /* Number of chars read in append */
+long   lct;                            /* Number of lines read in append */
+int    appflag;                        /* In append mode */
+int    addspec;                        /* Number of addresses specified */
+int    adderrr;                        /* Error in computing address */
+int    addpage;                        /* An ampersand was found */
+int    addques;                        /* A question mark was found */
+int    subnths;                        /* Which substitute wanted */
+int    subnewl;                        /* A newline is being replaced */
+int    subseek;                        /* Seek position of new line */
+int    suborig;                        /* Seek position of old line */
+char   *errstr;                        /* Pointer to last error message */
+char   *keyp;                          /* Pointer to crypt key */
+
+/*
+ * Main, command parser and general routines.
+ */
+int main(int argc, char *argv[])
+{
+       char *cp;
+       initialise();
+
+       if ((cp=getenv("ED")) != NULL)
+               setoptf(cp);
+       signal(SIGHUP, sighang);
+       signal(SIGINT, sigintr);
+       signal(SIGQUIT, sigintr);
+
+       setup(argc, argv);
+       for (;;) {
+               if (intflag) {
+/*
+                       finit(stdout);
+                       clearerr(stdin);
+                       clearerr(stdout);
+                       clearerr(stderr);
+*/
+                       savechr = '\0';
+                       intflag = 0;
+                       derror("Interrupt");
+               }
+               command();
+       }
+}
+
+/*
+ * Initialisation.
+ */
+void initialise(void)
+{
+       int i;
+       char *bp;
+
+       tmp = tmpfile();
+       if (tmp == NULL) {
+               terror("Cannot create temp file");
+               exit(1);
+       }
+
+       mflag = 0;
+       oflag = 0;
+       pflag = 0;
+       sflag = 0;
+       tflag = 0;
+       vflag = 0;
+       cflag = 1;
+       intflag = 0;
+       tmpseek = CLSIZE;
+       rcurbno = -1;
+       wcurbno = -1;
+       lnsize = LNSIZE;
+       if ((line=(LINE *)calloc(lnsize, sizeof(LINE))) == NULL) {
+               prints(stderr, "Cannot allocate line number table\n");
+               exit(1);
+       }
+       savechr = '\0';
+       lastchr = '\n';
+       gcp = NULL;
+       codebuf[0] = CSNUL;
+       file[0] = '\0';
+       for (i=0; i<MKSIZE; i++)
+               marklin[i] = 0;
+       dotadd = 0;
+       doladd = 0;
+       saved = 1;
+       subseek = 0;
+       keyp = NULL;
+}
+
+/*
+ * Process command line arguments.
+ */
+void setup(int argc, char *argv[])
+{
+       char *cp;
+       int i;
+       int f;
+
+       f = 0;
+       for (i=1; i<argc; i++) {
+               cp = argv[i];
+               switch (*cp++) {
+               case '-':
+                       switch (*cp++) {
+                       case '\0':
+                               cflag = 0;
+                               continue;
+                       case 't':
+                               tflag = 1;
+                               signal(SIGINT, SIG_IGN);
+                               signal(SIGQUIT, SIG_IGN);
+                               continue;
+                       case 'u':
+                               setbuf(stdout, NULL);
+                               continue;
+                       case 'x':
+                               setkey();
+                               continue;
+                       default:
+                               usage();
+                       }
+               case '+':
+                       setoptf(argv[i]);
+                       continue;
+               default:
+                       if (f++ != 0)
+                               usage();
+                       if (strlen(argv[i]) > FNSIZE)
+                               derror("File name too long");
+                       else {
+                               strcpy(file, argv[i]);
+                               edit();
+                       }
+               }
+       }
+}
+
+/*
+ * Print out a usage message.
+ */
+void usage(void)
+{
+       prints(stderr, "Usage: ed [-[x]] [+cmopsv] [file]\n");
+       exit(1);
+}
+
+/*
+ * Set a flag indicating that interrupt has been hit.
+ * Used for SIGQUIT and SIGINT.
+ */
+
+void sigintr(int s)
+{
+       intflag++;
+       signal(s, sigintr);
+}
+
+/*
+ * If we get a hangup signal, write the file onto the file
+ * `ed.hup' and leave.
+ */
+
+void sighang(int s)
+{
+       if (doladd != 0)
+               wfile(1, doladd, "ed.hup", "w", 1);
+       leave();
+}
+
+/*
+ * Leave the editor.
+ */
+void leave(void)
+{
+       fclose(tmp);
+       exit(0);
+}
+
+/*
+ * Process a command.
+ */
+int command(void)
+{
+       int c1, n, a3;
+       char name[FNSIZE+1];
+       int a1, a2, c;
+
+       for (;;) {
+               addspec = 0;
+               a1 = a2 = getaddr();
+               if (intflag)
+                       return (0);
+               if (adderrr)
+                       goto err;
+               c = getx();
+               if (addspec != 0) {
+                       while (c==',' || c==';') {
+                               a1 = a2;
+                               if (c == ';')
+                                       dotadd = a1;
+                               a2 = getaddr();
+                               if (adderrr)
+                                       goto err;
+                               if (addspec == 0) {
+                                       derror("Missing address");
+                                       goto err;
+                               }
+                               c = getx();
+                       }
+               }
+               if (c == '*') {
+                       if (addspec++ == 0) {
+                               a1 = 1;
+                               addspec++;
+                       }
+                       a2 = doladd;
+                       c = getx();
+               }
+               switch (c) {
+               case EOF:
+                       if (intflag == 0) {
+                               if (saved != 0)
+                                       leave();
+                               saved = 1;
+                               derror("File not saved");
+                       }
+                       return 0;
+               case '\n':
+                       if (addques) {
+                               if (errstr) {
+                                       prints(stdout, errstr);
+                                       printc(stdout, '\n');
+                               }
+                               return (0);
+                       }
+                       if (addpage) {
+                               a1 = a2;
+                               a2 = a1 + PGSIZE;
+                               if (a2 > doladd)
+                                       a2 = doladd;
+                               if (print(a1, a2) == 0)
+                                       return (0);
+                               return (1);
+                       }
+                       if (addspec == 0)
+                               a2 = dotadd+1;
+                       if (print(a2, a2) == 0)
+                               return (0);
+                       return (1);
+               case '!':
+                       if (rest() == 0)
+                               goto err;
+                       n = system(tempbuf);
+                       if (n < 0) {
+                               derror("Call failed");
+                               return (0);
+                       } else {
+                               prints(stdout, "!\n");
+                               return (1);
+                       }
+               case '=':
+                       if (verify(0) == 0)
+                               goto err;
+                       if (addspec == 0)
+                               a2 = doladd;
+                       printd(stdout, a2);
+                       printc(stdout, '\n');
+                       break;
+               case 'a':
+                       if (verify(1) == 0)
+                               goto err;
+                       getx();
+                       if (append(a2, readtty) == 0)
+                               return (0);
+                       ungetx('\n');
+                       break;
+               case 'c':
+                       if (verify(1) == 0)
+                               goto err;
+                       getx();
+                       if (delete(a1, a2) == 0)
+                               return (0);
+                       if (append(a1-1, readtty) == 0)
+                               return (0);
+                       ungetx('\n');
+                       break;
+               case 'd':
+                       if (verify(0) == 0)
+                               goto err;
+                       if (delete(a1, a2) == 0)
+                               goto err;
+                       break;
+               case 'e':
+                       n = saved;
+                       if (getfile(n?file:name) == 0)
+                               return (0);
+                       if (n == 0) {
+                               saved = 1;
+                               derror("File not saved");
+                               return (0);
+                       }
+                       return (edit());
+               case 'E':
+                       if (getfile(file) == 0)
+                               return (0);
+                       return (edit());
+               case 'f':
+                       if ((c=getx()) == ' ') {
+                               ungetx(' ');
+                               getfile(file);
+                               c = '\n';
+                       }
+                       if (c != '\n') {
+                               derror("Bad command");
+                               goto err;
+                       }
+                       if (file[0] != '\0') {
+                               prints(stdout, file);
+                               printc(stdout, '\n');
+                       }
+                       return (1);
+               case 'g':
+                       if (addspec == 0) {
+                               a1 = 1;
+                               a2 = doladd;
+                       }
+                       if (global(a1, a2, 0) == 0)
+                               goto err;
+                       if (verify(1) == 0)
+                               goto err;
+                       break;
+               case 'i':
+                       if (verify(1) == 0)
+                               goto err;
+                       getx();
+                       if (doladd==0 && a2==0)
+                               a2 = 1;
+                       if (append(a2-1, readtty) == 0)
+                               return (0);
+                       ungetx('\n');
+                       break;
+               case 'j':
+                       if (addspec < 2)
+                               a2 = a1+1;
+                       if (verify(0) == 0)
+                               goto err;
+                       if (join(a1, a2) == 0)
+                               goto err;
+                       break;
+               case 'k':
+                       if ((c=getx()) == '\n') {
+                               derror("No mark name specified");
+                               return (0);
+                       }
+                       if (verify(0) == 0)
+                               goto err;
+                       if (a2<1 || a2>doladd) {
+                               derror("Illegal address range");
+                               goto err;
+                       }
+                       if (!isascii(c) || !islower(c)) {
+                               derror("Bad mark name");
+                               goto err;
+                       }
+                       marklin[c-'a'] = line[a2]|1;
+                       break;
+               case 'l':
+                       if (verify(0) == 0)
+                               goto err;
+                       if (list(a1, a2) == 0)
+                               goto err;
+                       break;
+               case 'm':
+                       a3 = getaddr();
+                       if (adderrr)
+                               goto err;
+                       if (verify(0) == 0)
+                               goto err;
+                       if (move(a1, a2, a3) == 0)
+                               goto err;
+                       break;
+               case 'o':
+                       if (rest() == 0)
+                               goto err;
+                       if (tempbuf[0] == '\0')
+                               disoptf();
+                       else
+                               setoptf(tempbuf);
+                       return (1);
+               case 'p':
+               case 'P':
+                       if (verify(0) == 0)
+                               goto err;
+                       if (print(a1, a2) == 0)
+                               goto err;
+                       break;
+               case 'q':
+                       if (verify(1) == 0)
+                               goto err;
+                       if (addspec != 0) {
+                               derror("Cannot specify address");
+                               goto err;
+                       }
+                       if (saved == 0) {
+                               saved = 1;
+                               derror("File not saved");
+                               goto err;
+                       }
+                       leave();
+               case 'Q':
+                       if (verify(1) == 0)
+                               goto err;
+                       leave();
+               case 'r':
+                       if (getfile(name) == 0)
+                               return (0);
+                       if (addspec == 0)
+                               a2 = doladd;
+                       if ((fp=xfopen(name, "r")) == NULL) {
+                               derror("Cannot open file");
+                               return (0);
+                       }
+                       n = 1;
+                       if (append(a2, readfil) == 0)
+                               n = 0;
+                       else if (ferror(fp)) {
+                               derror("Read error");
+                               n = 0;
+                       }
+                       fclose(fp);
+                       if (cflag != 0) {
+                               printl(stdout, !oflag ? cct : lct);
+                               printc(stdout, '\n');
+                       }
+                       return (n);
+               case 's':
+                       if (subs1(a1, a2) == 0)
+                               goto err;
+                       if (verify(0) == 0)
+                               goto err;
+                       if (subs2(a1, a2) == 0)
+                               goto err;
+                       break;
+               case 't':
+                       a3 = getaddr();
+                       if (adderrr)
+                               goto err;
+                       if (verify(0) == 0)
+                               goto err;
+                       if (copy(a1, a2, a3) == 0)
+                               goto err;
+                       break;
+               case 'u':
+                       if (verify(0) == 0)
+                               goto err;
+                       if (a2<1 || a2>doladd) {
+                               derror("Illegal address range");
+                               goto err;
+                       }
+                       if ((line[a2]|1) != subseek) {
+                               derror("Cannot undo substitute");
+                               goto err;
+                       }
+                       line[a2] = suborig;
+                       subseek = 0;
+                       break;
+               case 'v':
+                       if (addspec == 0) {
+                               a1 = 1;
+                               a2 = doladd;
+                       }
+                       if (global(a1, a2, 1) == 0)
+                               goto err;
+                       if (verify(1) == 0)
+                               goto err;
+                       break;
+               case 'w':
+               case 'W':
+                       if ((c1=getx()) != 'q')
+                               ungetx(c1);
+                       if (getfile(name) == 0)
+                               return (0);
+                       if (addspec == 0) {
+                               a1 = 1;
+                               a2 = doladd;
+                       }
+                       if (c=='W' && keyp!=NULL) {
+                               derror("Cannot append in encryption mode");
+                               goto err;
+                       }
+                       if (wfile(a1, a2, name, c=='w'?"w":"a", 0) && c1=='q')
+                               leave();
+                       return (1);
+               case 'x':
+                       if (verify(1) == 0)
+                               goto err;
+                       if (setkey() == 0)
+                               goto err;
+                       break;
+               default:
+                       derror("Bad command");
+               err:
+                       while ((c=getx())!=EOF && c!='\n')
+                               ;
+                       return (0);
+               }
+               if (intflag)
+                       return (0);
+               switch (vcom) {
+               case 'l':
+                       list(dotadd, dotadd);
+                       break;
+               case 'p':
+                       print(dotadd, dotadd);
+                       break;
+               }
+               if ((c=getx()) == '\n')
+                       return (1);
+               if (mflag == 0) {
+                       derror("Internal error");
+                       goto err;
+               }
+               ungetx(c);
+       }
+}
+
+/*
+ * Get an address.
+ */
+int getaddr(void)
+{
+       LINE seek;
+       int absolute, abs, sign;
+       int a, c, n;
+
+       adderrr = 0;
+       addpage = 0;
+       addques = 0;
+       absolute = 0;
+       abs = 1;
+       sign = 0;
+       a = dotadd;
+       for (;;) {
+               switch (c=getx()) {
+               case '.':
+                       n = dotadd;
+                       abs = 1;
+                       break;
+               case '$':
+                       n = doladd;
+                       abs = 1;
+                       break;
+               case '&':
+                       if (sign == 0) {
+                               addpage++;
+                               continue;
+                       }
+                       n = PGSIZE;
+                       abs = 0;
+                       break;
+               case '\'':
+                       if (!isascii(c=getx()) || !islower(c)) {
+                               derror("Bad mark name");
+                               adderrr++;
+                               return (0);
+                       }
+                       seek = marklin[c-'a'];
+                       for (n=1; n<=doladd; n++) {
+                               if ((line[n]|1) == seek) {
+                                       abs = 0;
+                                       goto number;
+                               }
+                       }
+                       derror("Mark value not initialised");
+                       adderrr++;
+                       return (0);
+               case '+':
+                       sign++;
+                       continue;
+               case '^':
+               case '-':
+                       --sign;
+                       continue;
+               case '?':
+                       c = getx();
+                       ungetx(c);
+                       if (c == '\n') {
+                               addques++;
+                               return (0);
+                       }
+                       c = '?';
+               case '/':
+                       if (compile(c) == 0) {
+                               adderrr++;
+                               return (0);
+                       }
+                       if (a<1 || a>doladd) {
+                               derror("Bad initial address");
+                               adderrr++;
+                               return (0);
+                       }
+                       n = a;
+                       abs = 1;
+                       do {
+                               if (intflag)
+                                       return 0;
+                               if (c == '?') {
+                                       if (--n < 1)
+                                               n = doladd;
+                               } else {
+                                       if (++n > doladd)
+                                               n = 1;
+                               }
+                               if (n == a) {
+                                       if (execute(n))
+                                               break;
+                                       derror("Search failed");
+                                       adderrr++;
+                                       return (0);
+                               }
+                       } while (execute(n) == 0);
+                       break;
+               default:
+                       if (isascii(c) && isdigit(c)) {
+                               n = 0;
+                               do
+                                       n = n*10 + c-'0';
+                               while (isascii(c=getx()) && isdigit(c));
+                               ungetx(c);
+                               abs = 0;
+                               break;
+                       }
+                       ungetx(c);
+                       if (sign) {
+                               a += sign;
+                               addspec++;
+                       }
+                       return (a);
+               }
+       number:
+               if (sign == 0) {
+                       a = n;
+                       absolute = abs;
+               } else {
+                       if ((absolute+=abs) > 1) {
+                               derror("Relocation address error");
+                               adderrr++;
+                               return (0);
+                       }
+                       a += sign>0 ? n : -n;
+                       sign = 0;
+               }
+               addspec++;
+       }
+}
+
+/*
+ * Get a character.
+ */
+int getx(void)
+{
+       int c;
+
+       if (intflag != 0)
+               return (EOF);
+       if (savechr != '\0') {
+               c = savechr;
+               savechr = '\0';
+               return (c);
+       }
+       if (gcp == NULL) {
+               if (lastchr=='\n' && appflag==0) {
+                       if (pflag != 0) {
+                               printc(stderr, '*');
+                               fflush(stderr);
+                       }
+                       if (tflag != 0) {
+                               printc(stdout, '\000');
+                               printc(stdout, '\027');
+                               fflush(stdout);
+                       }
+               }
+               return (lastchr=getchar());
+       }
+       if ((c=*gcp++) != '\0')
+               return (c);
+       --gcp;
+       return (EOF);
+}
+
+/*
+ * Put back a character.
+ */
+void ungetx(int c)
+{
+       savechr = c;
+}
+
+/*
+ * Given a line number, `a', get a line from the temp file
+ * and store in the buffer `buffer'.  The number of characters read
+ * including the null is returned.
+ */
+int egetline(int a, char *buffer)
+{
+       LINE l;
+       int bno, off;
+       char *bp, *dp, *cp;
+
+       l = line[a];
+       bno = blockn(l);
+       off = offset(l);
+       bp = buffer;
+       for (;;) {
+               if ((cp=getdisk(bno++)) == NULL)
+                       return (0);
+               dp = &cp[off];
+               cp += DBSIZE;
+               while (dp < cp) {
+                       if ((*bp++=*dp++) == '\0')
+                               return (bp-buffer);
+               }
+               off = 0;
+       }
+}
+
+/*
+ * Given a buffer, `bp', containing a line and a number of
+ * characters, `n', in the buffer, add the line onto the end of
+ * the temp file.
+ */
+int putline(char *bp, int n)
+{
+       int bno, off, inc;
+       char *dp;
+
+       inc = (n+CLSIZE-1) & ~(CLSIZE-1);
+       if (tmpseek+inc > (ULARGE/2)*CLSIZE) {
+               derror("Temp file overflow");
+               return (0);
+       }
+       bno = tmpseek / DBSIZE;
+       off = tmpseek % DBSIZE;
+       for (;;) {
+               if (wcurbno>=0 && bno!=wcurbno)
+                       if (putdisk() == 0)
+                               return (0);
+               wcurbno = bno;
+               dp = &wdbcbuf[off];
+               while (dp < &wdbcbuf[DBSIZE]) {
+                       *dp++ = *bp++;
+                       if (--n == 0) {
+                               tmpseek += inc;
+                               return (1);
+                       }
+               }
+               bno++;
+               off = 0;
+       }
+}
+
+/*
+ * Given a block number, bring it into a disk buffer.  A pointer
+ * to the buffer is returned.
+ */
+char *getdisk(int bno)
+{
+       if (bno == wcurbno)
+               return (wdbcbuf);
+       if (bno == rcurbno)
+               return (rdbcbuf);
+       lseek(fileno(tmp), (long) bno * DBSIZE, 0);
+       if (read(fileno(tmp), rdbcbuf, sizeof(rdbcbuf)) != sizeof(rdbcbuf)) {
+               terror("Read error");
+               return (NULL);
+       }
+       rcurbno = bno;
+       return (rdbcbuf);
+}
+
+/*
+ * Write out the current disk buffer.
+ */
+int putdisk(void)
+{
+       lseek(fileno(tmp), (long) wcurbno * DBSIZE, 0);
+       if (write(fileno(tmp), wdbcbuf, sizeof(wdbcbuf)) != sizeof(wdbcbuf)) {
+               terror("Write error");
+               return (0);
+       }
+       return (1);
+}
+
+/*
+ * A diagnostic error.
+ */
+void derror(char *str)
+{
+       errstr = str;
+       if (tflag != 0) {
+               printc(stdout, '\000');
+               printc(stdout, '\025');
+               fflush(stdout);
+       } else {
+               printc(stderr, '?');
+               if (vflag != 0)
+                       prints(stderr, str);
+               printc(stderr, '\n');
+       }
+}
+
+/*
+ * A temp file error.
+ */
+void terror(char *str)
+{
+       errstr = str;
+       if (tflag != 0) {
+               printc(stdout, '\000');
+               printc(stdout, '\025');
+               fflush(stdout);
+       } else {
+               printc(stderr, '?');
+               prints(stderr, str);
+               printc(stderr, '\n');
+       }
+}
+
+/*
+ * Print out a decimal number.
+ */
+void printd(FILE *fp, int n)
+{
+       int c;
+
+       if (n < 0) {
+               printc(fp, '-');
+               if ((n=-n) < 0)
+                       n = 0;
+       }
+       c = n%10 + '0';
+       n /= 10;
+       if (n != 0)
+               printd(fp, n);
+       printc(fp, c);
+}
+
+/*
+ * Print out a long integer.
+ */
+void printl(FILE *fp, long n)
+{
+       int c;
+
+       if (n < 0) {
+               printc(fp, '-');
+               if ((n=-n) < 0)
+                       n = 0;
+       }
+       c = n%10 + '0';
+       n /= 10;
+       if (n != 0)
+               printl(fp, n);
+       printc(fp, c);
+}
+
+/*
+ * Print out an octal number.
+ */
+void printo(FILE *fp, int n)
+{
+       printc(fp, '0'+((n>>6)&03));
+       printc(fp, '0'+((n>>3)&07));
+       printc(fp, '0'+(n&07));
+}
+
+/*
+ * Print out a string.
+ */
+void prints(FILE *fp, char *cp)
+{
+       while (*cp)
+               printc(fp, *cp++);
+}
+
+/*
+ * Print out a character.
+ */
+void printc(FILE *fp, char c)
+{
+       putc(c, fp);
+       if (tflag==0 && c=='\n' && fp==stdout)
+               fflush(fp);
+}
+
+/*
+ * Commands.
+ */
+
+/*
+ * Edit a file.  The filename is stored in `file'.
+ */
+int edit(void)
+{
+       int ret, i;
+
+       if (doladd > 0)
+               delete(1, doladd);
+       tmpseek = CLSIZE;
+       rcurbno = -1;
+       wcurbno = -1;
+       if ((fp=xfopen(file, "r")) == NULL) {
+               derror("Cannot open file");
+               return (0);
+       }
+       ret = 1;
+       if (append(0, readfil) == 0)
+               ret = 0;
+       else if (ferror(fp)) {
+               derror("Read error");
+               ret = 0;
+       }
+       fclose(fp);
+       for (i=0; i<MKSIZE; i++)
+               marklin[i] = 0;
+       saved = 1;
+       if (cflag != 0) {
+               printl(stdout, !oflag ? cct : lct);
+               printc(stdout, '\n');
+       }
+       return (ret);
+}
+
+/*
+ * Make sure that the previous command is terminated either
+ * immediately by a newline or has a 'p' or a 'l' which is
+ * then terminated by a newline.  The variable `vcom' is set
+ * to the verify command.  A zero is returned if there were
+ * bad characters before the newline, otherwise a one.
+ */
+int verify(int flag)
+{
+       int c;
+
+       vcom = ' ';
+       if (flag==0 && mflag!=0)
+               return (1);
+       if ((c=getx()) == '\n') {
+               ungetx(c);
+               return (1);
+       }
+       if (c=='l' || c=='p') {
+               if (getx() == '\n') {
+                       ungetx('\n');
+                       vcom = c;
+                       return (1);
+               }
+       }
+       derror("Bad command");
+       return (0);
+}
+
+/*
+ * Get a filename of the form required for the r command.
+ * which is returned in `name'.  If no name is specified,
+ * the one stored in `file' is copied.  If a file name is
+ * specified and none exists in `file', it is copied to
+ * `file'.  If an illegal name is given, an error is derrord.
+ */
+int getfile(char *name)
+{
+       int c;
+       char *p1, *p2;
+
+       switch (c=getx()) {
+       case '\n':
+               p1 = name;
+               p2 = file;
+               while (*p1++ = *p2++)
+                       ;
+               break;
+       case ' ':
+       case '\t':
+               p1 = name;
+               p2 = &name[FNSIZE];
+               while ((c=getx()) == ' ' || c == '\t')
+                       ;
+               *p1++ = c;
+               while ((c=getx()) != EOF && c != '\n') {
+                       if (p1 >= p2) {
+                               derror("File name too long");
+                               name[0] = '\0';
+                               return (0);
+                       }
+                       *p1++ = c;
+               }
+               *p1 = '\0';
+               if (c == EOF) {
+                       derror("EOF in file name");
+                       name[0] = '\0';
+                       return (0);
+               }
+               if (file[0] == '\0') {
+                       p1 = file;
+                       p2 = name;
+                       while (*p1++ = *p2++)
+                               ;
+               }
+               break;
+       default:
+               while (getx() != '\n')
+                       ;
+               derror("Bad command");
+               return (0);
+       }
+       if (name[0] == '\0') {
+               derror("Null file name");
+               return (0);
+       }
+       return (1);
+}
+
+/*
+ * Read the rest of the input line into `tempbuf'.
+ */
+int rest(void)
+{
+       int c;
+       char *cp;
+
+       cp = tempbuf;
+       while ((c=getx())!=EOF && c!='\n') {
+               if (cp >= &tempbuf[TBSIZE-1]) {
+                       derror("Line too long");
+                       return (0);
+               }
+               *cp++ = c;
+       }
+       *cp = '\0';
+       return (1);
+}
+
+/*
+ * Append text after the given address.  A line of text is gotten
+ * by calling the given function until it returns NULL.
+ */
+int append(int a, int (*f)(void))
+{
+       int seek;
+       int n;
+
+       cct = 0;
+       lct = 0;
+       if (a<0 || a>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       dotadd = a;
+       while ((n=(*f)()) != 0) {
+               if (intflag)
+                       return (1);
+               seek = linead();
+               if (putline(linebuf, n) == 0)
+                       return (0);
+               saved = 0;
+               cct += n;
+               lct++;
+               if (expand(dotadd) == 0)
+                       return (0);
+               line[++dotadd] = seek;
+       }
+       return (1);
+}
+
+/*
+ * Expand the line table and leave a hole at the given address.
+ */
+int expand(int a)
+{
+       LINE *lp1;
+       LINE *lp2;
+       int n;
+
+       if (doladd+3 >= lnsize) {
+               lp1 = (LINE *)realloc(line, (n=lnsize*2)*sizeof(LINE));
+               if (lp1 == NULL) {
+                       derror("Line table overflow");
+                       return (0);
+               }
+               line = lp1;
+               lnsize = n;
+       }
+       lp1 = &line[doladd+2];
+       lp2 = &line[doladd+1];
+       n = doladd++ - a;
+       while (n--)
+               *--lp1 = *--lp2;
+       return (1);
+}
+
+/*
+ * Get a line of text from the terminal.  The number of characters
+ * including the null terminator at the end of the string is returned.
+ * If end of file or a line containing only a single dot is found,
+ * 0 is returned.
+ */
+int readtty(void)
+{
+       int c;
+       char *lp;
+
+       appflag++;
+       lp = linebuf;
+       while ((c=getx())!=EOF && c!='\n') {
+               if (lp < &linebuf[LBSIZE-1])
+                       *lp++ = c;
+       }
+       --appflag;
+       if (c==EOF && lp==linebuf) {
+               lastchr = '\n';
+               if (gcp == NULL)
+                       clearerr(stdin);
+               return (0);
+       }
+       if (linebuf[0]=='.' && lp==linebuf+1)
+               return (0);
+       *lp++ = '\0';
+       return (lp-linebuf);
+}
+
+/*
+ * Get a line of text from the file open on file pointer `fp'.
+ * Return the number of characters in the line including the
+ * null terminator at the end.  On end of file, 0 is returned.
+ */
+int readfil(void)
+{
+       int c, n;
+       char *lp;
+
+       if ((c=getc(fp)) == EOF)
+               return (0);
+       lp = linebuf;
+       n = LBSIZE-1;
+       while (c!=EOF && c!='\n') {
+               *lp++ = c;
+               if (--n == 0)
+                       break;
+               c = getc(fp);
+       }
+       *lp++ = '\0';
+       return (lp-linebuf);
+}
+
+/*
+ * Delete the given line range.
+ */
+int delete(int a1, int a2)
+{
+       LINE *p1, *p2;
+       int n;
+
+       if (a1>a2 || a1<1 || a2>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       p1 = &line[a1];
+       p2 = &line[a2+1];
+       n = doladd - a2;
+       while (n--)
+               *p1++ = *p2++;
+       doladd -= (a2+1) - a1;
+       if ((dotadd=a1) > doladd)
+               --dotadd;
+       saved = doladd==0;
+       return (1);
+}
+
+/*
+ * Concatenate the lines in the given range to form
+ * a single line.
+ */
+int join(int a1, int a2)
+{
+       long seek;
+       int bn, a;
+       int n;
+       char *lp, *tp;
+
+       if (a1>a2 || a1<1 || a2>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       bn = 0;
+       lp = linebuf;
+       for (a=a1; a<=a2; a++) {
+               if ((n=egetline(a, tempbuf)) == 0)
+                       return (0);
+               if ((bn+=--n) >= LBSIZE-1) {
+                       derror("Temporary buffer overflow");
+                       return (0);
+               }
+               tp = tempbuf;
+               while (n--)
+                       *lp++ = *tp++;
+       }
+       *lp++ = '\0';
+       seek = linead();
+       if (putline(linebuf, ++bn) == 0)
+               return (0);
+       line[a1] = seek;
+       a = doladd - a2;
+       for (n=0; n<a; n++)
+               line[a1+1+n] = line[a2+1+n];
+       dotadd = a1;
+       doladd -= a2 - a1;
+       return (1);
+}
+
+/*
+ * List the given line range.  All non-printing characters are
+ * escaped.
+ */
+int list(int a1, int a2)
+{
+       int a;
+       int n;
+       char *p;
+
+       if (a1>a2 || a1<1 || a2>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       for (a=a1; a<=a2; a++) {
+               if (intflag)
+                       return (1);
+               if (egetline(a, linebuf) == 0)
+                       break;
+               n = 0;
+               for (p=linebuf; *p; p++) {
+                       if (n++ >= 72) {
+                               n = 0;
+                               prints(stdout, "\\\n");
+                       }
+                       switch (*p) {
+                       case '\b':
+                               prints(stdout, "-\b<");
+                               continue;
+                       case '\t':
+                               prints(stdout, "-\b>");
+                               continue;
+                       case '\\':
+                               prints(stdout, "\\\\");
+                               continue;
+                       default:
+                               if (isascii(*p) && !iscntrl(*p)) {
+                                       printc(stdout, *p);
+                                       continue;
+                               }
+                               printc(stdout, '\\');
+                               printo(stdout, *p);
+                               n += 3;
+                       }
+               }
+               prints(stdout, "\\n");
+               printc(stdout, '\n');
+               dotadd = a;
+       }
+       return (1);
+}
+
+/*
+ * Take the text that is between lines `a1' and `a2' and
+ * place it after line `a3'.
+ */
+int move(int a1,int a2,int a3)
+{
+       LINE l;
+       int a, n, x;
+
+       if (a1>a2 || a1<1 || a2>doladd || a3<0 || a3>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       if (a3>=a1-1 && a3<=a2) {
+               dotadd = a2;
+               return (1);
+       }
+       if (a3 < a1) {
+               for (a=a1, x=a3+1; a<=a2; a++, x++) {
+                       l = line[a];
+                       for (n=a; n>x; --n)
+                               line[n] = line[n-1];
+                       line[x] = l;
+               }
+               dotadd = a3 + a2+1 - a1;
+       } else {
+               for (a=a2, x=a3; a>=a1; --a, --x) {
+                       l = line[a];
+                       for (n=a; n<x; n++)
+                               line[n] = line[n+1];
+                       line[x] = l;
+               }
+               dotadd = a3;
+       }
+       saved = 0;
+       return (1);
+}
+
+/*
+ * Given a string describing a set of options, set them.
+ */
+void setoptf(char *sp)
+{
+       int t;
+
+       t = 1;
+       while (*sp != '\0') {
+               switch (*sp++) {
+               case '+':
+                       t = 1;
+                       continue;
+               case '-':
+                       t = 0;
+                       continue;
+               case 'c':
+                       cflag = t;
+                       continue;
+               case 'm':
+                       mflag = t;
+                       continue;
+               case 'o':
+                       oflag = t;
+                       continue;
+               case 'p':
+                       pflag = t;
+                       continue;
+               case 's':
+                       sflag = t;
+                       continue;
+               case 'v':
+                       vflag = t;
+                       continue;
+               default:
+                       derror("Bad option");
+                       return;
+               }
+       }
+}
+
+/*
+ * Display options.
+ */
+void disoptf(void)
+{
+       if (cflag)
+               printc(stdout, 'c');
+       if (mflag)
+               printc(stdout, 'm');
+       if (oflag)
+               printc(stdout, 'o');
+       if (pflag)
+               printc(stdout, 'p');
+       if (sflag)
+               printc(stdout, 's');
+       if (vflag)
+               printc(stdout, 'v');
+       if (keyp != NULL)
+               printc(stdout, 'x');
+       printc(stdout, '\n');
+}
+
+/*
+ * Print the given line range.
+ */
+int print(int a1, int a2)
+{
+       int a;
+       char *p;
+
+       if (a1>a2 || a1<1 || a2>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       for (a=a1; a<=a2; a++) {
+               if (intflag)
+                       return (1);
+               if (egetline(a, linebuf) == 0)
+                       break;
+               p = linebuf;
+               while (*p)
+                       printc(stdout, *p++);
+               printc(stdout, '\n');
+               dotadd = a;
+       }
+       return (1);
+}
+
+/*
+ * Make a copy of the text between lines `a1' and `a2'
+ * and place them after `a3'.
+ */
+int copy(int a1, int a2, int a3)
+{
+       int i, j, n;
+
+       if (a1>a2 || a1<1 || a2>doladd || a3<0 || a3>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       n = (a2+1) - a1;
+       if (doladd+n+2 > lnsize) {
+               derror("Line table overflow");
+               return (0);
+       }
+       for (i=doladd; i>a3; --i)
+               line[i+n] = line[i];
+       for (i=0; i<n; i++) {
+               if ((j=i+a1) > a3)
+                       j += n;
+               if ((j=egetline(j, linebuf)) == 0)
+                       goto err;
+               line[a3+1+i] = linead();
+               if (putline(linebuf, j) == 0)
+                       goto err;
+       }
+       dotadd = a3+n;
+       doladd += n;
+       saved = 0;
+       return (1);
+
+err:
+       for (i=a3+1; i<=doladd; i++)
+               line[i] = line[i+n];
+       return (0);
+}
+
+/*
+ * Write the given line range onto the file whose name is
+ * stored in `file'.  `perm' is the permission string we
+ * want the file opened with.
+ */
+int wfile(int a1, int a2, char *name, char * perm, int sflag)
+{
+       int a;
+       char *cp;
+
+       if (doladd!=0 || addspec!=0) {
+               if (a1>a2 || a1<1 || a2>doladd) {
+                       derror("Address out of range");
+                       return (0);
+               }
+       }
+       if ((fp=xfopen(name, perm)) == NULL) {
+               derror("Cannot open file");
+               return (0);
+       }
+       cct = 0;
+       lct = 0;
+       for (a=a1; a<=a2; a++) {
+               if (egetline(a, linebuf) == 0)
+                       break;
+               cp = linebuf;
+               while (*cp) {
+                       printc(fp, *cp++);
+                       cct++;
+               }
+               printc(fp, '\n');
+               if (ferror(fp))
+                       break;
+               cct++;
+               lct++;
+       }
+       if (sflag==0 && cflag!=0) {
+               printl(stdout, !oflag ? cct : lct);
+               printc(stdout, '\n');
+       }
+       if ((a=ferror(fp)) == 0)
+               saved = 1;
+       else {
+               if (sflag == 0)
+                       derror("Write error");
+       }
+       fclose(fp);
+       sync();
+       return (!a);
+}
+
+/*
+ * Open a file.  If we are in encryption mode, the open unit is really a
+ * file descriptor piped to the crypt command.
+ */
+FILE *xfopen(char *fn, char *mode)
+{
+#if COHERENT
+       FILE *fp;
+       int f;
+       int u;
+       int a;
+       int b;
+       int pv[2];
+
+       if (keyp == NULL)
+               fp = fopen(fn, mode);
+       else {
+               switch (mode[0]) {
+               case 'a':
+                       return (NULL);
+               case 'r':
+                       a = 0;
+                       b = 1;
+                       break;
+               case 'w':
+                       a = 1;
+                       b = 0;
+                       break;
+               }
+               if (pipe(pv) < 0)
+                       return (NULL);
+               if ((f=fork()) < 0) {
+                       close(pv[0]);
+                       close(pv[1]);
+                       return (NULL);
+               }
+               if (f == 0) {
+                       if ((u=(a==0)?open(fn, 0):creat(fn, 0644)) < 0)
+                               exit(1);
+                       dup2(u, a);
+                       dup2(pv[b], b);
+                       close(u);
+                       close(pv[0]);
+                       close(pv[1]);
+                       execl("/bin/crypt", "crypt", keyp, NULL);
+                       exit(1);
+               }
+               close(pv[b]);
+               fp = fdopen(pv[a], mode);
+       }
+       return (fp);
+#else
+       return(fopen(fn, mode));
+#endif
+}
+
+/*
+ * Read a key from the standard input.
+ */
+int setkey(void)
+{
+#if COHERENT
+       int n;
+
+       keyp = getpass("key? ");
+       if ((n=strlen(keyp)) > CKSIZE) {
+               derror("Key too long");
+               keyp = NULL;
+               return (0);
+       }
+       if (n == 0)
+               keyp = NULL;
+       return (1);
+#else
+       derror("Cannot set key");
+       return (0);
+#endif
+}
+
+/*
+ * Regular expressions and the substitute and global commands.
+ */
+
+/*
+ * Global command.
+ */
+
+int global(int a1, int a2, int not)
+{
+       int a, c;
+       char *gp;
+
+       if (a1>a2 || a1<1 || a2>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       if (gcp != NULL) {
+               derror("Global inside global not allowed");
+               return (0);
+       }
+       if ((c=getx()) == '\n') {
+               ungetx(c);
+               derror("Syntax error");
+               return (0);
+       }
+       if (compile(c) == 0)
+               return 0;
+       gp = globbuf;
+       while ((c=getx()) != '\n') {
+               if (gp >= &globbuf[GBSIZE-3]) {
+                       derror("Global buffer overflow");
+                       return (0);
+               }
+               if (c == '\\')
+                       if ((c=getx())!='\n' && c!='\\')
+                               *gp++ = '\\';
+               *gp++ = c;
+       }
+       if (gp == globbuf)
+               goto out;
+       *gp++ = '\n';
+       *gp = '\0';
+       for (a=a1; a<=a2; a++) {
+               if (intflag)
+                       goto out;
+               if (execute(a) != not)
+                       line[a] |= 1;
+       }
+       a = 1;
+       while (a <= doladd) {
+               if (intflag)
+                       goto out;
+               if ((line[a]&1) == 0) {
+                       a++;
+                       continue;
+               }
+               --line[a];
+               dotadd = a;
+               gcp = globbuf;
+               while (*gcp != '\0')
+                       if (command() == 0)
+                               goto out;
+               a = 1;
+       }
+out:
+       for (a=1; a<=doladd; a++)
+               line[a] &= ~1;
+       gcp = NULL;
+       ungetx('\n');
+       return (1);
+}
+
+/*
+ * Substitute part 1.
+ * Check that address range is legal and parse substitute command.
+ */
+int subs1(int a1, int a2)
+{
+       int ec, c;
+       char *sp;
+
+       if (a1>a2 || a1<1 || a2>doladd) {
+               derror("Address out of range");
+               return (0);
+       }
+       subnewl = 0;
+       subnths = 1;
+       if (isascii(c=getx()) && isdigit(c)) {
+               subnths = 0;
+               while (isascii(c) && isdigit(c)) {
+                       subnths = subnths*10 + c-'0';
+                       c = getx();
+               }
+       }
+       if ((ec=c) == '\n') {
+               ungetx(c);
+               derror("Syntax error");
+               return (0);
+       }
+       if (compile(ec) == 0)
+               return (0);
+       sp = subsbuf;
+       while ((c=getx()) != ec) {
+               if (sp >= &subsbuf[SBSIZE-4]) {
+                       derror("Temporary buffer overflow");
+                       return (0);
+               }
+               switch (c) {
+               case '\n':
+                       ungetx(c);
+                       derror("Syntax error");
+                       return (0);
+               case '&':
+                       *sp++ = '\\';
+                       *sp++ = '0';
+                       continue;
+               case '\\':
+                       if ((c=getx()) == '\\') {
+                               *sp++ = '\\';
+                               *sp++ = '\\';
+                               continue;
+                       }
+                       if (c >= '1'  &&  c <= '9') {
+                               *sp++ = '\\';
+                               *sp++ = c;
+                               continue;
+                       }
+                       if (c == '\n')
+                               subnewl++;
+               default:
+                       *sp++ = c;
+                       continue;
+               }
+       }
+       *sp++ = '\0';
+       if ((c=getx()) == 'g')
+               subnths = 0;
+       else
+               ungetx(c);
+       return (1);
+}
+
+/*
+ * Substitute (part 2).
+ * Execute substitute command.
+ */
+int subs2(int a1, int a2)
+{
+       long seek, mark;
+       int len, nth, err, a;
+       char *mp, *sp;
+       int n;
+       char *lp, *rp;
+
+       err = 1;
+       for (a=a1; a<=a2; a++) {
+               if (intflag)
+                       return (1);
+               if (egetline(a, tempbuf) == 0)
+                       return (0);
+               for (n=0; n<1+BRSIZE; n++) {
+                       brace[n].b_bp = NULL;
+                       brace[n].b_ep = NULL;
+               }
+               brace[0].b_ep = tempbuf;
+               mp = tempbuf;
+               lp = linebuf;
+               nth = 0;
+               for (;;) {
+                       rp = codebuf;
+                       if (*rp == CSSOL) {
+                               if (mp != tempbuf)
+                                       break;
+                               rp++;
+                       }
+                       if ((rp=match(mp, rp)) == NULL) {
+                               if (*mp++ == '\0')
+                                       break;
+                               continue;
+                       }
+                       nth++;
+                       if (subnths) {
+                               if (nth < subnths) {
+                                       mp = rp;
+                                       continue;
+                               }
+                               if (nth > subnths)
+                                       goto done;
+                       }
+                       err = 0;
+                       saved = 0;
+                       dotadd = a;
+                       brace[0].b_bp = mp;
+                       mp = rp;
+                       rp = brace[0].b_ep;
+                       brace[0].b_ep = mp;
+                       len = mp - brace[0].b_bp;
+                       n = brace[0].b_bp - rp;
+                       if (lp+n >= &linebuf[LBSIZE-1])
+                               goto ovf;
+                       while (n--)
+                               *lp++ = *rp++;
+                       sp = subsbuf;
+                       while (*sp) {
+                               if (lp >= &linebuf[LBSIZE-4])
+                                       goto ovf;
+                               if (*sp != '\\') {
+                                       *lp++ = *sp++;
+                                       continue;
+                               }
+                               if (*++sp == '\\') {
+                                       *lp++ = *sp++;
+                                       continue;
+                               }
+                               n = *sp++ - '0';
+                               if ((rp=brace[n].b_bp) == NULL)
+                                       continue;
+                               n = brace[n].b_ep-rp;
+                               if (lp+n >= &linebuf[LBSIZE-4])
+                                       goto ovf;
+                               while (n--)
+                                       *lp++ = *rp++;
+                       }
+                       if (*mp == '\0')
+                               break;
+                       if (len == 0)
+                               mp++;
+                       if (subnths!=0 && nth==subnths)
+                               break;
+               }
+       done:
+               if (dotadd != a)
+                       continue;
+               rp = brace[0].b_ep;
+               while (*rp) {
+                       if (lp >= &linebuf[LBSIZE-1])
+                               goto ovf;
+                       *lp++ = *rp++;
+               }
+               *lp++ = '\0';
+               seek = linead();
+               if (subnewl) {
+                       lp = linebuf;
+                       while (*lp != '\n')
+                               lp++;
+                       *lp++ = '\0';
+               }
+               if (putline(linebuf, lp-linebuf) == 0)
+                       return (0);
+               suborig = line[a];
+               if (suborig&1)
+                       --suborig;
+               subseek = seek|1;
+               line[a] = seek;
+               mark = suborig|1;
+               for (n=0; n<MKSIZE; n++)
+                       if (marklin[n] == mark)
+                               marklin[n] = subseek;
+               if (subnewl != 0) {
+                       n = 1;
+                       do {
+                               mp = lp;
+                               while (*lp!='\n' && *lp!='\0')
+                                       lp++;
+                               if (*lp == '\0')
+                                       n = 0;
+                               *lp++ = '\0';
+                               seek = linead();
+                               if (putline(mp, lp-mp) == 0)
+                                       return (0);
+                               if (expand(a) == 0)
+                                       return (0);
+                               line[++a] = seek;
+                               dotadd = a;
+                               a2++;
+                       } while (n);
+               }
+       }
+       if (err!=0 && gcp==NULL) {
+               derror("Pattern not matched");
+               return (0);
+       }
+       return (1);
+
+ovf:
+       derror("Temporary buffer overflow");
+       return (0);
+}
+
+/*
+ * Compile a regular expression.  `ec' is the character upon which
+ * the regular expression ends.  If an error is encountered, the
+ * pattern is restored and input characters are thrown away until
+ * a new line is found.
+ */
+int compile(int ec)
+{
+       int bstack[BRSIZE], bcount, blevel, n;
+       int c;
+       char *cp, *lcp;
+
+       bcpy(tempbuf, codebuf);
+       bcount = 1;
+       blevel = 0;
+       cp = &codebuf[0];
+       if ((c=getx()) == ec) {
+               if (*cp == CSNUL) {
+                       derror("No saved pattern");
+                       goto err;
+               }
+               return (1);
+       }
+       if (c == '^') {
+               *cp++ = CSSOL;
+               c = getx();
+       }
+       while (c != ec) {
+               if (c == '\n') {
+                       goto nwl;
+               }
+               if (cp > &codebuf[CBSIZE-4])
+                       goto ovf;
+               switch (c) {
+               case '*':
+                       goto syn;
+               case '.':
+                       if ((c=getx()) != '*') {
+                               *cp++ = CSDOT;
+                               continue;
+                       }
+                       *cp++ = CMDOT;
+                       c = getx();
+                       continue;
+               case '$':
+                       if ((c=getx()) != ec) {
+                               ungetx(c);
+                               c = '$';
+                               goto character;
+                       }
+                       *cp++ = CSEOL;
+                       continue;
+               case '[':
+                       lcp = cp;
+                       if ((c=getx()) == '^')
+                               *cp++ = CSNCL;
+                       else {
+                               ungetx(c);
+                               *cp++ = CSCCL;
+                       }
+                       *cp++ = 0;
+                       if ((c=getx()) == ']')
+                               *cp++ = c;
+                       else
+                               ungetx(c);
+                       while ((c=getx()) != ']') {
+                               if (c == '\n')
+                                       goto nwl;
+                               if (c!='-' || cp==lcp+2) {
+                                       if (cp >= &codebuf[CBSIZE-4])
+                                               goto ovf;
+                                       *cp++ = c;
+                                       if (sflag && isascii(c) && isallet(c))
+                                               *cp++ = toother(c);
+                                       continue;
+                               }
+                               if ((c=getx()) == '\n')
+                                       goto nwl;
+                               if (c == ']') {
+                                       *cp++ = '-';
+                                       ungetx(c);
+                                       continue;
+                               }
+                               if ((n=cp[-1]) > c)
+                                       goto syn;
+                               while (++n <= c) {
+                                       if (cp >= &codebuf[CBSIZE-4])
+                                               goto ovf;
+                                       *cp++ = n;
+                                       if (sflag && isascii(c) && isallet(c))
+                                               *cp++ = toother(c);
+                               }
+                       }
+                       if ((c=getx()) == '*') {
+                               (*lcp)++;
+                               c = getx();
+                       }
+                       if ((n=cp-(lcp+2)) > 255) {
+                               derror("Character class too large");
+                               goto err;
+                       }
+                       *++lcp = n;
+                       continue;
+               case '\\':
+                       switch (c=getx()) {
+                       case '\n':
+                               goto nwl;
+                       case '(':
+                               if (bcount > BRSIZE) {
+                                       derror("Too many \\(");
+                                       goto err;
+                               }
+                               *cp++ = CSOPR;
+                               *cp++ = bstack[blevel++] = bcount++;
+                               c = getx();
+                               continue;
+                       case ')':
+                               if (blevel == 0)
+                                       goto syn;
+                               *cp++ = CSCPR;
+                               *cp++ = bstack[--blevel];
+                               c = getx();
+                               continue;
+                       default:
+                               if (isascii(c) && isdigit(c)) {
+                                       *cp++ = CSBRN;
+                                       *cp++ = c-'0';
+                                       c = getx();
+                                       continue;
+                               }
+                       }
+               default:
+               character:
+                       if (sflag && isascii(c) && isallet(c)) {
+                               *cp++ = CSSCC;
+                               if (isupper(c))
+                                       c = tolower(c);
+                       } else
+                               *cp++ = CSCHR;
+                       *cp++ = c;
+                       if ((c=getx()) == '*') {
+                               cp[-2]++;
+                               c = getx();
+                       }
+               }
+       }
+       *cp++ = CSNUL;
+       return (1);
+ovf:
+       derror("Code buffer overflow");
+       bcpy(codebuf, tempbuf);
+       return (0);
+nwl:
+       ungetx(c);
+syn:
+       derror("Syntax error");
+err:
+       bcpy(codebuf, tempbuf);
+       return (0);
+}
+
+/*
+ * Copy BSIZE buffer into dest from source
+ */
+void bcpy(char *dest, char *source)
+{
+       int n;
+
+       n = CBSIZE;
+       do
+               *dest++ = *source++;
+       while (--n > 0);
+}
+
+/*
+ * Return 1 if the compiled expression in `codebuf' matches the line in
+ * `linebuf', else 0.
+ */
+int execute(int a)
+{
+       int i;
+       char *lp, *ep;
+
+       if (egetline(a, linebuf) == 0)
+               return (0);
+       for (i=0; i<1+BRSIZE; i++) {
+               brace[i].b_bp = NULL;
+               brace[i].b_ep = NULL;
+       }
+       if (codebuf[0] == CSSOL)
+               ep = match(lp=linebuf, &codebuf[1]);
+       else {
+               ep = NULL;
+               lp = linebuf;
+               do {
+                       if (ep=match(lp, codebuf))
+                               break;
+               } while (*lp++);
+       }
+       if (ep) {
+               brace[0].b_bp = lp;
+               brace[0].b_ep = ep;
+       }
+       return (ep ? 1 : 0);
+}
+
+/*
+ * Given a pointer to a compiled expression, `cp', and a pointer to
+ * a line, `lp', return 1 if the expression matches, else 0.
+ */
+char *match(char *lp, char *cp)
+{
+       int n;
+       char *llp, *lcp;
+
+       for (;;) {
+               switch (*cp++) {
+               case CSNUL:
+                       return (lp);
+               case CSEOL:
+                       if (*lp)
+                               return (NULL);
+                       return (lp);
+               case CSOPR:
+                       brace[*cp++].b_bp = lp;
+                       continue;
+               case CSCPR:
+                       brace[*cp++].b_ep = lp;
+                       continue;
+               case CSBRN:
+                       n = *cp++;
+                       lcp = cp;
+                       cp = brace[n].b_bp;
+                       n = brace[n].b_ep - cp;
+                       if (n > LBSIZE)
+                               return (NULL);
+                       while (n-- > 0)
+                               if (*lp++ != *cp++)
+                                       return (NULL);
+                       cp = lcp;
+                       continue;
+               case CSDOT:
+                       if (*lp++ == '\0')
+                               return (NULL);
+                       continue;
+               case CMDOT:
+                       llp = lp;
+                       while (*lp)
+                               lp++;
+                       goto star;
+               case CSCHR:
+                       if (*cp++ != *lp++)
+                               return (NULL);
+                       continue;
+               case CMCHR:
+                       llp = lp;
+                       while (*cp == *lp)
+                               lp++;
+                       cp++;
+                       goto star;
+               case CSSCC:
+                       if (*cp++ == tolower(*lp++))
+                               continue;
+                       return (NULL);
+               case CMSCC:
+                       llp = lp;
+                       while (*cp == tolower(*lp++))
+                               ;
+                       cp++;
+                       goto star;
+               case CSCCL:
+                       n = *cp++;
+                       while (*cp++ != *lp)
+                               if (--n == 0)
+                                       return (NULL);
+                       lp++;
+                       cp += n-1;
+                       continue;
+               case CMCCL:
+                       llp = lp;
+                       lcp = cp;
+                       while (*lp) {
+                               cp = lcp;
+                               n = *cp++;
+                               while (*cp++ != *lp)
+                                       if (--n == 0)
+                                               goto star;
+                               lp++;
+                       }
+                       cp = lcp + *lcp + 1;
+                       goto star;
+               case CSNCL:
+                       if (*lp == '\0')
+                               return (NULL);
+                       n = *cp++;
+                       while (n--)
+                               if (*cp++ == *lp)
+                                       return (NULL);
+                       lp++;
+                       continue;
+               case CMNCL:
+                       llp = lp;
+                       lcp = cp;
+                       while (*lp) {
+                               cp = lcp;
+                               n = *cp++;
+                               while (n--) {
+                                       if (*cp++ == *lp) {
+                                               cp = lcp + *lcp + 1;
+                                               goto star;
+                                       }
+                               }
+                               lp++;
+                       }
+                       cp = lcp + *lcp + 1;
+               star:
+                       do {
+                               if (lcp=match(lp, cp))
+                                       return (lcp);
+                       } while (--lp >= llp);
+                       return (NULL);
+               }
+       }
+}
diff --git a/Applications/MWC/cmd/ed.h b/Applications/MWC/cmd/ed.h
new file mode 100644 (file)
index 0000000..639947e
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * An editor.
+ * Header file.
+ */
+
+#define ULARGE 65535L                  /* Largest unsigned value */
+#define LNSIZE 2000                    /* Initial size of line number table */
+#define LBSIZE 512                     /* Size of line buffer */
+#define CBSIZE 512                     /* Size of code buffer */
+#define TBSIZE CBSIZE                  /* Size of temporary buffer */
+#define SBSIZE 512                     /* Replace substitute buffer size */
+#define GBSIZE 512                     /* Global command buffer size */
+#define DBSIZE 512                     /* Size of disk buffer cache */
+#define BRSIZE 9                       /* Number of \( ... \) allowed */
+#define FNSIZE 64                      /* File name size */
+#define        CKSIZE  64                      /* Length of crypt key */
+#define MKSIZE 26                      /* Size of mark line table */
+#define PGSIZE 22                      /* Page size */
+
+/*
+ * Functions for finding out whether a character is a letter and
+ * to switch case from lower to upper and vice versa.
+ * Knows about ctype.h internals.
+ */
+#define isallet(c)     isalpha(c)
+#define toother(c)     ((c)^' ')
+
+/*
+ * Click size definitions.
+ * These functions are used to get the block number and offset into
+ * the block given a seek pointer gotten from the line number table.
+ * It takes into account the size of the disk buffer (DBSIZE) and
+ * the fact that the seek pointer may be off by one (caused by the
+ * global command marking lines.
+ */
+#define CLSIZE         16
+#define blockn(a)      ((a)>>6)
+#define offset(a)      (((a)&076) << 3)
+#define linead()       (tmpseek >> 3)
+
+/*
+ * Definition for stream directives in regular expressions.
+ * The order of these must not be changed.  In particular,
+ * the `stream of' directives must immediately follow their
+ * single counterparts.
+ */
+#define CSNUL  000                     /* End of expression */
+#define CSSOL  001                     /* Match start of line */
+#define CSEOL  002                     /* End of line */
+#define CSOPR  003                     /* \( */
+#define CSCPR  004                     /* \) */
+#define CSBRN  005                     /* Match nth \( ... \) */
+#define CSDOT  006                     /* Any character */
+#define CMDOT  007                     /* Stream of any characters */
+#define CSCHR  010                     /* Match given character */
+#define CMCHR  011                     /* Match stream of given characters */
+#define CSSCC  012                     /* Char in single case */
+#define CMSCC  013                     /* Stream of chars in single case */
+#define CSCCL  014                     /* Character class */
+#define CMCCL  015                     /* Stream of character class */
+#define CSNCL  016                     /* Not character class */
+#define CMNCL  017                     /* Stream of not char class */
+
+/*
+ * Typedefs.
+ */
+typedef        unsigned LINE;                  /* Typedef for line table */
+
+/*
+ * Structure for remembering \( ... \).
+ */
+typedef        struct {
+       char    *b_bp;                  /* Ptr to start of string matched */
+       char    *b_ep;                  /* Ptr to end of string matched */
+} BRACE;
+
+/*
+ * Global variables.
+ */
+extern int     cflag;                  /* Print character counts */
+extern int     mflag;                  /* Allow multiple commands per line */
+extern int     pflag;                  /* Editor prompts */
+extern int     oflag;                  /* Behaves like the old editor */
+extern int     sflag;                  /* Match patterns in single case */
+extern int     tflag;                  /* Set up for screen editor */
+extern int     vflag;                  /* Verbose error messages */
+extern int     intflag;                /* Interrupt has been hit */
+extern char    *tfn;                   /* Temp file name */
+extern FILE    *tmp;                   /* Temp file pointer */
+extern long    tmpseek;                /* Free space seek ptr in tmp file */
+extern int     rcurbno;                /* Current read block number */
+extern int     wcurbno;                /* Current write block number */
+extern LINE    *line;                  /* Pointer to line table */
+extern int     lnsize;                 /* Current size of line table */
+extern int     savechr;                /* Character that was ungetx'ed */
+extern int     lastchr;                /* Last character we read */
+extern char    *gcp;                   /* Global input pointer */
+extern char    linebuf[LBSIZE];        /* Line buffer */
+extern char    codebuf[CBSIZE];        /* Code buffer */
+extern char    tempbuf[TBSIZE];        /* Temporary buffer */
+extern char    subsbuf[SBSIZE];        /* Substitute buffer */
+extern char    globbuf[GBSIZE];        /* Buffer for global command */
+extern char    rdbcbuf[DBSIZE];        /* Disk buffer cache */
+extern char    wdbcbuf[DBSIZE];        /* Write buffer cache */
+extern BRACE   brace[1+BRSIZE];        /* For remembering \( \) */
+extern char    file[FNSIZE+1];         /* Filename */
+extern LINE    marklin[MKSIZE];        /* Mark line table */
+extern int     dotadd;                 /* Address of the current line */
+extern int     doladd;                 /* Address of last line */
+extern char    vcom;                   /* Verify command */
+extern int     saved;                  /* File saved since last written */
+extern FILE    *fp;                    /* File pointer for readfil */
+extern long    cct;                    /* Number of chars read in append */
+extern long    lct;                    /* Number of lines read in append */
+extern int     appflag;                /* In append mode */
+extern int     addspec;                /* Number of addresses specified */
+extern int     adderrr;                /* Error in computing address */
+extern int     addpage;                /* An ampersand was found */
+extern int     addques;                /* A question mark was found */
+extern int     subnths;                /* Which substitute wanted */
+extern int     subnewl;                /* A newline is being replaced */
+extern int     subseek;                /* Seek position of new line */
+extern int     suborig;                /* Seek position of old line */
+extern char    *errstr;                /* Pointer to last error message */
+extern char    *keyp;                  /* Pointer to crypt key */
+
+extern void initialise(void);
+extern void setup(int argc, char *argv[]);
+extern void usage(void);
+extern void sigintr(int s);
+extern void sighang(int s);
+extern void leave(void);
+extern int command(void);
+extern int getaddr(void);
+extern int getx(void);
+extern void ungetx(int c);
+extern int egetline(int a, char *buffer);
+extern int putline(char *bp, int n);
+extern char *getdisk(int bno);
+extern int putdisk(void);
+extern void derror(char *str);
+extern void terror(char *str);
+extern void printd(FILE *fp, int n);
+extern void printl(FILE *fp, long n);
+extern void printo(FILE *fp, int n);
+extern void prints(FILE *fp, char *cp);
+extern void printc(FILE *fp, char c);
+extern int edit(void);
+extern int verify(int flag);
+extern int getfile(char *name);
+extern int rest(void);
+extern int append(int a, int (*f)(void));
+extern int expand(int a);
+extern int readtty(void);
+extern int readfil(void);
+extern int delete(int a1, int a2);
+extern int join(int a1, int a2);
+extern int list(int a1, int a2);
+extern int move(int a1,int a2,int a3);
+extern void setoptf(char *sp);
+extern void disoptf(void);
+extern int print(int a1, int a2);
+extern int copy(int a1, int a2, int a3);
+extern int wfile(int a1, int a2, char *name, char * perm, int sflag);
+extern FILE *xfopen(char *fn, char *mode);
+extern int setkey(void);
+extern int global(int a1, int a2, int not);
+extern int subs1(int a1, int a2);
+extern int subs2(int a1, int a2);
+extern int compile(int ec);
+extern void bcpy(char *dest, char *source);
+extern int execute(int a);
+extern char *match(char *lp, char *cp);
+