/*
* Editor
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <regexp.h>

enum
{
       FNSIZE  = 128,          /* file name */
       LBSIZE  = 4096,         /* max line size */
       BLKSIZE = 4096,         /* block size in temp file */
       NBLK    = 8191,         /* max size of temp file */
       ESIZE   = 256,          /* max size of reg exp */
       GBSIZE  = 256,          /* max size of global command */
       MAXSUB  = 9,            /* max number of sub reg exp */
       ESCFLG  = Runemax,      /* escape Rune - user defined code */
       EOF     = -1,
};

void    (*oldhup)(int);
void    (*oldquit)(int);
int*    addr1;
int*    addr2;
int     anymarks;
Biobuf  bcons;
int     col;
long    count;
int*    dol;
int*    dot;
int     fchange;
char    file[FNSIZE];
Rune    genbuf[LBSIZE];
int     given;
Rune*   globp;
int     iblock;
int     ichanged;
int     io;
Biobuf  iobuf;
int     lastc;
char    line[70];
Rune*   linebp;
Rune    linebuf[LBSIZE];
int     listf;
int     listn;
Rune*   loc1;
Rune*   loc2;
int     names[26];
int     nleft;
int     oblock;
int     oflag;
Reprog  *pattern;
int     peekc;
int     pflag;
int     rescuing;
Rune    rhsbuf[LBSIZE/sizeof(Rune)];
char    savedfile[FNSIZE];
jmp_buf savej;
int     subnewa;
int     subolda;
Resub   subexp[MAXSUB];
char*   tfname;
int     tline;
int     waiting;
int     wrapp;
int*    zero;

char    Q[]     = "";
char    T[]     = "TMP";
char    WRERR[] = "WRITE ERROR";
int     bpagesize = 20;
char    hex[]   = "0123456789abcdef";
char*   linp    = line;
ulong   nlall = 128;
int     tfile   = -1;
int     vflag   = 1;

void    add(int);
int*    address(void);
int     append(int(*)(void), int*);
void    browse(void);
void    callunix(void);
void    commands(void);
void    compile(int);
int     compsub(void);
void    dosub(void);
void    error(char*);
int     match(int*);
void    exfile(int);
void    filename(int);
Rune*   getblock(int, int);
int     getchr(void);
int     getcopy(void);
int     getfile(void);
Rune*   getline(int);
int     getnum(void);
int     getsub(void);
int     gettty(void);
void    global(int);
void    init(void);
void    join(void);
void    move(int);
void    newline(void);
void    nonzero(void);
void    notifyf(void*, char*);
Rune*   place(Rune*, Rune*, Rune*);
void    printcom(void);
void    putchr(int);
void    putd(void);
void    putfile(void);
int     putline(void);
void    putshst(Rune*);
void    putst(char*);
void    quit(void);
void    rdelete(int*, int*);
void    regerror(char *);
void    reverse(int*, int*);
void    setnoaddr(void);
void    setwide(void);
void    squeeze(int);
void    substitute(int);

void
main(int argc, char *argv[])
{
       char *p1, *p2;

       Binit(&bcons, 0, OREAD);
       Blethal(&bcons, nil);
       notify(notifyf);
       ARGBEGIN {
       case 'o':
               oflag = 1;
               vflag = 0;
               break;
       } ARGEND

       USED(argc);
       if(*argv && (strcmp(*argv, "-") == 0)) {
               argv++;
               vflag = 0;
       }
       if(oflag) {
               p1 = "/fd/1";
               p2 = savedfile;
               while(*p2++ = *p1++)
                       ;
               globp = L"a";
       } else
       if(*argv) {
               p1 = *argv;
               p2 = savedfile;
               while(*p2++ = *p1++)
                       if(p2 >= &savedfile[sizeof(savedfile)])
                               p2--;
               globp = L"r";
       }
       zero = malloc((nlall+5)*sizeof(int*));
       tfname = mktemp(strdup("/tmp/eXXXXX"));
       init();
       setjmp(savej);
       commands();
       quit();
}

