summaryrefslogtreecommitdiff
path: root/appl/cmd/ed.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/ed.b')
-rw-r--r--appl/cmd/ed.b1588
1 files changed, 1588 insertions, 0 deletions
diff --git a/appl/cmd/ed.b b/appl/cmd/ed.b
new file mode 100644
index 00000000..e374ce4e
--- /dev/null
+++ b/appl/cmd/ed.b
@@ -0,0 +1,1588 @@
+#
+# Editor
+#
+
+implement Editor;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "regex.m";
+ regex: Regex;
+ Re: import regex;
+include "sh.m";
+ sh: Sh;
+
+Editor: module {
+ init: fn(nil: ref Draw->Context, args: list of string);
+};
+
+FNSIZE: con 128; # file name
+LBSIZE: con 4096; # max line size
+BLKSIZE: con 4096; # block size in temp file
+NBLK: con 8191; # max size of temp file
+ESIZE: con 256; # max size of reg exp
+GBSIZE: con 256; # max size of global command
+MAXSUB: con 9; # max number of sub reg exp
+ESCFLG: con 16rFFFF; # escape Rune - user defined code
+EOF: con -1;
+BytesPerRune: con 2;
+RunesPerBlock: con BLKSIZE / BytesPerRune;
+
+APPEND_GETTTY, APPEND_GETSUB, APPEND_GETCOPY, APPEND_GETFILE: con iota;
+
+Subexp: adt {
+ rsp, rep: int;
+};
+
+Globp: adt {
+ s: string;
+ isnil: int;
+};
+
+addr1: int;
+addr2: int;
+anymarks: int;
+col: int;
+count: int;
+dol: int;
+dot: int;
+fchange: int;
+file: string;
+genbuf := array[LBSIZE] of int;
+given: int;
+globp: Globp;
+iblock: int;
+ichanged: int;
+io: ref Sys->FD;
+iobuf: ref Iobuf;
+lastc: int;
+line := array [70] of byte;
+linebp := -1;
+linebuf := array [LBSIZE] of int;
+listf: int;
+listn: int;
+loc1: int;
+loc2: int;
+names := array [26] of int;
+oblock: int;
+oflag: int;
+pattern: Re;
+peekc: int;
+pflag: int;
+rescuing: int;
+rhsbuf := array [LBSIZE/2] of int;
+savedfile: string;
+subnewa: int;
+subolda: int;
+subexp: array of Subexp;
+tfname: string;
+tline: int;
+waiting: int;
+wrapp: int;
+zero: array of int;
+drawctxt: ref Draw->Context;
+
+Q: con "";
+T: con "TMP";
+WRERR: con "WRITE ERROR";
+bpagesize := 20;
+hex: con "0123456789abcdef";
+linp: int;
+nlall := 128;
+tfile: ref Sys->FD;
+vflag := 1;
+
+debug(s: string)
+{
+ sys->print("%s", s);
+}
+
+init(ctxt: ref Draw->Context, args: list of string)
+{
+ drawctxt = ctxt;
+
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if (bufio == nil) {
+ sys->fprint(sys->fildes(2), "can't load %s\n", Bufio->PATH);
+ return;
+ }
+ regex = load Regex Regex->PATH;
+ if (regex == nil) {
+ sys->fprint(sys->fildes(2), "can't load %s\n", Regex->PATH);
+ return;
+ }
+
+# notify(notifyf);
+
+ if (args != nil)
+ args = tl args;
+
+ if (args != nil && hd args == "-o") {
+ oflag = 1;
+ vflag = 0;
+ args = tl args;
+ }
+
+ if (args != nil && hd args == "-") {
+ vflag = 0;
+ args = tl args;
+ }
+
+ if (oflag) {
+ savedfile = "/fd/1";
+ globp = ("a", 0);
+ } else if (args != nil) {
+ savedfile = hd args;
+ globp = ("r", 0);
+ }
+ else
+ globp = (nil, 1);
+ zero = array [nlall + 5] of int;
+ tfname = mktemp("/tmp/eXXXXX");
+# debug(sys->sprint("tfname %s\n", tfname));
+ _init();
+ for(;;){
+ {
+ commands();
+ quit();
+ }exception{
+ "savej" =>
+ ;
+ }
+ }
+}
+
+casee(c: int)
+{
+ setnoaddr();
+ if(vflag && fchange) {
+ fchange = 0;
+ error(Q);
+ }
+ filename(c);
+ _init();
+ addr2 = 0;
+ caseread();
+}
+
+casep()
+{
+ newline();
+ printcom();
+}
+
+caseq()
+{
+ setnoaddr();
+ newline();
+ quit();
+}
+
+caseread()
+{
+#debug("caseread " + file);
+ if((io=sys->open(file, Sys->OREAD)) == nil) {
+ lastc = '\n';
+ error(file);
+ }
+ iobuf = bufio->fopen(io, Sys->OREAD);
+ setwide();
+ squeeze(0);
+ c := 0 != dol;
+ append(APPEND_GETFILE, addr2);
+ exfile(Sys->OREAD);
+
+ fchange = c;
+}
+
+commands()
+{
+ a1: int;
+ c, temp: int;
+ lastsep: int;
+
+ for(;;) {
+ if(pflag) {
+ pflag = 0;
+ addr1 = addr2 = dot;
+ printcom();
+ }
+ c = '\n';
+ for(addr1 = -1;;) {
+ lastsep = c;
+ a1 = address();
+ c = getchr();
+ if(c != ',' && c != ';')
+ break;
+ if(lastsep == ',')
+ error(Q);
+ if(a1 < 0) {
+ a1 = 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;
+#debug(sys->sprint("%d,%d %c\n", addr1, addr2, c));
+ case c {
+ 'a' =>
+ add(0);
+ continue;
+
+ 'b' =>
+ nonzero();
+ browse();
+ continue;
+
+ 'c' =>
+ nonzero();
+ newline();
+ rdelete(addr1, addr2);
+ append(APPEND_GETTTY, addr1-1);
+ continue;
+
+ 'd' =>
+ nonzero();
+ newline();
+ rdelete(addr1, addr2);
+ continue;
+
+ 'E' =>
+ fchange = 0;
+ c = 'e';
+ casee(c);
+ continue;
+
+ 'e' =>
+ casee(c);
+ continue;
+
+ 'f' =>
+ setnoaddr();
+ filename(c);
+ putst(savedfile);
+ continue;
+
+ 'g' =>
+ global(1);
+ continue;
+
+ 'i' =>
+ add(-1);
+ continue;
+
+ 'j' =>
+ if(!given)
+ addr2++;
+ newline();
+ join();
+ continue;
+
+ 'k' =>
+ nonzero();
+ c = getchr();
+ if(c < 'a' || c > 'z')
+ error(Q);
+ newline();
+ names[c-'a'] = zero[addr2] & ~16r1;
+ anymarks |= 16r1;
+ continue;
+
+ 'm' =>
+ move(0);
+ continue;
+
+ 'n' =>
+ listn++;
+ newline();
+ printcom();
+ continue;
+
+ '\n' =>
+ if(a1 < 0) {
+ a1 = dot+1;
+ addr2 = a1;
+ addr1 = a1;
+ }
+ if(lastsep==';')
+ addr1 = a1;
+ printcom();
+ continue;
+
+ 'l' =>
+ listf++;
+ casep();
+ continue;
+
+ 'p' or 'P' =>
+ casep();
+ continue;
+
+ 'Q' =>
+ fchange = 0;
+ caseq();
+ continue;
+
+ 'q' =>
+ caseq();
+ continue;
+
+ 'r' =>
+ filename(c);
+ caseread();
+ continue;
+
+ 's' =>
+ nonzero();
+ substitute(!globp.isnil);
+ continue;
+
+ 't' =>
+ move(1);
+ continue;
+
+ 'u' =>
+ nonzero();
+ newline();
+ if((zero[addr2]&~8r01) != subnewa)
+ error(Q);
+ zero[addr2] = subolda;
+ dot = addr2;
+ continue;
+
+ 'v' =>
+ global(0);
+ continue;
+
+ 'W' or 'w' =>
+ if (c == 'W')
+ wrapp++;
+ setwide();
+ squeeze(dol>0);
+ temp = getchr();
+ if(temp != 'q' && temp != 'Q') {
+ peekc = temp;
+ temp = 0;
+ }
+ filename(c);
+ if(!wrapp ||
+ ((io = sys->open(file, Sys->OWRITE)) == nil) ||
+ ((sys->seek(io, big 0, Sys->SEEKEND)) < big 0))
+ if((io = sys->create(file, Sys->OWRITE, 8r0666)) == nil)
+ error(file);
+ iobuf = bufio->fopen(io, Sys->OWRITE);
+ wrapp = 0;
+ if(dol > 0)
+ putfile();
+ exfile(Sys->OWRITE);
+ if(addr1<=1 && addr2==dol)
+ fchange = 0;
+ if(temp == 'Q')
+ fchange = 0;
+ if(temp)
+ quit();
+ continue;
+
+ '=' =>
+ setwide();
+ squeeze(0);
+ newline();
+ count = addr2 - 0;
+ putd();
+ putchr('\n');
+ continue;
+
+ '!' =>
+ callunix();
+ continue;
+
+ EOF =>
+ return;
+
+ }
+ error(Q);
+ }
+}
+
+printcom()
+{
+ a1: int;
+
+ nonzero();
+ a1 = addr1;
+ do {
+ if(listn) {
+ count = a1-0;
+ putd();
+ putchr('\t');
+ }
+ putshst(getline(zero[a1++]));
+ } while(a1 <= addr2);
+ dot = addr2;
+ listf = 0;
+ listn = 0;
+ pflag = 0;
+}
+
+
+address(): int
+{
+ sign, a, opcnt, nextopand, b, c: int;
+
+ 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 = 0;
+ a += sign*getnum();
+ } else
+ case c {
+ '$' or '.' =>
+ if (c == '$')
+ a = dol;
+ if(opcnt)
+ error(Q);
+
+ '\'' =>
+ c = getchr();
+ if(opcnt || c < 'a' || c > 'z')
+ error(Q);
+ a = 0;
+ do {
+ a++;
+ } while(a <= dol && names[c-'a'] != (zero[a] & ~8r01));
+
+ '?' or '/' =>
+ if (c == '?')
+ sign = -sign;
+ compile(c);
+ b = a;
+ for(;;) {
+ a += sign;
+ if(a <= 0)
+ a = dol;
+ if(a > dol)
+ a = 0;
+ if(match(a))
+ break;
+ if(a == b)
+ error(Q);
+ }
+ break;
+
+ * =>
+ if(nextopand == opcnt) {
+ a += sign;
+ if(a < 0 || dol < a)
+ continue; # error(Q);
+ }
+ if(c != '+' && c != '-' && c != '^') {
+ peekc = c;
+ if(opcnt == 0)
+ a = -1;
+ return a;
+ }
+ sign = 1;
+ if(c != '+')
+ sign = -sign;
+ nextopand = ++opcnt;
+ continue;
+ }
+ sign = 1;
+ opcnt++;
+ } while(0 <= a && a <= dol);
+ error(Q);
+ return -1;
+}
+
+getnum(): int
+{
+ r, c: int;
+
+ r = 0;
+ for(;;) {
+ c = getchr();
+ if(c < '0' || c > '9')
+ break;
+ r = r*10 + (c-'0');
+ }
+ peekc = c;
+ return r;
+}
+
+setwide()
+{
+ if(!given) {
+ addr1 = 0 + (dol>0);
+ addr2 = dol;
+ }
+}
+
+setnoaddr()
+{
+ if(given)
+ error(Q);
+}
+
+nonzero()
+{
+ squeeze(1);
+}
+
+squeeze(i: int)
+{
+ if(addr1 < 0+i || addr2 > dol || addr1 > addr2)
+ error(Q);
+}
+
+newline()
+{
+ c: int;
+
+ 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);
+}
+
+filename(comm: int)
+{
+ rune: int;
+ c: int;
+
+ count = 0;
+ c = getchr();
+ if(c == '\n' || c == EOF) {
+ if(savedfile == nil && comm != 'f')
+ error(Q);
+ file = savedfile;
+ return;
+ }
+ if(c != ' ')
+ error(Q);
+ while((c=getchr()) == ' ')
+ ;
+ if(c == '\n')
+ error(Q);
+ file = nil;
+ do {
+ if(c == ' ' || c == EOF)
+ error(Q);
+ rune = c;
+ file[len file] = c;
+ } while((c=getchr()) != '\n');
+ if(savedfile == nil || comm == 'e' || comm == 'f')
+ savedfile = file;
+}
+
+exfile(om: int)
+{
+
+ if(om == Sys->OWRITE)
+ if(iobuf.flush() < 0)
+ error(Q);
+ iobuf.close();
+ iobuf = nil;
+ io = nil;
+ if(vflag) {
+ putd();
+ putchr('\n');
+ }
+}
+
+error1(s: string)
+{
+ c: int;
+
+ wrapp = 0;
+ listf = 0;
+ listn = 0;
+ count = 0;
+ sys->seek(sys->fildes(0), big 0, Sys->SEEKEND); # what does this do?
+ pflag = 0;
+ if(!globp.isnil)
+ lastc = '\n';
+ globp = (nil, 1);
+ peekc = lastc;
+ if(lastc)
+ for(;;) {
+ c = getchr();
+ if(c == '\n' || c == EOF)
+ break;
+ }
+ if(io != nil)
+ io = nil;
+ putchr('?');
+ putst(s);
+}
+
+error(s: string)
+{
+ error1(s);
+ raise "savej";
+}
+
+rescue()
+{
+ rescuing = 1;
+ if(dol > 0) {
+ addr1 = 0+1;
+ addr2 = dol;
+ io = sys->create("ed.hup", Sys->OWRITE, 8r0666);
+ if(io != nil){
+ iobuf = bufio->fopen(io, Sys->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();
+# }
+
+getchr(): int
+{
+ s := array [Sys->UTFmax] of byte;
+ i: int;
+ r: int;
+ status: int;
+ if(lastc = peekc) {
+ peekc = 0;
+#debug(sys->sprint("getchr: peekc %c\n", lastc));
+ return lastc;
+ }
+ if(!globp.isnil) {
+ if (globp.s != nil) {
+ lastc = globp.s[0];
+ globp.s = globp.s[1:];
+#debug(sys->sprint("getchr: globp %c remaining %d\n", lastc, len globp.s));
+ return lastc;
+ }
+ globp = (nil, 1);
+#debug(sys->sprint("getchr: globp end\n"));
+ return EOF;
+ }
+#debug("globp nil\n");
+ for(i=0;;) {
+ if(sys->read(sys->fildes(0), s[i:], 1) <= 0)
+ return lastc = EOF;
+ i++;
+ (r, nil, status) = sys->byte2char(s, 0);
+ if (status > 0)
+ break;
+
+ }
+ lastc = r;
+ return lastc;
+}
+
+gety(): int
+{
+ c: int;
+ gf: int;
+ p: int;
+
+ p = 0;
+ gf = !globp.isnil;
+ for(;;) {
+ c = getchr();
+ if(c == '\n') {
+ linebuf[p] = 0;
+ return 0;
+ }
+ if(c == EOF) {
+ if(gf)
+ peekc = c;
+ return c;
+ }
+ if(c == 0)
+ continue;
+ linebuf[p++] = c;
+ if(p >= len linebuf)
+ error(Q);
+ }
+ return 0;
+}
+
+gettty(): int
+{
+ rc: int;
+
+ rc = gety();
+ if(rc)
+ return rc;
+ if(linebuf[0] == '.' && linebuf[1] == 0)
+ return EOF;
+ return 0;
+}
+
+getfile(): int
+{
+ c: int;
+ lp: int;
+
+ lp = 0;
+ do {
+ c = iobuf.getc();
+ if(c < 0) {
+ if(lp > 0) {
+ putst("'\\n' appended");
+ c = '\n';
+ } else
+ return EOF;
+ }
+ if(lp >= len linebuf) {
+ lastc = '\n';
+ error(Q);
+ }
+ linebuf[lp++] = c;
+ count++;
+ } while(c != '\n');
+ linebuf[lp - 1] = 0;
+#debug(sys->sprint("getline read %d\n", lp));
+ return 0;
+}
+
+putfile()
+{
+ a1: int;
+ lp: int;
+ c: int;
+
+ a1 = addr1;
+ do {
+ lp = getline(zero[a1++]);
+ for(;;) {
+ count++;
+ c = linebuf[lp++];
+ if(c == 0) {
+ if (iobuf.putc('\n') < 0)
+ error(Q);
+ break;
+ }
+ if (iobuf.putc(c) < 0)
+ error(Q);
+ }
+ } while(a1 <= addr2);
+ if(iobuf.flush() < 0)
+ error(Q);
+}
+
+append(f: int, a: int): int
+{
+ a1, a2, rdot, nline, _tl: int;
+ rv: int;
+
+ nline = 0;
+ dot = a;
+ for (;;) {
+ case f {
+ APPEND_GETTTY => rv = gettty();
+ APPEND_GETSUB => rv = getsub();
+ APPEND_GETCOPY => rv = getcopy();
+ APPEND_GETFILE => rv = getfile();
+ }
+ if (rv != 0)
+ break;
+ if(dol >= nlall) {
+ nlall += 512;
+ newzero := array [nlall + 5] of int;
+ if(newzero == nil) {
+ error("MEM?");
+ rescue();
+ }
+ newzero[0:] = zero;
+ zero = newzero;
+ }
+ _tl = putline();
+ nline++;
+ a1 = ++dol;
+ a2 = a1+1;
+ rdot = ++dot;
+ zero[rdot:] = zero[rdot - 1: a1];
+ zero[rdot] = _tl;
+ }
+#debug(sys->sprint("end of append - dot %d\n", dot));
+ return nline;
+}
+
+add(i: int)
+{
+ if(i && (given || dol > 0)) {
+ addr1--;
+ addr2--;
+ }
+ squeeze(0);
+ newline();
+ append(APPEND_GETTTY, addr2);
+}
+
+bformat, bnum: int;
+
+browse()
+{
+ forward, n: int;
+
+ 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 <= 0)
+ addr1 = 0+1;
+ }
+ printcom();
+}
+
+callunix()
+{
+ buf: string;
+ c: int;
+
+ if (sh == nil)
+ sh = load Sh Sh->PATH;
+ if (sh == nil) {
+ putst("can't load shell");
+ return;
+ }
+ setnoaddr();
+ while((c=getchr()) != EOF && c != '\n')
+ buf[len buf] = c;
+ sh->system(drawctxt, buf);
+ if(vflag)
+ putst("!");
+}
+
+quit()
+{
+ if(vflag && fchange && dol!=0) {
+ fchange = 0;
+ error(Q);
+ }
+ sys->remove(tfname);
+ exit;
+}
+
+onquit(nil: int)
+{
+ quit();
+}
+
+rdelete(ad1, ad2: int)
+{
+ a1, a2, a3: int;
+
+ a1 = ad1;
+ a2 = ad2+1;
+ a3 = dol;
+ dol -= a2 - a1;
+ do {
+ zero[a1++] = zero[a2++];
+ } while (a2 <= a3);
+ a1 = ad1;
+ if(a1 > dol)
+ a1 = dol;
+ dot = a1;
+ fchange = 1;
+}
+
+gdelete()
+{
+ a1, a2, a3: int;
+
+ a3 = dol;
+ for(a1=0; (zero[a1]&8r01)==0; a1++)
+ if(a1>=a3)
+ return;
+ for(a2=a1+1; a2<=a3;) {
+ if(zero[a2] & 8r01) {
+ a2++;
+ dot = a1;
+ } else
+ zero[a1++] = zero[a2++];
+ }
+ dol = a1-1;
+ if(dot > dol)
+ dot = dol;
+ fchange = 1;
+}
+
+getline(_tl: int): int
+{
+ lp, bp: int;
+ nl: int;
+ block: array of int;
+#debug(sys->sprint("getline %d\n", _tl));
+ lp = 0;
+ (block, bp) = getblock(_tl, Sys->OREAD);
+ nl = len block - bp;
+ _tl &= ~(RunesPerBlock - 1);
+ while(linebuf[lp++] = block[bp++]) {
+ nl--;
+ if(nl == 0) {
+ (block, bp) = getblock(_tl += RunesPerBlock, Sys->OREAD);
+ nl = len block;
+ }
+ }
+ return 0;
+}
+
+putline(): int
+{
+ lp, bp: int;
+ nl, _tl: int;
+ block: array of int;
+ fchange = 1;
+ lp = 0;
+ _tl = tline;
+ (block, bp) = getblock(_tl, Sys->OWRITE);
+ nl = len block - bp;
+ _tl &= ~(RunesPerBlock-1); # _tl is now at the beginning of the block
+ while(block[bp] = linebuf[lp++]) {
+ if(block[bp++] == '\n') {
+ block[bp-1] = 0;
+ linebp = lp;
+ break;
+ }
+ nl--;
+ if(nl == 0) {
+ _tl += RunesPerBlock;
+ (block, bp) = getblock(_tl, Sys->OWRITE);
+ nl = len block;
+ }
+ }
+ nl = tline;
+ tline += ((lp) + 8r03) & 8r077776;
+ return nl;
+}
+
+tbuf := array [BLKSIZE] of byte;
+
+getrune(buf: array of byte): int
+{
+ return int buf[0] + (int buf[1] << 8);
+}
+
+putrune(buf: array of byte, v: int)
+{
+ buf[0] = byte (v);
+ buf[1] = byte (v >> 8);
+}
+
+blkio(b: int, buf: array of int, writefunc: int)
+{
+ sys->seek(tfile, big b * big BLKSIZE, Sys->SEEKSTART);
+ if (writefunc) {
+ # flatten buf into tbuf
+ for (x := 0; x < RunesPerBlock; x++)
+ putrune(tbuf[x * BytesPerRune:], buf[x]);
+ if (sys->write(tfile, tbuf, BLKSIZE) != len tbuf) {
+ error(T);
+ }
+ }
+ else {
+ if (sys->read(tfile, tbuf, len tbuf) != len tbuf) {
+ error(T);
+ }
+ for (x := 0; x < RunesPerBlock; x++)
+ buf[x] = getrune(tbuf[x * BytesPerRune:]);
+ }
+}
+
+ibuff := array [RunesPerBlock] of int;
+obuff := array [RunesPerBlock] of int;
+
+getblock(atl, iof: int): (array of int, int)
+{
+ bno, off: int;
+
+ bno = atl / RunesPerBlock;
+ off = (atl * BytesPerRune) & (BLKSIZE-1) & ~8r03;
+ if(bno >= NBLK) {
+ lastc = '\n';
+ error(T);
+ }
+ off /= BytesPerRune;
+ if(bno == iblock) {
+ ichanged |= iof;
+#debug(sys->sprint("getblock(%d, %d): returns ibuff offset %d\n", atl, iof, off));
+ return (ibuff, off);
+ }
+ if(bno == oblock) {
+#debug(sys->sprint("getblock(%d, %d): returns obuff offset %d\n", atl, iof, off));
+ return (obuff, off);
+ }
+ if(iof == Sys->OREAD) {
+ if(ichanged)
+ blkio(iblock, ibuff, 1);
+ ichanged = 0;
+ iblock = bno;
+ blkio(bno, ibuff, 0);
+#debug(sys->sprint("getblock(%d, %d): returns ibuff offset %d\n", atl, iof, off));
+ return (ibuff, off);
+ }
+ if(oblock >= 0)
+ blkio(oblock, obuff, 1);
+ oblock = bno;
+#debug(sys->sprint("getblock(%d, %d): returns offset %d\n", atl, iof, off));
+ return (obuff, off);
+}
+
+_init()
+{
+ markp: int;
+
+ tfile = nil;
+ tline = RunesPerBlock;
+ for(markp = 0; markp < len names; markp++)
+ names[markp] = 0;
+ subnewa = 0;
+ anymarks = 0;
+ iblock = -1;
+ oblock = -1;
+ ichanged = 0;
+ if((tfile = sys->create(tfname, Sys->ORDWR, 8r0600)) == nil){
+ error1(T);
+ exit;
+ }
+ dot = dol = 0;
+}
+
+global(k: int)
+{
+ globuf: string;
+ c, a1: int;
+
+ if(!globp.isnil)
+ error(Q);
+ setwide();
+ squeeze(dol > 0);
+ c = getchr();
+ if(c == '\n')
+ error(Q);
+ compile(c);
+ globuf = nil;
+ while((c=getchr()) != '\n') {
+ if(c == EOF)
+ error(Q);
+ if(c == '\\') {
+ c = getchr();
+ if(c != '\n')
+ globuf[len globuf] = '\\';
+ }
+ globuf[len globuf] = c;
+ }
+ if(globuf == nil)
+ globuf = "p";
+ globuf[len globuf] = '\n';
+ for(a1=0; a1<=dol; a1++) {
+ zero[a1] &= ~8r01;
+ if(a1 >= addr1 && a1 <= addr2 && match(a1) == k)
+ zero[a1] |= 8r01;
+ }
+
+ #
+ # Special case: g/.../d (avoid n^2 algorithm)
+
+ if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) {
+ gdelete();
+ return;
+ }
+ for(a1=0; a1<=dol; a1++) {
+ if(zero[a1] & 8r01) {
+ zero[a1] &= ~8r01;
+ dot = a1;
+ globp = (globuf, 0);
+ commands();
+ a1 = 0;
+ }
+ }
+}
+
+join()
+{
+ gp, lp: int;
+ a1: int;
+
+ nonzero();
+ gp = 0;
+ for(a1=addr1; a1<=addr2; a1++) {
+ lp = getline(zero[a1]);
+ while(genbuf[gp] = linebuf[lp++])
+ if(gp++ >= LBSIZE-2)
+ error(Q);
+ }
+ lp = 0;
+ gp = 0;
+ while(linebuf[lp++] = genbuf[gp++])
+ ;
+ zero[addr1] = putline();
+ if(addr1 < addr2)
+ rdelete(addr1+1, addr2);
+ dot = addr1;
+}
+
+substitute(inglob: int)
+{
+ mp, a1, nl, gsubf, n: int;
+
+ n = getnum(); # OK even if n==0
+ gsubf = compsub();
+ for(a1 = addr1; a1 <= addr2; a1++) {
+ if(match(a1)){
+ m := n;
+
+ do {
+ span := loc2-loc1;
+
+ if(--m <= 0) {
+ dosub();
+ if(!gsubf)
+ break;
+ if(span == 0) { # null RE match
+ if(zero[loc2] == 0)
+ break;
+ loc2++;
+ }
+ }
+ } while(match(-1));
+ if(m <= 0) {
+ inglob |= 8r01;
+ subnewa = putline();
+ zero[a1] &= ~8r01;
+ if(anymarks) {
+ for(mp=0; mp<len names; mp++)
+ if(names[mp] == zero[a1])
+ names[mp] = subnewa;
+ }
+ subolda = zero[a1];
+ zero[a1] = subnewa;
+#debug(sys->sprint("append-getsub linebp = %d\n", linebp));
+ nl = append(APPEND_GETSUB, a1);
+ addr2 += nl;
+ }
+ }
+ }
+ if(inglob == 0)
+ error(Q);
+}
+
+compsub(): int
+{
+ seof, c: int;
+ p: int;
+
+ seof = getchr();
+ if(seof == '\n' || seof == ' ')
+ error(Q);
+ compile(seof);
+ p = 0;
+ for(;;) {
+ c = getchr();
+ if(c == '\\') {
+ c = getchr();
+ rhsbuf[p++] = ESCFLG;
+ if(p >= LBSIZE / 2)
+ error(Q);
+ } else
+ if(c == '\n' && (globp.isnil || globp.s == nil)) {
+ peekc = c;
+ pflag++;
+ break;
+ } else
+ if(c == seof)
+ break;
+ rhsbuf[p++] = c;
+ if(p >= LBSIZE / 2)
+ error(Q);
+ }
+ rhsbuf[p] = 0;
+ peekc = getchr();
+ if(peekc == 'g') {
+ peekc = 0;
+ newline();
+ return 1;
+ }
+ newline();
+ return 0;
+}
+
+getsub(): int
+{
+ p1, p2: int;
+
+ p1 = 0;
+ if((p2 = linebp) == -1)
+ return EOF;
+ while(linebuf[p1++] = linebuf[p2++])
+ ;
+ linebp = -1;
+ return 0;
+}
+
+dosub()
+{
+ lp, sp, rp: int;
+ c, n: int;
+
+# lp = linebuf;
+# sp = genbuf;
+# rp = rhsbuf;
+ lp = 0;
+ sp = 0;
+ rp = 0;
+ while(lp < loc1)
+ genbuf[sp++] = linebuf[lp++];
+ while(c = rhsbuf[rp++]) {
+ if(c == '&'){
+ sp = place(sp, loc1, loc2);
+ continue;
+ }
+ if(c == ESCFLG && (c = rhsbuf[rp++]) >= '1' && c < MAXSUB+'0') {
+ n = c-'0';
+ if(subexp != nil && subexp[n].rsp >= 0 && subexp[n].rep >= 0) {
+ sp = place(sp, subexp[n].rsp, subexp[n].rep);
+ continue;
+ }
+ error(Q);
+ }
+ genbuf[sp++] = c;
+ if(sp >= LBSIZE)
+ error(Q);
+ }
+ lp = loc2;
+ loc2 = sp;
+ while(genbuf[sp++] = linebuf[lp++])
+ if(sp >= LBSIZE)
+ error(Q);
+ linebuf[0:] = genbuf[0: sp];
+}
+
+place(sp: int, l1: int, l2: int): int
+{
+
+ while(l1 < l2) {
+ genbuf[sp++] = linebuf[l1++];
+ if(sp >= LBSIZE)
+ error(Q);
+ }
+ return sp;
+}
+
+move(cflag: int)
+{
+ _adt, ad1, ad2: int;
+
+ nonzero();
+ if((_adt = address()) < 0) # address() guarantees addr is in range
+ error(Q);
+ newline();
+ if(cflag) {
+ ad1 = dol;
+ append(APPEND_GETCOPY, ad1++);
+ ad2 = dol;
+ } else {
+ ad2 = addr2;
+ for(ad1 = addr1; ad1 <= ad2;)
+ zero[ad1++] &= ~8r01;
+ 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;
+}
+
+reverse(a1, a2: int)
+{
+ t: int;
+
+ for(;;) {
+ t = zero[--a2];
+ if(a2 <= a1)
+ return;
+ zero[a2] = zero[a1];
+ zero[a1++] = t;
+ }
+}
+
+getcopy(): int
+{
+ if(addr1 > addr2)
+ return EOF;
+ getline(zero[addr1++]);
+ return 0;
+}
+
+compile(eof: int)
+{
+ c: int;
+
+ if((c = getchr()) == '\n') {
+ peekc = c;
+ c = eof;
+ }
+ if(c == eof) {
+ if(pattern == nil)
+ error(Q);
+ return;
+ }
+ pattern = nil;
+ program := "";
+ do {
+
+ if(c == '\\') {
+ program[len program] = '\\';
+ if((c = getchr()) == '\n') {
+ error(Q);
+ return;
+ }
+ }
+ program[len program] = c;
+ } while((c = getchr()) != eof && c != '\n');
+ if(c == '\n')
+ peekc = c;
+ diag: string;
+#debug("program " + program + "\n");
+ (pattern, diag) = regex->compile(program, 1);
+#if (diag != nil)
+# debug("diag " + diag + "\n");
+ if (diag != nil)
+ pattern = nil;
+}
+
+mkstring(a: array of int): string
+{
+ s: string;
+ for (x := 0; x < len a; x++) {
+ if (a[x] == 0)
+ break;
+ s[x] = a[x];
+ }
+ return s;
+}
+
+match(addr: int): int
+{
+ rsp: int;
+ if(pattern == nil)
+ return 0;
+ if(addr >= 0){
+ if(addr == 0)
+ return 0;
+ rsp = getline(zero[addr]);
+ } else
+ rsp = loc2;
+ s := mkstring(linebuf);
+ subexp = regex->executese(pattern, s, (rsp, len s), rsp == 0, 1);
+ if(subexp != nil) {
+ (loc1, loc2) = subexp[0];
+ return 1;
+ }
+ loc1 = loc2 = -1;
+ return 0;
+}
+
+putd()
+{
+ r: int;
+
+ r = count%10;
+ count /= 10;
+ if(count)
+ putd();
+ putchr(r + '0');
+}
+
+putst(s: string)
+{
+ col = 0;
+ for(x := 0; x < len s; x++)
+ putchr(s[x]);
+ putchr('\n');
+}
+
+putshst(sp: int)
+{
+ col = 0;
+ while(linebuf[sp]) {
+ putchr(linebuf[sp++]);
+ }
+ putchr('\n');
+}
+
+putchr(ac: int)
+{
+ lp: int;
+ c: int;
+ rune: int;
+ lp = linp;
+ c = ac;
+ if(listf) {
+ if(c == '\n') {
+ if(linp != 0 && line[linp - 1] == byte ' ') {
+ line[lp++] = byte '\\';
+ line[lp++] = byte 'n';
+ }
+ } else {
+ if(col > (72-6-2)) {
+ col = 8;
+ line[lp++] = byte '\\';
+ line[lp++] = byte '\n';
+ line[lp++] = byte '\t';
+ }
+ col++;
+ if(c=='\b' || c=='\t' || c=='\\') {
+ line[lp++] = byte '\\';
+ if(c == '\b')
+ c = 'b';
+ else
+ if(c == '\t')
+ c = 't';
+ col++;
+ } else
+ if(c<' ' || c>=8r0177) {
+ line[lp++] = byte '\\';
+ line[lp++] = byte 'x';
+ line[lp++] = byte hex[c>>12];
+ line[lp++] = byte hex[c>>8&16rF];
+ line[lp++] = byte hex[c>>4&16rF];
+ c = hex[c&16rF];
+ col += 5;
+ }
+ }
+ }
+
+ rune = c;
+ lp += sys->char2byte(rune, line, lp);
+
+ if(c == '\n' || lp >= len line - 5) {
+ linp = 0;
+ if (oflag)
+ sys->write(sys->fildes(2), line, lp);
+ else
+ sys->write(sys->fildes(1), line, lp);
+ return;
+ }
+ linp = lp;
+}
+
+stringfromint(i: int): string
+{
+ s: string;
+ s[0] = i;
+ return s;
+}
+
+mktemp(as: string): string
+{
+ pid: int;
+ s: string;
+
+ s = nil;
+ pid = sys->pctl(0, nil);
+ for (x := len as - 1; x >= 0; x--)
+ if (as[x] == 'X') {
+ s = stringfromint('0' + pid % 10) + s;
+ pid /= 10;
+ }
+ else
+ s = stringfromint(as[x]) + s;
+ s[len s] = 'a';
+ for (;;) {
+ (rv, nil) := sys->stat(s);
+ if (rv < 0)
+ break;
+ if (s[len s - 1] == 'z')
+ return "/";
+ s[len s - 1]++;
+ }
+ return s;
+}