Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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);
        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("/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);
                        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) {
                        tl += BLKSIZE/sizeof(Rune);
                        bp = getblock(tl, 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));
        /* &~3 so the ptr is aligned to 4 (?) */
        off = (atl*sizeof(Rune)) & (BLKSIZE-1) & ~3;
        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[LBSIZE/sizeof(Rune)])
                                error(Q);
                } else
                if(c == '\n' && (!globp || !globp[0])) {
                        peekc = c;
                        pflag++;
                        break;
                } else
                if(c == seof)
                        break;
                *p++ = c;
                if(p >= &rhsbuf[LBSIZE/sizeof(Rune)])
                        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);
}