void
commands(void)
{
       int *a1, c, temp;
       char lastsep;
       Dir *d;

       for(;;) {
               if(pflag) {
                       pflag = 0;
                       addr1 = addr2 = dot;
                       printcom();
               }
               c = '\n';
               for(addr1 = 0;;) {
                       lastsep = c;
                       a1 = address();
                       c = getchr();
                       if(c != ',' && c != ';')
                               break;
                       if(lastsep == ',')
                               error(Q);
                       if(a1 == 0) {
                               a1 = zero+1;
                               if(a1 > dol)
                                       a1--;
                       }
                       addr1 = a1;
                       if(c == ';')
                               dot = a1;
               }
               if(lastsep != '\n' && a1 == 0)
                       a1 = dol;
               if((addr2=a1) == 0) {
                       given = 0;
                       addr2 = dot;
               } else
                       given = 1;
               if(addr1 == 0)
                       addr1 = addr2;
               switch(c) {

               case 'a':
                       add(0);
                       continue;

               case 'b':
                       nonzero();
                       browse();
                       continue;

               case 'c':
                       nonzero();
                       newline();
                       rdelete(addr1, addr2);
                       append(gettty, addr1-1);
                       continue;

               case 'd':
                       nonzero();
                       newline();
                       rdelete(addr1, addr2);
                       continue;

               case 'E':
                       fchange = 0;
                       c = 'e';
               case 'e':
                       setnoaddr();
                       if(vflag && fchange) {
                               fchange = 0;
                               error(Q);
                       }
                       filename(c);
                       init();
                       addr2 = zero;
                       goto caseread;

               case 'f':
                       setnoaddr();
                       filename(c);
                       putst(savedfile);
                       continue;

               case 'g':
                       global(1);
                       continue;

               case 'i':
                       add(-1);
                       continue;


               case 'j':
                       if(!given)
                               addr2++;
                       newline();
                       join();
                       continue;

               case 'k':
                       nonzero();
                       c = getchr();
                       if(c < 'a' || c > 'z')
                               error(Q);
                       newline();
                       names[c-'a'] = *addr2 & ~01;
                       anymarks |= 01;
                       continue;

               case 'm':
                       move(0);
                       continue;

               case 'n':
                       listn++;
                       newline();
                       printcom();
                       continue;

               case '\n':
                       if(a1==0) {
                               a1 = dot+1;
                               addr2 = a1;
                               addr1 = a1;
                       }
                       if(lastsep==';')
                               addr1 = a1;
                       printcom();
                       continue;

               case 'l':
                       listf++;
               case 'p':
               case 'P':
                       newline();
                       printcom();
                       continue;

               case 'Q':
                       fchange = 0;
               case 'q':
                       setnoaddr();
                       newline();
                       quit();

               case 'r':
                       filename(c);
               caseread:
                       if((io=open(file, OREAD)) < 0) {
                               lastc = '\n';
                               error(file);
                       }
                       if((d = dirfstat(io)) != nil){
                               if(d->mode & DMAPPEND)
                                       print("warning: %s is append only\n", file);
                               free(d);
                       }
                       Binit(&iobuf, io, OREAD);
                       Blethal(&iobuf, nil);
                       setwide();
                       squeeze(0);
                       c = zero != dol;
                       append(getfile, addr2);
                       exfile(OREAD);

                       fchange = c;
                       continue;

               case 's':
                       nonzero();
                       substitute(globp != 0);
                       continue;

               case 't':
                       move(1);
                       continue;

               case 'u':
                       nonzero();
                       newline();
                       if((*addr2&~01) != subnewa)
                               error(Q);
                       *addr2 = subolda;
                       dot = addr2;
                       continue;

               case 'v':
                       global(0);
                       continue;

               case 'W':
                       wrapp++;
               case 'w':
                       setwide();
                       squeeze(dol>zero);
                       temp = getchr();
                       if(temp != 'q' && temp != 'Q') {
                               peekc = temp;
                               temp = 0;
                       }
                       filename(c);
                       if(!wrapp ||
                         ((io = open(file, OWRITE)) == -1) ||
                         ((seek(io, 0L, 2)) == -1))
                               if((io = create(file, OWRITE, 0666)) < 0)
                                       error(file);
                       Binit(&iobuf, io, OWRITE);
                       wrapp = 0;
                       if(dol > zero)
                               putfile();
                       exfile(OWRITE);
                       if(addr1<=zero+1 && addr2==dol)
                               fchange = 0;
                       if(temp == 'Q')
                               fchange = 0;
                       if(temp)
                               quit();
                       continue;

               case '=':
                       setwide();
                       squeeze(0);
                       newline();
                       count = addr2 - zero;
                       putd();
                       putchr(L'\n');
                       continue;

               case '!':
                       callunix();
                       continue;

               case EOF:
                       return;

               }
               error(Q);
       }
}

