BSD 2 development
authorBill Joy <wnj@ucbvax.Berkeley.EDU>
Wed, 9 May 1979 07:19:53 +0000 (23:19 -0800)
committerBill Joy <wnj@ucbvax.Berkeley.EDU>
Wed, 9 May 1979 07:19:53 +0000 (23:19 -0800)
Work on file src/ex/ex_re.c
Work on file src/ex/ex_re.h
Work on file src/ex/ex_set.c
Work on file src/ex/ex_subr.c

Synthesized-from: 2bsd

src/ex/ex_re.c [new file with mode: 0644]
src/ex/ex_re.h [new file with mode: 0644]
src/ex/ex_set.c [new file with mode: 0644]
src/ex/ex_subr.c [new file with mode: 0644]

diff --git a/src/ex/ex_re.c b/src/ex/ex_re.c
new file mode 100644 (file)
index 0000000..114a7cc
--- /dev/null
@@ -0,0 +1,845 @@
+/* Copyright (c) 1979 Regents of the University of California */
+#include "ex.h"
+#include "ex_re.h"
+
+/*
+ * Global, substitute and regular expressions.
+ * Very similar to ed, with some re extensions and
+ * confirmed substitute.
+ */
+global(k)
+       bool k;
+{
+       register char *gp;
+       register int c;
+       register line *a1;
+       char globuf[GBSIZE], *Cwas;
+       int lines = lineDOL();
+       char *oglobp = globp;
+
+       Cwas = Command;
+       if (inglobal)
+               error("Global within global@not allowed");
+       markDOT();
+       setall();
+       nonzero();
+       if (skipend())
+               error("Global needs re|Missing regular expression for global");
+       c = getchar();
+       ignore(compile(c, 0));
+       savere(scanre);
+       gp = globuf;
+       while ((c = getchar()) != '\n') {
+               switch (c) {
+
+               case EOF:
+                       c = '\n';
+                       goto brkwh;
+
+               case '\\':
+                       c = getchar();
+                       switch (c) {
+
+                       case '\\':
+                               ungetchar(c);
+                               break;
+
+                       case '\n':
+                               break;
+
+                       default:
+                               *gp++ = '\\';
+                               break;
+                       }
+                       break;
+               }
+               *gp++ = c;
+               if (gp >= &globuf[GBSIZE - 2])
+                       error("Global command too long");
+       }
+brkwh:
+       ungetchar(c);
+out:
+       newline();
+       *gp++ = c;
+       *gp++ = 0;
+       inglobal = 1;
+       for (a1 = one; a1 <= dol; a1++) {
+               *a1 &= ~01;
+               if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
+                       *a1 |= 01;
+       }
+       /* should use gdelete from ed to avoid n**2 here on g/.../d */
+       saveall();
+       if (inopen)
+               inopen = -1;
+       for (a1 = one; a1 <= dol; a1++) {
+               if (*a1 & 01) {
+                       *a1 &= ~01;
+                       dot = a1;
+                       globp = globuf;
+                       commands(1, 1);
+                       a1 = zero;
+               }
+       }
+       globp = oglobp;
+       inglobal = 0;
+       endline = 1;
+       Command = Cwas;
+       netchHAD(lines);
+       setlastchar(EOF);
+       if (inopen) {
+               ungetchar(EOF);
+               inopen = 1;
+       }
+}
+
+bool   xflag;
+int    scount, slines, stotal;
+
+substitute(c)
+       int c;
+{
+       register line *addr;
+       register int n;
+       int gsubf;
+
+       gsubf = compsub(c);
+       if (!inglobal)
+               save12(), undkind = UNDCHANGE;
+       stotal = 0;
+       slines = 0;
+       for (addr = addr1; addr <= addr2; addr++) {
+               scount = 0;
+               if (dosubcon(0, addr) == 0)
+                       continue;
+               if (gsubf)
+                       while (*loc2)
+                               if (dosubcon(1, addr) == 0)
+                                       break;
+               if (scount) {
+                       stotal += scount;
+                       slines++;
+                       putmark(addr);
+                       n = append(getsub, addr);
+                       addr += n;
+                       addr2 += n;
+               }
+       }
+       if (stotal == 0 && !inglobal && !xflag)
+               error("Fail|Substitute pattern match failed");
+       snote(stotal, slines);
+       return (stotal);
+}
+
+compsub(ch)
+{
+       register int seof, c;
+       int gsubf;
+
+       gsubf = 0;
+       xflag = 0;
+       switch (ch) {
+
+       case 's':
+               ignore(skipwh());
+               seof = getchar();
+               if (endcmd(seof))
+                       error("Substitute needs re|Missing regular expression for substitute");
+               seof = compile(seof, 1);
+               savere(subre);
+               comprhs(seof);
+               break;
+
+       case '&':
+               if (subre.Expbuf[0] == 0)
+                       error("No previous substitute re|No previous substitute to repeat");
+               resre(subre);
+               break;
+
+       case '~':
+               if (re.Expbuf[0] == 0)
+                       error("No previous re|No previous regular expression");
+               savere(subre);
+               break;
+       }
+       for (;;) {
+               c = getchar();
+               switch (c) {
+
+               case 'g':
+                       gsubf++;
+                       continue;
+
+               case 'c':
+                       xflag++;
+                       continue;
+
+               default:
+                       ungetchar(c);
+                       setcount();
+                       newline();
+                       return (gsubf);
+               }
+       }
+}
+
+comprhs(seof)
+       int seof;
+{
+       register char *rp, *orp;
+       register int c;
+       char orhsbuf[LBSIZE / 2];
+
+       rp = rhsbuf;
+       CP(orhsbuf, rp);
+       for (;;) {
+               c = getchar();
+               if (c == seof)
+                       break;
+               switch (c) {
+
+               case '\\':
+                       c = getchar();
+                       if (c == EOF) {
+                               ungetchar(c);
+                               break;
+                       }
+                       if (value(MAGIC)) {
+                               /*
+                                * When "magic", \& turns into a plain &,
+                                * and all other chars work fine quoted.
+                                */
+                               if (c != '&')
+                                       c |= QUOTE;
+                               break;
+                       }
+magic:
+                       if (c == '~') {
+                               for (orp = orhsbuf; *orp; *rp++ = *orp++)
+                                       if (rp >= &rhsbuf[LBSIZE / 2 + 1])
+                                               goto toobig;
+                               continue;
+                       }
+                       c |= QUOTE;
+                       break;
+
+               case '\n':
+               case EOF:
+                       ungetchar(c);
+                       goto endrhs;
+
+               case '~':
+               case '&':
+                       if (value(MAGIC))
+                               goto magic;
+                       break;
+               }
+               if (rp >= &rhsbuf[LBSIZE / 2 - 1])
+toobig:
+                       error("Replacement pattern too long@- limit 256 characters");
+               *rp++ = c;
+       }
+endrhs:
+       *rp++ = 0;
+}
+
+getsub()
+{
+       register char *p;
+
+       if ((p = linebp) == 0)
+               return (EOF);
+       strcLIN(p);
+       linebp = 0;
+       return (0);
+}
+
+dosubcon(f, a)
+       bool f;
+       line *a;
+{
+
+       if (execute(f, a) == 0)
+               return (0);
+       if (confirmed(a)) {
+               dosub();
+               scount++;
+       }
+       return (1);
+}
+
+confirmed(a)
+       line *a;
+{
+       register int c, ch;
+
+       if (xflag == 0)
+               return (1);
+       pofix();
+       pline(lineno(a));
+       if (inopen)
+               putchar('\n' | QUOTE);
+       c = column(loc1 - 1);
+       ugo(c - 1 + (inopen ? 1 : 0), ' ');
+       ugo(column(loc2 - 1) - c, '^');
+       flush();
+       ch = c = getkey();
+again:
+       if (c == '\r')
+               c = '\n';
+       if (inopen)
+               putchar(c), flush();
+       if (c != '\n' && c != EOF) {
+               c = getkey();
+               goto again;
+       }
+       noteinp();
+       return (ch == 'y');
+}
+
+getch()
+{
+       char c;
+
+       if (read(2, &c, 1) != 1)
+               return (EOF);
+       return (c & TRIM);
+}
+
+ugo(cnt, with)
+       int with;
+       int cnt;
+{
+
+       if (cnt > 0)
+               do
+                       putchar(with);
+               while (--cnt > 0);
+}
+
+int    casecnt;
+bool   destuc;
+
+dosub()
+{
+       register char *lp, *sp, *rp;
+       int c;
+
+       lp = linebuf;
+       sp = genbuf;
+       rp = rhsbuf;
+       while (lp < loc1)
+               *sp++ = *lp++;
+       casecnt = 0;
+       while (c = *rp++) {
+               if (c & QUOTE)
+                       switch (c & TRIM) {
+
+                       case '&':
+                               sp = place(sp, loc1, loc2);
+                               if (sp == 0)
+                                       goto ovflo;
+                               continue;
+
+                       case 'l':
+                               casecnt = 1;
+                               destuc = 0;
+                               continue;
+
+                       case 'L':
+                               casecnt = LBSIZE;
+                               destuc = 0;
+                               continue;
+
+                       case 'u':
+                               casecnt = 1;
+                               destuc = 1;
+                               continue;
+
+                       case 'U':
+                               casecnt = LBSIZE;
+                               destuc = 1;
+                               continue;
+
+                       case 'E':
+                       case 'e':
+                               casecnt = 0;
+                               continue;
+                       }
+               if (c < 0 && (c &= TRIM) >= '1' && c < nbra + '1') {
+                       sp = place(sp, braslist[c - '1'], braelist[c - '1']);
+                       if (sp == 0)
+                               goto ovflo;
+                       continue;
+               }
+               if (casecnt)
+                       *sp++ = fixcase(c & TRIM);
+               else
+                       *sp++ = c & TRIM;
+               if (sp >= &genbuf[LBSIZE])
+ovflo:
+                       error("Line overflow@in substitute - limit 512 chars");
+       }
+       lp = loc2;
+       loc2 = sp + (linebuf - genbuf) + (loc1 == loc2);
+       while (*sp++ = *lp++)
+               if (sp >= &genbuf[LBSIZE])
+                       goto ovflo;
+       strcLIN(genbuf);
+}
+
+fixcase(c)
+       register int c;
+{
+
+       if (casecnt == 0)
+               return (c);
+       casecnt--;
+       if (destuc) {
+               if (islower(c))
+                       c = toupper(c);
+       } else
+               if (isupper(c))
+                       c = tolower(c);
+       return (c);
+}
+
+char *
+place(sp, l1, l2)
+       register char *sp, *l1, *l2;
+{
+
+       while (l1 < l2) {
+               *sp++ = fixcase(*l1++);
+               if (sp >= &genbuf[LBSIZE])
+                       return (0);
+       }
+       return (sp);
+}
+
+snote(total, lines)
+       register int total, lines;
+{
+
+       if (!notable(total))
+               return;
+       printf(mesg("%d subs|%d substitutions"), total);
+       if (lines != 1 && lines != total)
+               printf(" on %d lines", lines);
+       noonl();
+       flush();
+}
+
+compile(eof, oknl)
+       int eof;
+       int oknl;
+{
+       register int c;
+       register char *ep;
+       char *lastep;
+       char bracket[NBRA], *bracketp, *rhsp;
+       int cclcnt;
+
+       if (isalpha(eof) || isdigit(eof))
+               error("Re delimiter must not be letter or digit|Regular expressions cannot be delimited by letters or digits");
+       ep = expbuf;
+       c = getchar();
+       if (eof == '\\')
+               switch (c) {
+
+               case '/':
+               case '?':
+                       if (scanre.Expbuf[0] == 0)
+                               error("No previous scan re|No previous scanning regular expression");
+                       resre(scanre);
+                       return (c);
+
+               case '&':
+                       if (subre.Expbuf[0] == 0)
+                               error("No previous substitute re|No previous substitute regular expression");
+                       resre(subre);
+                       return (c);
+
+               default:
+                       error("Badly formed re|Regular expression \ must be followed by / or ?");
+               }
+       if (c == eof || c == '\n' || c == EOF) {
+               if (*ep == 0)
+                       error("No previous re|No previous regular expression");
+               if (c == '\n' && oknl == 0)
+                       error("Missing closing delimiter@for regular expression");
+               if (c != eof)
+                       ungetchar(c);
+               return (eof);
+       }
+       bracketp = bracket;
+       nbra = 0;
+       circfl = 0;
+       if (c == '^') {
+               c = getchar();
+               circfl++;
+       }
+       ungetchar(c);
+       for (;;) {
+               if (ep >= &expbuf[ESIZE - 2])
+complex:
+                       cerror("Re too complex|Regular expression too complicated");
+               c = getchar();
+               if (c == eof || c == EOF) {
+                       if (bracketp != bracket)
+                               cerror("Unmatched \\(|More \\('s than \\)'s in regular expression");
+                       *ep++ = CEOF;
+                       if (c == EOF)
+                               ungetchar(c);
+                       return (eof);
+               }
+               if (value(MAGIC)) {
+                       if (c != '*' || ep == expbuf)
+                               lastep = ep;
+               } else
+                       if (c != '\\' || peekchar() != '*' || ep == expbuf)
+                               lastep = ep;
+               switch (c) {
+
+               case '\\':
+                       if (!intag)
+                               c = getchar();
+                       switch (c) {
+
+                       case '(':
+                               if (nbra >= NBRA)
+                                       cerror("Awash in \\('s!|Too many \\('d subexressions in a regular expression");
+                               *bracketp++ = nbra;
+                               *ep++ = CBRA;
+                               *ep++ = nbra++;
+                               continue;
+
+                       case ')':
+                               if (bracketp <= bracket)
+                                       cerror("Extra \\)|More \\)'s than \\('s in regular expression");
+                               *ep++ = CKET;
+                               *ep++ = *--bracketp;
+                               continue;
+
+                       case '<':
+                               *ep++ = CBRC;
+                               continue;
+
+                       case '>':
+                               *ep++ = CLET;
+                               continue;
+                       }
+                       if (value(MAGIC) == 0 && !intag)
+magic:
+                       switch (c) {
+
+                       case '.':
+                               *ep++ = CDOT;
+                               continue;
+
+                       case '~':
+                               rhsp = rhsbuf;
+                               while (*rhsp) {
+                                       if (*rhsp & QUOTE) {
+                                               c = *rhsp & TRIM;
+                                               if (c == '&')
+                                                       error("Replacement pattern contains &@- cannot use in re");
+                                               if (c >= '1' && c <= '9')
+                                                       error("Replacement pattern contains \\d@- cannot use in re");
+                                       }
+                                       if (ep >= &expbuf[ESIZE-2])
+                                               goto complex;
+                                       *ep++ = CCHR;
+                                       *ep++ = *rhsp++ & TRIM;
+                               }
+                               continue;
+
+                       case '*':
+                               if (ep == expbuf)
+                                       break;
+                               if (*lastep == CBRA || *lastep == CKET)
+                                       cerror("Illegal *|Can't * a \\( ... \\) in regular expression");
+                               if (*lastep == CCHR && (lastep[1] & QUOTE))
+                                       cerror("Illegal *|Can't * a \\n in regular expression");
+                               *lastep |= STAR;
+                               continue;
+
+                       case '[':
+                               *ep++ = CCL;
+                               *ep++ = 0;
+                               cclcnt = 1;
+                               c = getchar();
+                               if (c == '^') {
+                                       c = getchar();
+                                       ep[-2] = NCCL;
+                               }
+                               if (c == ']')
+                                       cerror("Bad character class|Empty character class '[]' or '[^]' cannot match");
+                               while (c != ']') {
+                                       if (c == '\\' && any(peekchar(), "]-^\\"))
+                                               c = getchar() | QUOTE;
+                                       if (c == '\n' || c == EOF)
+                                               cerror("Missing ]");
+                                       *ep++ = c;
+                                       cclcnt++;
+                                       if (ep >= &expbuf[ESIZE])
+                                               goto complex;
+                                       c = getchar();
+                               }
+                               lastep[1] = cclcnt;
+                               continue;
+                       }
+                       if (c == EOF) {
+                               ungetchar(EOF);
+                               c = '\\';
+                               goto defchar;
+                       }
+                       *ep++ = CCHR;
+                       if (c == '\n')
+                               cerror("No newlines in re's|Can't escape newlines into regular expressions");
+/*
+                       if (c < '1' || c > NBRA + '1') {
+*/
+                               *ep++ = c;
+                               continue;
+/*
+                       }
+                       c -= '1';
+                       if (c >= nbra)
+                               cerror("Bad \\n|\\n in regular expression with n greater than the number of \\('s");
+                       *ep++ = c | QUOTE;
+                       continue;
+*/
+
+               case '\n':
+                       if (oknl) {
+                               ungetchar(c);
+                               *ep++ = CEOF;
+                               return (eof);
+                       }
+                       cerror("Badly formed re|Missing closing delimiter for regular expression");
+
+               case '$':
+                       if (peekchar() == eof || peekchar() == EOF || oknl && peekchar() == '\n') {
+                               *ep++ = CDOL;
+                               continue;
+                       }
+                       goto defchar;
+
+               case '.':
+               case '~':
+               case '*':
+               case '[':
+                       if (value(MAGIC) && !intag)
+                               goto magic;
+defchar:
+               default:
+                       *ep++ = CCHR;
+                       *ep++ = c;
+                       continue;
+               }
+       }
+}
+
+cerror(s)
+       char *s;
+{
+
+       expbuf[0] = 0;
+       error(s);
+}
+
+same(a, b)
+       register int a, b;
+{
+
+       return (a == b || value(IGNORECASE) &&
+          ((islower(a) && toupper(a) == b) || (islower(b) && toupper(b) == a)));
+}
+
+char   *locs;
+
+execute(gf, addr)
+       line *addr;
+{
+       register char *p1, *p2;
+       register int c;
+
+       if (gf) {
+               if (circfl)
+                       return (0);
+               locs = p1 = loc2;
+       } else {
+               if (addr == zero)
+                       return (0);
+               p1 = linebuf;
+               getline(*addr);
+               locs = 0;
+       }
+       p2 = expbuf;
+       if (circfl) {
+               loc1 = p1;
+               return (advance(p1, p2));
+       }
+       /* fast check for first character */
+       if (*p2 == CCHR) {
+               c = p2[1];
+               do {
+                       if (c != *p1 && (!value(IGNORECASE) ||
+                          !((islower(c) && toupper(c) == *p1) || (islower(*p1) && toupper(*p1) == c))))
+                               continue;
+                       if (advance(p1, p2)) {
+                               loc1 = p1;
+                               return (1);
+                       }
+               } while (*p1++);
+               return (0);
+       }
+       /* regular algorithm */
+       do {
+               if (advance(p1, p2)) {
+                       loc1 = p1;
+                       return (1);
+               }
+       } while (*p1++);
+       return (0);
+}
+
+#define        uletter(c)      (isalpha(c) || c == '_')
+
+advance(lp, ep)
+       register char *lp, *ep;
+{
+       register char *curlp;
+       char *sp, *sp1;
+       int c;
+
+       for (;;) switch (*ep++) {
+
+       case CCHR:
+/* useless
+               if (*ep & QUOTE) {
+                       c = *ep++ & TRIM;
+                       sp = braslist[c];
+                       sp1 = braelist[c];
+                       while (sp < sp1) {
+                               if (!same(*sp, *lp))
+                                       return (0);
+                               sp++, lp++;
+                       }
+                       continue;
+               }
+*/
+               if (!same(*ep, *lp))
+                       return (0);
+               ep++, lp++;
+               continue;
+
+       case CDOT:
+               if (*lp++)
+                       continue;
+               return (0);
+
+       case CDOL:
+               if (*lp == 0)
+                       continue;
+               return (0);
+
+       case CEOF:
+               loc2 = lp;
+               return (1);
+
+       case CCL:
+               if (cclass(ep, *lp++, 1)) {
+                       ep += *ep;
+                       continue;
+               }
+               return (0);
+
+       case NCCL:
+               if (cclass(ep, *lp++, 0)) {
+                       ep += *ep;
+                       continue;
+               }
+               return (0);
+
+       case CBRA:
+               braslist[*ep++] = lp;
+               continue;
+
+       case CKET:
+               braelist[*ep++] = lp;
+               continue;
+
+       case CDOT|STAR:
+               curlp = lp;
+               while (*lp++)
+                       continue;
+               goto star;
+
+       case CCHR|STAR:
+               curlp = lp;
+               while (same(*lp, *ep))
+                       lp++;
+               lp++;
+               ep++;
+               goto star;
+
+       case CCL|STAR:
+       case NCCL|STAR:
+               curlp = lp;
+               while (cclass(ep, *lp++, ep[-1] == (CCL|STAR)))
+                       continue;
+               ep += *ep;
+               goto star;
+star:
+               do {
+                       lp--;
+                       if (lp == locs)
+                               break;
+                       if (advance(lp, ep))
+                               return (1);
+               } while (lp > curlp);
+               return (0);
+
+       case CBRC:
+               if (lp == expbuf)
+                       continue;
+               if ((isdigit(*lp) || uletter(*lp)) && !uletter(lp[-1]) && !isdigit(lp[-1]))
+                       continue;
+               return (0);
+
+       case CLET:
+               if (!uletter(*lp) && !isdigit(*lp))
+                       continue;
+               return (0);
+
+       default:
+               error("Re internal error");
+       }
+}
+
+cclass(set, c, af)
+       register char *set;
+       register int c;
+       int af;
+{
+       register int n;
+
+       if (c == 0)
+               return (0);
+       if (value(IGNORECASE) && isupper(c))
+               c = tolower(c);
+       n = *set++;
+       while (--n)
+               if (n > 2 && set[1] == '-') {
+                       if (c >= (set[0] & TRIM) && c <= (set[2] & TRIM))
+                               return (af);
+                       set += 3;
+                       n -= 2;
+               } else
+                       if ((*set++ & TRIM) == c)
+                               return (af);
+       return (!af);
+}
diff --git a/src/ex/ex_re.h b/src/ex/ex_re.h
new file mode 100644 (file)
index 0000000..2f88c74
--- /dev/null
@@ -0,0 +1,66 @@
+/* Copyright (c) 1979 Regents of the University of California */
+/*
+ * Regular expression definitions.
+ * The regular expressions in ex are similar to those in ed,
+ * with the addition of the word boundaries from Toronto ed
+ * and allowing character classes to have [a-b] as in the shell.
+ * The numbers for the nodes below are spaced further apart then
+ * necessary because I at one time partially put in + and | (one or
+ * more and alternation.)
+ */
+struct regexp {
+       char    Expbuf[ESIZE + 2];
+       bool    Circfl;
+       short   Nbra;
+};
+
+/*
+ * There are three regular expressions here, the previous (in re),
+ * the previous substitute (in subre) and the previous scanning (in scanre).
+ * It would be possible to get rid of "re" by making it a stack parameter
+ * to the appropriate routines.
+ */
+struct regexp re;              /* Last re */
+struct regexp scanre;          /* Last scanning re */
+struct regexp subre;           /* Last substitute re */
+
+/*
+ * Defining circfl and expbuf like this saves us from having to change
+ * old code in the ex_re.c stuff.
+ */
+#define        expbuf  re.Expbuf
+#define        circfl  re.Circfl
+#define        nbra    re.Nbra
+
+/*
+ * Since the phototypesetter v7-epsilon
+ * C compiler doesn't have structure assignment...
+ */
+#define        savere(a)       copy(&a, &re, sizeof (struct regexp));
+#define        resre(a)        copy(&re, &a, sizeof (struct regexp));
+
+/*
+ * Definitions for substitute
+ */
+char   *braslist[NBRA];        /* Starts of \(\)'ed text in lhs */
+char   *braelist[NBRA];        /* Ends... */
+char   rhsbuf[RHSSIZE];        /* Rhs of last substitute */
+
+/*
+ * Definitions of codes for the compiled re's.
+ * The re algorithm is described in a paper
+ * by K. Thompson in the CACM about 10 years ago
+ * and is the same as in ed.
+ */
+#define        STAR    1
+
+#define        CBRA    1
+#define        CDOT    4
+#define        CCL     8
+#define        NCCL    12
+#define        CDOL    16
+#define        CEOF    17
+#define        CKET    18
+#define        CCHR    20
+#define        CBRC    24
+#define        CLET    25
diff --git a/src/ex/ex_set.c b/src/ex/ex_set.c
new file mode 100644 (file)
index 0000000..7724742
--- /dev/null
@@ -0,0 +1,178 @@
+/* Copyright (c) 1979 Regents of the University of California */
+#include "ex.h"
+#include "ex_temp.h"
+
+/*
+ * Set command.
+ */
+char   optname[ONMSZ];
+
+set()
+{
+       register char *cp;
+       register struct option *op;
+       register int c;
+       bool no;
+
+       setnoaddr();
+       if (skipend()) {
+               if (peekchar() != EOF)
+                       ignchar();
+               propts();
+               return;
+       }
+       do {
+               cp = optname;
+               do {
+                       if (cp < &optname[ONMSZ - 2])
+                               *cp++ = getchar();
+               } while (isalpha(peekchar()));
+               *cp = 0;
+               cp = optname;
+               if (eq("all", cp)) {
+                       if (inopen)
+                               pofix();
+                       prall();
+                       goto next;
+               }
+               no = 0;
+               if (cp[0] == 'n' && cp[1] == 'o') {
+                       cp += 2;
+                       no++;
+               }
+               for (op = options; op < &options[NOPTS]; op++)
+                       if (eq(op->oname, cp) || op->oabbrev && eq(op->oabbrev, cp))
+                               break;
+               if (op->oname == 0)
+                       serror("%s: No such option@- 'set all' gives all option values", cp);
+               c = skipwh();
+               if (peekchar() == '?') {
+                       ignchar();
+printone:
+                       propt(op);
+                       noonl();
+                       goto next;
+               }
+               if (op->otype == ONOFF) {
+                       op->ovalue = 1 - no;
+                       goto next;
+               }
+               if (no)
+                       serror("Option %s is not a toggle", op->oname);
+               if (c != 0 || setend())
+                       goto printone;
+               if (getchar() != '=')
+                       serror("Missing =@in assignment to option %s", op->oname);
+               switch (op->otype) {
+
+               case NUMERIC:
+                       if (!isdigit(peekchar()))
+                               error("Digits required@after = when assigning numeric option");
+                       op->ovalue = getnum();
+                       if (value(TABSTOP) <= 0)
+                               value(TABSTOP) = TABS;
+                       break;
+
+               case STRING:
+               case OTERM:
+                       cp = optname;
+                       while (!setend()) {
+                               if (cp >= &optname[ONMSZ])
+                                       error("String too long@in option assignment");
+                               *cp++ = getchar();
+                       }
+                       *cp = 0;
+                       if (op->otype == OTERM) {
+                               if (inopen)
+                                       error("Can't change type of terminal from within open/visual");
+                               setterm(optname);
+                       } else {
+                               CP(op->osvalue, optname);
+                               op->odefault = 1;
+                       }
+                       break;
+               }
+next:
+               flush();
+       } while (!skipend());
+       eol();
+}
+
+setend()
+{
+
+       return (iswhite(peekchar()) || endcmd(peekchar()));
+}
+
+prall()
+{
+       register int incr = (NOPTS + 2) / 3;
+       register int rows = incr;
+       register struct option *op = options;
+
+       for (; rows; rows--, op++) {
+               propt(op);
+               tab(24);
+               propt(&op[incr]);
+               if (&op[2*incr] < &options[NOPTS]) {
+                       tab(48);
+                       propt(&op[2 * incr]);
+               }
+               putNFL();
+       }
+}
+
+propts()
+{
+       register struct option *op;
+
+       for (op = options; op < &options[NOPTS]; op++) {
+#ifdef V6
+               if (op == &options[TERM])
+#else
+               if (op == &options[TTYTYPE])
+#endif
+                       continue;
+               switch (op->otype) {
+
+               case ONOFF:
+               case NUMERIC:
+                       if (op->ovalue == op->odefault)
+                               continue;
+                       break;
+
+               case STRING:
+                       if (op->odefault == 0)
+                               continue;
+                       break;
+               }
+               propt(op);
+               putchar(' ');
+       }
+       noonl();
+       flush();
+}
+
+propt(op)
+       register struct option *op;
+{
+       register char *name;
+       
+       name = op->oname;
+
+       switch (op->otype) {
+
+       case ONOFF:
+               printf("%s%s", op->ovalue ? "" : "no", name);
+               break;
+
+       case NUMERIC:
+               printf("%s=%d", name, op->ovalue);
+               break;
+
+       case STRING:
+       case OTERM:
+               printf("%s=%s", name, op->osvalue);
+               break;
+       }
+}
diff --git a/src/ex/ex_subr.c b/src/ex/ex_subr.c
new file mode 100644 (file)
index 0000000..318cc9e
--- /dev/null
@@ -0,0 +1,743 @@
+/* Copyright (c) 1979 Regents of the University of California */
+#include "ex.h"
+#include "ex_re.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Random routines, in alphabetical order.
+ */
+
+any(c, s)
+       int c;
+       register char *s;
+{
+       register int x;
+
+       while (x = *s++)
+               if (x == c)
+                       return (1);
+       return (0);
+}
+
+backtab(i)
+       register int i;
+{
+       register int j;
+
+       j = i % value(SHIFTWIDTH);
+       if (j == 0)
+               j = value(SHIFTWIDTH);
+       i -= j;
+       if (i < 0)
+               i = 0;
+       return (i);
+}
+
+change()
+{
+
+       tchng++;
+       chng = tchng;
+}
+
+/*
+ * Column returns the number of
+ * columns occupied by printing the
+ * characters through position cp of the
+ * current line.
+ */
+column(cp)
+       register char *cp;
+{
+
+       if (cp == 0)
+               cp = &linebuf[LBSIZE - 2];
+       return (qcolumn(cp, (char *) 0));
+}
+
+Copy(to, from, size)
+       register char *from, *to;
+       register int size;
+{
+
+       if (size > 0)
+               do
+                       *to++ = *from++;
+               while (--size > 0);
+}
+
+copyw(to, from, size)
+       register line *from, *to;
+       register int size;
+{
+
+       if (size > 0)
+               do
+                       *to++ = *from++;
+               while (--size > 0);
+}
+
+copywR(to, from, size)
+       register line *from, *to;
+       register int size;
+{
+
+       while (--size >= 0)
+               to[size] = from[size];
+}
+
+ctlof(c)
+       int c;
+{
+
+       return (c == TRIM ? '?' : c | ('A' - 1));
+}
+
+dingdong()
+{
+
+       if (VB)
+               putpad(VB);
+       else if (value(ERRORBELLS))
+               putch('\207');
+}
+
+fixindent(indent)
+       int indent;
+{
+       register int i;
+       register char *cp;
+
+       i = whitecnt(genbuf);
+       cp = vpastwh(genbuf);
+       if (*cp == 0 && i == indent && linebuf[0] == 0) {
+               genbuf[0] = 0;
+               return (i);
+       }
+       CP(genindent(i), cp);
+       return (i);
+}
+
+filioerr(cp)
+       char *cp;
+{
+       register int oerrno = errno;
+
+       lprintf("\"%s\"", cp);
+       errno = oerrno;
+       syserror();
+}
+
+char *
+genindent(indent)
+       register int indent;
+{
+       register char *cp;
+
+       for (cp = genbuf; indent >= value(TABSTOP); indent -= value(TABSTOP))
+               *cp++ = '\t';
+       for (; indent > 0; indent--)
+               *cp++ = ' ';
+       return (cp);
+}
+
+getDOT()
+{
+
+       getline(*dot);
+}
+
+line *
+getmark(c)
+       register int c;
+{
+       register line *addr;
+       
+       for (addr = one; addr <= dol; addr++)
+               if (names[c - 'a'] == (*addr &~ 01)) {
+                       return (addr);
+               }
+       return (0);
+}
+
+getn(cp)
+       register char *cp;
+{
+       register int i = 0;
+
+       while (isdigit(*cp))
+               i = i * 10 + *cp++ - '0';
+       if (*cp)
+               return (0);
+       return (i);
+}
+
+ignnEOF()
+{
+       register int c = getchar();
+
+       if (c == EOF)
+               ungetchar(c);
+}
+
+iswhite(c)
+       int c;
+{
+
+       return (c == ' ' || c == '\t');
+}
+
+junk(c)
+       register int c;
+{
+
+       if (c && !value(BEAUTIFY))
+               return (0);
+       if (c >= ' ' && c != TRIM)
+               return (0);
+       switch (c) {
+
+       case '\t':
+       case '\n':
+       case '\f':
+               return (0);
+
+       default:
+               return (1);
+       }
+}
+
+killed()
+{
+
+       killcnt(addr2 - addr1 + 1);
+}
+
+killcnt(cnt)
+       register int cnt;
+{
+
+       if (inopen) {
+               notecnt = cnt;
+               notenam = notesgn = "";
+               return;
+       }
+       if (!notable(cnt))
+               return;
+       printf("%d lines", cnt);
+       if (value(TERSE) == 0) {
+               printf(" %c%s", Command[0] | ' ', Command + 1);
+               if (Command[strlen(Command) - 1] != 'e')
+                       putchar('e');
+               putchar('d');
+       }
+       putNFL();
+}
+
+lineno(a)
+       line *a;
+{
+
+       return (a - zero);
+}
+
+lineDOL()
+{
+
+       return (lineno(dol));
+}
+
+lineDOT()
+{
+
+       return (lineno(dot));
+}
+
+markDOT()
+{
+
+       markpr(dot);
+}
+
+markpr(which)
+       line *which;
+{
+
+       if ((inglobal == 0 || inopen) && which <= endcore) {
+               names['z'-'a'+1] = *which & ~01;
+               if (inopen)
+                       ncols['z'-'a'+1] = cursor;
+       }
+}
+
+markreg(c)
+       register int c;
+{
+
+       if (c == '\'' || c == '`')
+               return ('z' + 1);
+       if (c >= 'a' && c <= 'z')
+               return (c);
+       return (0);
+}
+
+/*
+ * Mesg decodes the terse/verbose strings. Thus
+ *     'xxx@yyy' -> 'xxx' if terse, else 'xxx yyy'
+ *     'xxx|yyy' -> 'xxx' if terse, else 'yyy'
+ * All others map to themselves.
+ */
+char *
+mesg(str)
+       register char *str;
+{
+       register char *cp;
+
+       str = strcpy(genbuf, str);
+       for (cp = str; *cp; cp++)
+               switch (*cp) {
+
+               case '@':
+                       if (value(TERSE))
+                               *cp = 0;
+                       else
+                               *cp = ' ';
+                       break;
+
+               case '|':
+                       if (value(TERSE) == 0)
+                               return (cp + 1);
+                       *cp = 0;
+                       break;
+               }
+       return (str);
+}
+
+/*VARARGS2*/
+merror(seekpt, i)
+#ifdef lint
+       char *seekpt;
+#else
+       int seekpt;
+#endif
+       int i;
+{
+       register char *cp = linebuf;
+
+       if (seekpt == 0)
+               return;
+       merror1(seekpt);
+       if (*cp == '\n')
+               putnl(), cp++;
+       if (inopen && CE)
+               vclreol();
+       if (SO && SE)
+               putpad(SO);
+       printf(mesg(cp), i);
+       if (SO && SE)
+               putpad(SE);
+}
+
+merror1(seekpt)
+#ifdef lint
+       char *seekpt;
+#else
+       int seekpt;
+#endif
+{
+
+       lseek(erfile, (long) seekpt, 0);
+       if (read(erfile, linebuf, 128) < 2)
+               CP(linebuf, "ERROR");
+}
+
+morelines()
+{
+
+       if ((int) sbrk(1024 * sizeof (line)) == -1)
+               return (-1);
+       endcore += 1024;
+       return (0);
+}
+
+nonzero()
+{
+
+       if (addr1 == zero) {
+               notempty();
+               error("Nonzero address required@on this command");
+       }
+}
+
+notable(i)
+       int i;
+{
+
+       return (hush == 0 && !inglobal && i > value(REPORT));
+}
+
+
+notempty()
+{
+
+       if (dol == zero)
+               error("No lines@in the buffer");
+}
+
+
+netchHAD(cnt)
+       int cnt;
+{
+
+       netchange(lineDOL() - cnt);
+}
+
+netchange(i)
+       register int i;
+{
+       register char *cp;
+
+       if (i > 0)
+               notesgn = cp = "more ";
+       else
+               notesgn = cp = "fewer ", i = -i;
+       if (inopen) {
+               notecnt = i;
+               notenam = "";
+               return;
+       }
+       if (!notable(i))
+               return;
+       printf(mesg("%d %slines@in file after %s"), i, cp, Command);
+       putNFL();
+}
+
+putmark(addr)
+       line *addr;
+{
+
+       putmk1(addr, putline());
+}
+
+putmk1(addr, n)
+       register line *addr;
+       int n;
+{
+       register line *markp;
+
+       *addr &= ~1;
+       for (markp = (anymarks ? names : &names['z'-'a'+1]); markp <= &names['z'-'a'+1]; markp++)
+               if (*markp == *addr)
+                       *markp = n;
+       *addr = n;
+}
+
+char *
+plural(i)
+       long i;
+{
+
+       return (i == 1 ? "" : "s");
+}
+
+int    qcount();
+short  vcntcol;
+
+qcolumn(lim, gp)
+       register char *lim, *gp;
+{
+       register int x;
+       int (*OO)();
+
+       OO = Outchar;
+       Outchar = qcount;
+       vcntcol = 0;
+       if (lim != NULL)
+               x = lim[1], lim[1] = 0;
+       pline(0);
+       if (lim != NULL)
+               lim[1] = x;
+       if (gp)
+               while (*gp)
+                       putchar(*gp++);
+       Outchar = OO;
+       return (vcntcol);
+}
+
+int
+qcount(c)
+       int c;
+{
+
+       if (c == '\t') {
+               vcntcol += value(TABSTOP) - vcntcol % value(TABSTOP);
+               return;
+       }
+       vcntcol++;
+}
+
+reverse(a1, a2)
+       register line *a1, *a2;
+{
+       register line t;
+
+       for (;;) {
+               t = *--a2;
+               if (a2 <= a1)
+                       return;
+               *a2 = *a1;
+               *a1++ = t;
+       }
+}
+
+save(a1, a2)
+       line *a1;
+       register line *a2;
+{
+       register int more;
+
+       undkind = UNDNONE;
+       undadot = dot;
+       more = (a2 - a1 + 1) - (unddol - dol);
+       while (more > (endcore - truedol))
+               if (morelines() < 0)
+                       error("Out of memory@saving lines for undo - try using ed or re");
+       if (more)
+               (*(more > 0 ? copywR : copyw))(unddol + more + 1, unddol + 1,
+                   (truedol - unddol));
+       unddol += more;
+       truedol += more;
+       copyw(dol + 1, a1, a2 - a1 + 1);
+       undkind = UNDALL;
+       unddel = a1 - 1;
+       undap1 = a1;
+       undap2 = a2 + 1;
+}
+
+save12()
+{
+
+       save(addr1, addr2);
+}
+
+saveall()
+{
+
+       save(one, dol);
+}
+
+span()
+{
+
+       return (addr2 - addr1 + 1);
+}
+
+sync()
+{
+
+       chng = 0;
+       tchng = 0;
+       xchng = 0;
+}
+
+
+skipwh()
+{
+       register int wh;
+
+       wh = 0;
+       while (iswhite(peekchar())) {
+               wh++;
+               ignchar();
+       }
+       return (wh);
+}
+
+/*VARARGS2*/
+smerror(seekpt, cp)
+#ifdef lint
+       char *seekpt;
+#else
+       int seekpt;
+#endif
+       char *cp;
+{
+
+       if (seekpt == 0)
+               return;
+       merror1(seekpt);
+       if (inopen && CE)
+               vclreol();
+       if (SO && SE)
+               putpad(SO);
+       lprintf(mesg(linebuf), cp);
+       if (SO && SE)
+               putpad(SE);
+}
+
+#define        std_nerrs (sizeof std_errlist / sizeof std_errlist[0])
+
+#define        error(i)        i
+
+#ifdef lint
+char   *std_errlist[] = {
+#else
+short  std_errlist[] = {
+#endif
+       error("Error 0"),
+       error("Not super-user"),
+       error("No such file or directory"),
+       error("No such process"),
+       error("Interrupted system call"),
+       error("Physical I/O error"),
+       error("No such device or address"),
+       error("Argument list too long"),
+       error("Exec format error"),
+       error("Bad file number"),
+       error("No children"),
+       error("No more processes"),
+       error("Not enough core"),
+       error("Permission denied"),
+       error("Bad address"),
+       error("Block device required"),
+       error("Mount device busy"),
+       error("File exists"),
+       error("Cross-device link"),
+       error("No such device"),
+       error("Not a directory"),
+       error("Is a directory"),
+       error("Invalid argument"),
+       error("File table overflow"),
+       error("Too many open files"),
+       error("Not a typewriter"),
+       error("Text file busy"),
+       error("File too large"),
+       error("No space left on device"),
+       error("Illegal seek"),
+       error("Read-only file system"),
+       error("Too many links"),
+       error("Broken pipe")
+#ifndef QUOTA
+       , error("Math argument")
+       , error("Result too large")
+#else
+       , error("Quota exceeded")
+#endif
+};
+
+#undef error
+
+char *
+strend(cp)
+       register char *cp;
+{
+
+       while (*cp)
+               cp++;
+       return (cp);
+}
+
+strcLIN(dp)
+       char *dp;
+{
+
+       CP(linebuf, dp);
+}
+
+syserror()
+{
+       register int e = errno;
+
+       dirtcnt = 0;
+       putchar(' ');
+       if (e >= 0 && errno <= std_nerrs)
+               error(std_errlist[e]);
+       else
+               error("System error %d", e);
+}
+
+char *
+vfindcol(i)
+       int i;
+{
+       register char *cp;
+       register int (*OO)() = Outchar;
+
+       Outchar = qcount;
+       ignore(qcolumn(linebuf - 1, NOSTR));
+       for (cp = linebuf; *cp && vcntcol < i; cp++)
+               putchar(*cp);
+       if (cp != linebuf)
+               cp--;
+       Outchar = OO;
+       return (cp);
+}
+
+char *
+vskipwh(cp)
+       register char *cp;
+{
+
+       while (iswhite(*cp) && cp[1])
+               cp++;
+       return (cp);
+}
+
+
+char *
+vpastwh(cp)
+       register char *cp;
+{
+
+       while (iswhite(*cp))
+               cp++;
+       return (cp);
+}
+
+whitecnt(cp)
+       register char *cp;
+{
+       register int i;
+
+       i = 0;
+       for (;;)
+               switch (*cp++) {
+
+               case '\t':
+                       i += value(TABSTOP) - i % value(TABSTOP);
+                       break;
+
+               case ' ':
+                       i++;
+                       break;
+
+               default:
+                       return (i);
+               }
+}
+
+#ifdef lint
+Ignore(a)
+       char *a;
+{
+
+       a = a;
+}
+
+Ignorf(a)
+       int (*a)();
+{
+
+       a = a;
+}
+#endif
+
+markit(addr)
+       line *addr;
+{
+
+       if (addr != dot && addr >= one && addr <= dol)
+               markDOT();
+}
+