| /* |
| * Editor |
| */ |
| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <regexp.h> |
| |
| #undef EOF /* stdio? */ |
| |
| 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 = 0xFFFF, /* escape Rune - user defined code */ |
| EOF = -1 |
| }; |
| |
| void (*oldhup)(int); |
| void (*oldquit)(int); |
| int* addr1; |
| int* addr2; |
| int anymarks; |
| 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); |
| |
| Rune La[] = { 'a', 0 }; |
| Rune Lr[] = { 'r', 0 }; |
| |
| char tmp[] = "/var/tmp/eXXXXX"; |
| |
| void |
| main(int argc, char *argv[]) |
| { |
| char *p1, *p2; |
| |
| notify(notifyf); |
| ARGBEGIN { |
| case 'o': |
| oflag = 1; |
| vflag = 0; |
| break; |
| } ARGEND |
| |
| USED(argc); |
| if(*argv && (strcmp(*argv, "-") == 0)) { |
| argv++; |
| vflag = 0; |
| } |
| if(oflag) { |
| p1 = "/dev/stdout"; |
| p2 = savedfile; |
| while(*p2++ = *p1++) |
| ; |
| globp = La; |
| } else |
| if(*argv) { |
| p1 = *argv; |
| p2 = savedfile; |
| while(*p2++ = *p1++) |
| if(p2 >= &savedfile[sizeof(savedfile)]) |
| p2--; |
| globp = Lr; |
| } |
| zero = malloc((nlall+5)*sizeof(int*)); |
| tfname = mktemp(tmp); |
| 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('\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('\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('\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('?'); |
| 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('\n'); |
| lastc = '\n'; |
| error1(Q); |
| notejmp(a, savej, 0); |
| } |
| if(strcmp(s, "hangup") == 0 || strcmp(s, "kill") == 0){ |
| if(rescuing) |
| noted(NDFLT); |
| rescue(); |
| } |
| if(strstr(s, "child")) |
| noted(NCONT); |
| fprint(2, "ed: note: %s\n", s); |
| abort(); |
| } |
| |
| int |
| getchr(void) |
| { |
| char s[UTFmax]; |
| int i; |
| Rune r; |
| |
| if(lastc = peekc) { |
| peekc = 0; |
| return lastc; |
| } |
| if(globp) { |
| if((lastc=*globp++) != 0) |
| return lastc; |
| globp = 0; |
| return EOF; |
| } |
| for(i=0;;) { |
| if(read(0, s+i, 1) <= 0) |
| return lastc = EOF; |
| i++; |
| if(fullrune(s, i)) |
| break; |
| |
| } |
| chartorune(&r, s); |
| lastc = r; |
| 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-2]) |
| 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, d; |
| |
| nline = 0; |
| dot = a; |
| while((*f)() == 0) { |
| if((dol-zero) >= nlall) { |
| nlall += 512; |
| a1 = realloc(zero, (nlall+50)*sizeof(int*)); |
| if(a1 == 0) { |
| error("MEM?"); |
| rescue(); |
| } |
| /* relocate pointers; avoid wraparound if sizeof(int) < sizeof(int*) */ |
| d = addr1 - zero; |
| addr1 = a1 + d; |
| d = addr2 - zero; |
| addr2 = a1 + d; |
| d = dol - zero; |
| dol = a1 + d; |
| d = dot - zero; |
| dot = a1 + d; |
| zero = a1; |
| } |
| d = putline(); |
| nline++; |
| a1 = ++dol; |
| a2 = a1+1; |
| rdot = ++dot; |
| while(a1 > rdot) |
| *--a2 = *--a1; |
| *rdot = d; |
| } |
| 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) { |
| execlp("rc", "rc", "-c", buf, (char*)0); |
| sysfatal("exec failed: %r"); |
| 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, int isread) |
| { |
| int n; |
| |
| seek(tfile, b*BLKSIZE, 0); |
| if(isread) |
| n = read(tfile, buf, BLKSIZE); |
| else |
| n = write(tfile, buf, BLKSIZE); |
| if(n != 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, 0); |
| ichanged = 0; |
| iblock = bno; |
| blkio(bno, ibuff, 1); |
| return (Rune*)(ibuff+off); |
| } |
| if(oblock >= 0) |
| blkio(oblock, obuff, 0); |
| 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-2]) |
| 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].s.rsp && subexp[n].e.rep) { |
| sp = place(sp, subexp[n].s.rsp, subexp[n].e.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].s.rsp = getline(*addr); |
| } else |
| subexp[0].s.rsp = loc2; |
| subexp[0].e.rep = 0; |
| if(rregexec(pattern, linebuf, subexp, MAXSUB)) { |
| loc1 = subexp[0].s.rsp; |
| loc2 = subexp[0].e.rep; |
| return 1; |
| } |
| loc1 = loc2 = 0; |
| return 0; |
| |
| } |
| |
| void |
| putd(void) |
| { |
| int r; |
| |
| r = count%10; |
| count /= 10; |
| if(count) |
| putd(); |
| putchr(r + '0'); |
| } |
| |
| void |
| putst(char *sp) |
| { |
| Rune r; |
| |
| col = 0; |
| for(;;) { |
| sp += chartorune(&r, sp); |
| if(r == 0) |
| break; |
| putchr(r); |
| } |
| putchr('\n'); |
| } |
| |
| void |
| putshst(Rune *sp) |
| { |
| col = 0; |
| while(*sp) |
| putchr(*sp++); |
| putchr('\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); |
| } |