void
printcom(void)
{
       int *a1;

       nonzero();
       a1 = addr1;
       do {
               if(listn) {
                       count = a1-zero;
                       putd();
                       putchr(L'\t');
               }
               putshst(getline(*a1++));
       } while(a1 <= addr2);
       dot = addr2;
       listf = 0;
       listn = 0;
       pflag = 0;
}

int*
address(void)
{
       int sign, *a, opcnt, nextopand, *b, c;

       nextopand = -1;
       sign = 1;
       opcnt = 0;
       a = dot;
       do {
               do {
                       c = getchr();
               } while(c == ' ' || c == '\t');
               if(c >= '0' && c <= '9') {
                       peekc = c;
                       if(!opcnt)
                               a = zero;
                       a += sign*getnum();
               } else
               switch(c) {
               case '$':
                       a = dol;
               case '.':
                       if(opcnt)
                               error(Q);
                       break;
               case '\'':
                       c = getchr();
                       if(opcnt || c < 'a' || c > 'z')
                               error(Q);
                       a = zero;
                       do {
                               a++;
                       } while(a <= dol && names[c-'a'] != (*a & ~01));
                       break;
               case '?':
                       sign = -sign;
               case '/':
                       compile(c);
                       b = a;
                       for(;;) {
                               a += sign;
                               if(a <= zero)
                                       a = dol;
                               if(a > dol)
                                       a = zero;
                               if(match(a))
                                       break;
                               if(a == b)
                                       error(Q);
                       }
                       break;
               default:
                       if(nextopand == opcnt) {
                               a += sign;
                               if(a < zero || dol < a)
                                       continue;       /* error(Q); */
                       }
                       if(c != '+' && c != '-' && c != '^') {
                               peekc = c;
                               if(opcnt == 0)
                                       a = 0;
                               return a;
                       }
                       sign = 1;
                       if(c != '+')
                               sign = -sign;
                       nextopand = ++opcnt;
                       continue;
               }
               sign = 1;
               opcnt++;
       } while(zero <= a && a <= dol);
       error(Q);
       return 0;
}

int
getnum(void)
{
       int r, c;

       r = 0;
       for(;;) {
               c = getchr();
               if(c < '0' || c > '9')
                       break;
               r = r*10 + (c-'0');
       }
       peekc = c;
       return r;
}

void
setwide(void)
{
       if(!given) {
               addr1 = zero + (dol>zero);
               addr2 = dol;
       }
}

void
setnoaddr(void)
{
       if(given)
               error(Q);
}

void
nonzero(void)
{
       squeeze(1);
}

void
squeeze(int i)
{
       if(addr1 < zero+i || addr2 > dol || addr1 > addr2)
               error(Q);
}

void
newline(void)
{
       int c;

       c = getchr();
       if(c == '\n' || c == EOF)
               return;
       if(c == 'p' || c == 'l' || c == 'n') {
               pflag++;
               if(c == 'l')
                       listf++;
               else
               if(c == 'n')
                       listn++;
               c = getchr();
               if(c == '\n')
                       return;
       }
       error(Q);
}

void
filename(int comm)
{
       char *p1, *p2;
       Rune rune;
       int c;

       count = 0;
       c = getchr();
       if(c == '\n' || c == EOF) {
               p1 = savedfile;
               if(*p1 == 0 && comm != 'f')
                       error(Q);
               p2 = file;
               while(*p2++ = *p1++)
                       ;
               return;
       }
       if(c != ' ')
               error(Q);
       while((c=getchr()) == ' ')
               ;
       if(c == '\n')
               error(Q);
       p1 = file;
       do {
               if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF)
                       error(Q);
               rune = c;
               p1 += runetochar(p1, &rune);
       } while((c=getchr()) != '\n');
       *p1 = 0;
       if(savedfile[0] == 0 || comm == 'e' || comm == 'f') {
               p1 = savedfile;
               p2 = file;
               while(*p1++ = *p2++)
                       ;
       }
}

void
exfile(int om)
{

       if(om == OWRITE)
               if(Bflush(&iobuf) < 0)
                       error(Q);
       close(io);
       io = -1;
       if(vflag) {
               putd();
               putchr(L'\n');
       }
}

void
error1(char *s)
{
       int c;

       wrapp = 0;
       listf = 0;
       listn = 0;
       count = 0;
       seek(0, 0, 2);
       pflag = 0;
       if(globp)
               lastc = '\n';
       globp = 0;
       peekc = lastc;
       if(lastc)
               for(;;) {
                       c = getchr();
                       if(c == '\n' || c == EOF)
                               break;
               }
       if(io > 0) {
               close(io);
               io = -1;
       }
       putchr(L'?');
       putst(s);
}

void
error(char *s)
{
       error1(s);
       longjmp(savej, 1);
}

void
rescue(void)
{
       rescuing = 1;
       if(dol > zero) {
               addr1 = zero+1;
               addr2 = dol;
               io = create("ed.hup", OWRITE, 0666);
               if(io > 0){
                       Binit(&iobuf, io, OWRITE);
                       putfile();
               }
       }
       fchange = 0;
       quit();
}

void
notifyf(void *a, char *s)
{
       if(strcmp(s, "interrupt") == 0){
               if(rescuing || waiting)
                       noted(NCONT);
               putchr(L'\n');
               lastc = '\n';
               error1(Q);
               notejmp(a, savej, 0);
       }
       if(strcmp(s, "hangup") == 0){
               if(rescuing)
                       noted(NDFLT);
               rescue();
       }
       fprint(2, "ed: note: %s\n", s);
       abort();
}

int
getchr(void)
{
       if(lastc = peekc) {
               peekc = 0;
               return lastc;
       }
       if(globp) {
               if((lastc=*globp++) != 0)
                       return lastc;
               globp = 0;
               return EOF;
       }
       lastc = Bgetrune(&bcons);
       return lastc;
}

int
gety(void)
{
       int c;
       Rune *gf, *p;

       p = linebuf;
       gf = globp;
       for(;;) {
               c = getchr();
               if(c == '\n') {
                       *p = 0;
                       return 0;
               }
               if(c == EOF) {
                       if(gf)
                               peekc = c;
                       return c;
               }
               if(c == 0)
                       continue;
               *p++ = c;
               if(p >= &linebuf[LBSIZE-sizeof(Rune)])
                       error(Q);
       }
}

int
gettty(void)
{
       int rc;

       rc = gety();
       if(rc)
               return rc;
       if(linebuf[0] == '.' && linebuf[1] == 0)
               return EOF;
       return 0;
}

int
getfile(void)
{
       int c;
       Rune *lp;

       lp = linebuf;
       do {
               c = Bgetrune(&iobuf);
               if(c < 0) {
                       if(lp > linebuf) {
                               putst("'\\n' appended");
                               c = '\n';
                       } else
                               return EOF;
               }
               if(lp >= &linebuf[LBSIZE]) {
                       lastc = '\n';
                       error(Q);
               }
               *lp++ = c;
               count++;
       } while(c != '\n');
       lp[-1] = 0;
       return 0;
}

void
putfile(void)
{
       int *a1;
       Rune *lp;
       long c;

       a1 = addr1;
       do {
               lp = getline(*a1++);
               for(;;) {
                       count++;
                       c = *lp++;
                       if(c == 0) {
                               if(Bputrune(&iobuf, '\n') < 0)
                                       error(Q);
                               break;
                       }
                       if(Bputrune(&iobuf, c) < 0)
                               error(Q);
               }
       } while(a1 <= addr2);
       if(Bflush(&iobuf) < 0)
               error(Q);
}

int
append(int (*f)(void), int *a)
{
       int *a1, *a2, *rdot, nline, tl;

       nline = 0;
       dot = a;
       while((*f)() == 0) {
               if((dol-zero) >= nlall) {
                       nlall += 512;
                       a1 = realloc(zero, (nlall+5)*sizeof(int*));
                       if(a1 == 0) {
                               error("MEM?");
                               rescue();
                       }
                       tl = a1 - zero; /* relocate pointers */
                       zero += tl;
                       addr1 += tl;
                       addr2 += tl;
                       dol += tl;
                       dot += tl;
               }
               tl = putline();
               nline++;
               a1 = ++dol;
               a2 = a1+1;
               rdot = ++dot;
               while(a1 > rdot)
                       *--a2 = *--a1;
               *rdot = tl;
       }
       return nline;
}

void
add(int i)
{
       if(i && (given || dol > zero)) {
               addr1--;
               addr2--;
       }
       squeeze(0);
       newline();
       append(gettty, addr2);
}

void
browse(void)
{
       int forward, n;
       static int bformat, bnum; /* 0 */

       forward = 1;
       peekc = getchr();
       if(peekc != '\n'){
               if(peekc == '-' || peekc == '+') {
                       if(peekc == '-')
                               forward = 0;
                       getchr();
               }
               n = getnum();
               if(n > 0)
                       bpagesize = n;
       }
       newline();
       if(pflag) {
               bformat = listf;
               bnum = listn;
       } else {
               listf = bformat;
               listn = bnum;
       }
       if(forward) {
               addr1 = addr2;
               addr2 += bpagesize;
               if(addr2 > dol)
                       addr2 = dol;
       } else {
               addr1 = addr2-bpagesize;
               if(addr1 <= zero)
                       addr1 = zero+1;
       }
       printcom();
}

void
callunix(void)
{
       int c, pid;
       Rune rune;
       char buf[512];
       char *p;

       setnoaddr();
       p = buf;
       while((c=getchr()) != EOF && c != '\n')
               if(p < &buf[sizeof(buf) - 6]) {
                       rune = c;
                       p += runetochar(p, &rune);
               }
       *p = 0;
       pid = fork();
       if(pid == 0) {
               execl("/bin/rc", "rc", "-c", buf, nil);
               exits("execl failed");
       }
       waiting = 1;
       while(waitpid() != pid)
               ;
       waiting = 0;
       if(vflag)
               putst("!");
}

void
quit(void)
{
       if(vflag && fchange && dol!=zero) {
               fchange = 0;
               error(Q);
       }
       remove(tfname);
       exits(0);
}

void
onquit(int sig)
{
       USED(sig);
       quit();
}

void
rdelete(int *ad1, int *ad2)
{
       int *a1, *a2, *a3;

       a1 = ad1;
       a2 = ad2+1;
       a3 = dol;
       dol -= a2 - a1;
       do {
               *a1++ = *a2++;
       } while(a2 <= a3);
       a1 = ad1;
       if(a1 > dol)
               a1 = dol;
       dot = a1;
       fchange = 1;
}

void
gdelete(void)
{
       int *a1, *a2, *a3;

       a3 = dol;
       for(a1=zero; (*a1&01)==0; a1++)
               if(a1>=a3)
                       return;
       for(a2=a1+1; a2<=a3;) {
               if(*a2 & 01) {
                       a2++;
                       dot = a1;
               } else
                       *a1++ = *a2++;
       }
       dol = a1-1;
       if(dot > dol)
               dot = dol;
       fchange = 1;
}

Rune*
getline(int tl)
{
       Rune *lp, *bp;
       int nl;

       lp = linebuf;
       bp = getblock(tl, OREAD);
       nl = nleft;
       tl &= ~((BLKSIZE/sizeof(Rune)) - 1);
       while(*lp++ = *bp++) {
               nl -= sizeof(Rune);
               if(nl == 0) {
                       bp = getblock(tl += BLKSIZE/sizeof(Rune), OREAD);
                       nl = nleft;
               }
       }
       return linebuf;
}

int
putline(void)
{
       Rune *lp, *bp;
       int nl, tl;

       fchange = 1;
       lp = linebuf;
       tl = tline;
       bp = getblock(tl, OWRITE);
       nl = nleft;
       tl &= ~((BLKSIZE/sizeof(Rune))-1);
       while(*bp = *lp++) {
               if(*bp++ == '\n') {
                       bp[-1] = 0;
                       linebp = lp;
                       break;
               }
               nl -= sizeof(Rune);
               if(nl == 0) {
                       tl += BLKSIZE/sizeof(Rune);
                       bp = getblock(tl, OWRITE);
                       nl = nleft;
               }
       }
       nl = tline;
       tline += ((lp-linebuf) + 03) & 077776;
       return nl;
}

void
blkio(int b, uchar *buf, long (*iofcn)(int, void *, long))
{
       seek(tfile, b*BLKSIZE, 0);
       if((*iofcn)(tfile, buf, BLKSIZE) != BLKSIZE) {
               error(T);
       }
}

Rune*
getblock(int atl, int iof)
{
       int bno, off;

       static uchar ibuff[BLKSIZE];
       static uchar obuff[BLKSIZE];

       bno = atl / (BLKSIZE/sizeof(Rune));
       off = (atl*sizeof(Rune)) & (BLKSIZE-1) & ~03;
       if(bno >= NBLK) {
               lastc = '\n';
               error(T);
       }
       nleft = BLKSIZE - off;
       if(bno == iblock) {
               ichanged |= iof;
               return (Rune*)(ibuff+off);
       }
       if(bno == oblock)
               return (Rune*)(obuff+off);
       if(iof == OREAD) {
               if(ichanged)
                       blkio(iblock, ibuff, write);
               ichanged = 0;
               iblock = bno;
               blkio(bno, ibuff, read);
               return (Rune*)(ibuff+off);
       }
       if(oblock >= 0)
               blkio(oblock, obuff, write);
       oblock = bno;
       return (Rune*)(obuff+off);
}

void
init(void)
{
       int *markp;

       close(tfile);
       tline = 2;
       for(markp = names; markp < &names[26]; )
               *markp++ = 0;
       subnewa = 0;
       anymarks = 0;
       iblock = -1;
       oblock = -1;
       ichanged = 0;
       if((tfile = create(tfname, ORDWR, 0600)) < 0){
               error1(T);
               exits(0);
       }
       dot = dol = zero;
}

void
global(int k)
{
       Rune *gp, globuf[GBSIZE];
       int c, *a1;

       if(globp)
               error(Q);
       setwide();
       squeeze(dol > zero);
       c = getchr();
       if(c == '\n')
               error(Q);
       compile(c);
       gp = globuf;
       while((c=getchr()) != '\n') {
               if(c == EOF)
                       error(Q);
               if(c == '\\') {
                       c = getchr();
                       if(c != '\n')
                               *gp++ = '\\';
               }
               *gp++ = c;
               if(gp >= &globuf[GBSIZE-2])
                       error(Q);
       }
       if(gp == globuf)
               *gp++ = 'p';
       *gp++ = '\n';
       *gp = 0;
       for(a1=zero; a1<=dol; a1++) {
               *a1 &= ~01;
               if(a1 >= addr1 && a1 <= addr2 && match(a1) == k)
                       *a1 |= 01;
       }

       /*
        * Special case: g/.../d (avoid n^2 algorithm)
        */
       if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) {
               gdelete();
               return;
       }
       for(a1=zero; a1<=dol; a1++) {
               if(*a1 & 01) {
                       *a1 &= ~01;
                       dot = a1;
                       globp = globuf;
                       commands();
                       a1 = zero;
               }
       }
}

void
join(void)
{
       Rune *gp, *lp;
       int *a1;

       nonzero();
       gp = genbuf;
       for(a1=addr1; a1<=addr2; a1++) {
               lp = getline(*a1);
               while(*gp = *lp++)
                       if(gp++ >= &genbuf[LBSIZE-sizeof(Rune)])
                               error(Q);
       }
       lp = linebuf;
       gp = genbuf;
       while(*lp++ = *gp++)
               ;
       *addr1 = putline();
       if(addr1 < addr2)
               rdelete(addr1+1, addr2);
       dot = addr1;
}

void
substitute(int inglob)
{
       int *mp, *a1, nl, gsubf, n;

       n = getnum();   /* OK even if n==0 */
       gsubf = compsub();
       for(a1 = addr1; a1 <= addr2; a1++) {
               if(match(a1)){
                       int *ozero;
                       int m = n;

                       do {
                               int span = loc2-loc1;

                               if(--m <= 0) {
                                       dosub();
                                       if(!gsubf)
                                               break;
                                       if(span == 0) { /* null RE match */
                                               if(*loc2 == 0)
                                                       break;
                                               loc2++;
                                       }
                               }
                       } while(match(0));
                       if(m <= 0) {
                               inglob |= 01;
                               subnewa = putline();
                               *a1 &= ~01;
                               if(anymarks) {
                                       for(mp=names; mp<&names[26]; mp++)
                                               if(*mp == *a1)
                                                       *mp = subnewa;
                               }
                               subolda = *a1;
                               *a1 = subnewa;
                               ozero = zero;
                               nl = append(getsub, a1);
                               addr2 += nl;
                               nl += zero-ozero;
                               a1 += nl;
                       }
               }
       }
       if(inglob == 0)
               error(Q);
}

int
compsub(void)
{
       int seof, c;
       Rune *p;

       seof = getchr();
       if(seof == '\n' || seof == ' ')
               error(Q);
       compile(seof);
       p = rhsbuf;
       for(;;) {
               c = getchr();
               if(c == '\\') {
                       c = getchr();
                       *p++ = ESCFLG;
                       if(p >= &rhsbuf[nelem(rhsbuf)])
                               error(Q);
               } else
               if(c == '\n' && (!globp || !globp[0])) {
                       peekc = c;
                       pflag++;
                       break;
               } else
               if(c == seof)
                       break;
               *p++ = c;
               if(p >= &rhsbuf[nelem(rhsbuf)])
                       error(Q);
       }
       *p = 0;
       peekc = getchr();
       if(peekc == 'g') {
               peekc = 0;
               newline();
               return 1;
       }
       newline();
       return 0;
}

int
getsub(void)
{
       Rune *p1, *p2;

       p1 = linebuf;
       if((p2 = linebp) == 0)
               return EOF;
       while(*p1++ = *p2++)
               ;
       linebp = 0;
       return 0;
}

void
dosub(void)
{
       Rune *lp, *sp, *rp;
       int c, n;

       lp = linebuf;
       sp = genbuf;
       rp = rhsbuf;
       while(lp < loc1)
               *sp++ = *lp++;
       while(c = *rp++) {
               if(c == '&'){
                       sp = place(sp, loc1, loc2);
                       continue;
               }
               if(c == ESCFLG && (c = *rp++) >= '1' && c < MAXSUB+'0') {
                       n = c-'0';
                       if(subexp[n].rsp && subexp[n].rep) {
                               sp = place(sp, subexp[n].rsp, subexp[n].rep);
                               continue;
                       }
                       error(Q);
               }
               *sp++ = c;
               if(sp >= &genbuf[LBSIZE])
                       error(Q);
       }
       lp = loc2;
       loc2 = sp - genbuf + linebuf;
       while(*sp++ = *lp++)
               if(sp >= &genbuf[LBSIZE])
                       error(Q);
       lp = linebuf;
       sp = genbuf;
       while(*lp++ = *sp++)
               ;
}

Rune*
place(Rune *sp, Rune *l1, Rune *l2)
{

       while(l1 < l2) {
               *sp++ = *l1++;
               if(sp >= &genbuf[LBSIZE])
                       error(Q);
       }
       return sp;
}

void
move(int cflag)
{
       int *adt, *ad1, *ad2;

       nonzero();
       if((adt = address())==0)        /* address() guarantees addr is in range */
               error(Q);
       newline();
       if(cflag) {
               int *ozero, delta;
               ad1 = dol;
               ozero = zero;
               append(getcopy, ad1++);
               ad2 = dol;
               delta = zero - ozero;
               ad1 += delta;
               adt += delta;
       } else {
               ad2 = addr2;
               for(ad1 = addr1; ad1 <= ad2;)
                       *ad1++ &= ~01;
               ad1 = addr1;
       }
       ad2++;
       if(adt<ad1) {
               dot = adt + (ad2-ad1);
               if((++adt)==ad1)
                       return;
               reverse(adt, ad1);
               reverse(ad1, ad2);
               reverse(adt, ad2);
       } else
       if(adt >= ad2) {
               dot = adt++;
               reverse(ad1, ad2);
               reverse(ad2, adt);
               reverse(ad1, adt);
       } else
               error(Q);
       fchange = 1;
}

void
reverse(int *a1, int *a2)
{
       int t;

       for(;;) {
               t = *--a2;
               if(a2 <= a1)
                       return;
               *a2 = *a1;
               *a1++ = t;
       }
}

int
getcopy(void)
{
       if(addr1 > addr2)
               return EOF;
       getline(*addr1++);
       return 0;
}

void
compile(int eof)
{
       Rune c;
       char *ep;
       char expbuf[ESIZE];

       if((c = getchr()) == '\n') {
               peekc = c;
               c = eof;
       }
       if(c == eof) {
               if(!pattern)
                       error(Q);
               return;
       }
       if(pattern) {
               free(pattern);
               pattern = 0;
       }
       ep = expbuf;
       do {
               if(c == '\\') {
                       if(ep >= expbuf+sizeof(expbuf)) {
                               error(Q);
                               return;
                       }
                       ep += runetochar(ep, &c);
                       if((c = getchr()) == '\n') {
                               error(Q);
                               return;
                       }
               }
               if(ep >= expbuf+sizeof(expbuf)) {
                       error(Q);
                       return;
               }
               ep += runetochar(ep, &c);
       } while((c = getchr()) != eof && c != '\n');
       if(c == '\n')
               peekc = c;
       *ep = 0;
       pattern = regcomp(expbuf);
}

int
match(int *addr)
{
       if(!pattern)
               return 0;
       if(addr){
               if(addr == zero)
                       return 0;
               subexp[0].rsp = getline(*addr);
       } else
               subexp[0].rsp = loc2;
       subexp[0].rep = 0;
       if(rregexec(pattern, linebuf, subexp, MAXSUB)) {
               loc1 = subexp[0].rsp;
               loc2 = subexp[0].rep;
               return 1;
       }
       loc1 = loc2 = 0;
       return 0;

}

void
putd(void)
{
       int r;

       r = count%10;
       count /= 10;
       if(count)
               putd();
       putchr(r + L'0');
}

void
putst(char *sp)
{
       Rune r;

       col = 0;
       for(;;) {
               sp += chartorune(&r, sp);
               if(r == 0)
                       break;
               putchr(r);
       }
       putchr(L'\n');
}

void
putshst(Rune *sp)
{
       col = 0;
       while(*sp)
               putchr(*sp++);
       putchr(L'\n');
}

void
putchr(int ac)
{
       char *lp;
       int c;
       Rune rune;

       lp = linp;
       c = ac;
       if(listf) {
               if(c == '\n') {
                       if(linp != line && linp[-1] == ' ') {
                               *lp++ = '\\';
                               *lp++ = 'n';
                       }
               } else {
                       if(col > (72-6-2)) {
                               col = 8;
                               *lp++ = '\\';
                               *lp++ = '\n';
                               *lp++ = '\t';
                       }
                       col++;
                       if(c=='\b' || c=='\t' || c=='\\') {
                               *lp++ = '\\';
                               if(c == '\b')
                                       c = 'b';
                               else
                               if(c == '\t')
                                       c = 't';
                               col++;
                       } else
                       if(c<' ' || c>='\177') {
                               *lp++ = '\\';
                               *lp++ = 'x';
                               *lp++ =  hex[c>>12];
                               *lp++ =  hex[c>>8&0xF];
                               *lp++ =  hex[c>>4&0xF];
                               c     =  hex[c&0xF];
                               col += 5;
                       }
               }
       }

       rune = c;
       lp += runetochar(lp, &rune);

       if(c == '\n' || lp >= &line[sizeof(line)-5]) {
               linp = line;
               write(oflag? 2: 1, line, lp-line);
               return;
       }
       linp = lp;
}

char*
mktemp(char *as)
{
       char *s;
       unsigned pid;
       int i;

       pid = getpid();
       s = as;
       while(*s++)
               ;
       s--;
       while(*--s == 'X') {
               *s = pid % 10 + '0';
               pid /= 10;
       }
       s++;
       i = 'a';
       while(access(as, 0) != -1) {
               if(i == 'z')
                       return "/";
               *s = i++;
       }
       return as;
}

void
regerror(char *s)
{
       USED(s);
       error(Q);
}