|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <draw.h> | 
|  | #include <thread.h> | 
|  | #include <cursor.h> | 
|  | #include <mouse.h> | 
|  | #include <keyboard.h> | 
|  | #include <frame.h> | 
|  | #include <fcall.h> | 
|  | #include <plumb.h> | 
|  | #include "dat.h" | 
|  | #include "fns.h" | 
|  |  | 
|  | enum | 
|  | { | 
|  | Slop = 100	/* room to grow with reallocation */ | 
|  | }; | 
|  |  | 
|  | static | 
|  | void | 
|  | sizecache(Buffer *b, uint n) | 
|  | { | 
|  | if(n <= b->cmax) | 
|  | return; | 
|  | b->cmax = n+Slop; | 
|  | b->c = runerealloc(b->c, b->cmax); | 
|  | } | 
|  |  | 
|  | static | 
|  | void | 
|  | addblock(Buffer *b, uint i, uint n) | 
|  | { | 
|  | if(i > b->nbl) | 
|  | error("internal error: addblock"); | 
|  |  | 
|  | b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]); | 
|  | if(i < b->nbl) | 
|  | memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*)); | 
|  | b->bl[i] = disknewblock(disk, n); | 
|  | b->nbl++; | 
|  | } | 
|  |  | 
|  | static | 
|  | void | 
|  | delblock(Buffer *b, uint i) | 
|  | { | 
|  | if(i >= b->nbl) | 
|  | error("internal error: delblock"); | 
|  |  | 
|  | diskrelease(disk, b->bl[i]); | 
|  | b->nbl--; | 
|  | if(i < b->nbl) | 
|  | memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*)); | 
|  | b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Move cache so b->cq <= q0 < b->cq+b->cnc. | 
|  | * If at very end, q0 will fall on end of cache block. | 
|  | */ | 
|  |  | 
|  | static | 
|  | void | 
|  | flush(Buffer *b) | 
|  | { | 
|  | if(b->cdirty || b->cnc==0){ | 
|  | if(b->cnc == 0) | 
|  | delblock(b, b->cbi); | 
|  | else | 
|  | diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc); | 
|  | b->cdirty = FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | static | 
|  | void | 
|  | setcache(Buffer *b, uint q0) | 
|  | { | 
|  | Block **blp, *bl; | 
|  | uint i, q; | 
|  |  | 
|  | if(q0 > b->nc) | 
|  | error("internal error: setcache"); | 
|  | /* | 
|  | * flush and reload if q0 is not in cache. | 
|  | */ | 
|  | if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc)) | 
|  | return; | 
|  | /* | 
|  | * if q0 is at end of file and end of cache, continue to grow this block | 
|  | */ | 
|  | if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<Maxblock) | 
|  | return; | 
|  | flush(b); | 
|  | /* find block */ | 
|  | if(q0 < b->cq){ | 
|  | q = 0; | 
|  | i = 0; | 
|  | }else{ | 
|  | q = b->cq; | 
|  | i = b->cbi; | 
|  | } | 
|  | blp = &b->bl[i]; | 
|  | while(q+(*blp)->u.n <= q0 && q+(*blp)->u.n < b->nc){ | 
|  | q += (*blp)->u.n; | 
|  | i++; | 
|  | blp++; | 
|  | if(i >= b->nbl) | 
|  | error("block not found"); | 
|  | } | 
|  | bl = *blp; | 
|  | /* remember position */ | 
|  | b->cbi = i; | 
|  | b->cq = q; | 
|  | sizecache(b, bl->u.n); | 
|  | b->cnc = bl->u.n; | 
|  | /*read block*/ | 
|  | diskread(disk, bl, b->c, b->cnc); | 
|  | } | 
|  |  | 
|  | void | 
|  | bufinsert(Buffer *b, uint q0, Rune *s, uint n) | 
|  | { | 
|  | uint i, m, t, off; | 
|  |  | 
|  | if(q0 > b->nc) | 
|  | error("internal error: bufinsert"); | 
|  |  | 
|  | while(n > 0){ | 
|  | setcache(b, q0); | 
|  | off = q0-b->cq; | 
|  | if(b->cnc+n <= Maxblock){ | 
|  | /* Everything fits in one block. */ | 
|  | t = b->cnc+n; | 
|  | m = n; | 
|  | if(b->bl == nil){	/* allocate */ | 
|  | if(b->cnc != 0) | 
|  | error("internal error: bufinsert1 cnc!=0"); | 
|  | addblock(b, 0, t); | 
|  | b->cbi = 0; | 
|  | } | 
|  | sizecache(b, t); | 
|  | runemove(b->c+off+m, b->c+off, b->cnc-off); | 
|  | runemove(b->c+off, s, m); | 
|  | b->cnc = t; | 
|  | goto Tail; | 
|  | } | 
|  | /* | 
|  | * We must make a new block.  If q0 is at | 
|  | * the very beginning or end of this block, | 
|  | * just make a new block and fill it. | 
|  | */ | 
|  | if(q0==b->cq || q0==b->cq+b->cnc){ | 
|  | if(b->cdirty) | 
|  | flush(b); | 
|  | m = min(n, Maxblock); | 
|  | if(b->bl == nil){	/* allocate */ | 
|  | if(b->cnc != 0) | 
|  | error("internal error: bufinsert2 cnc!=0"); | 
|  | i = 0; | 
|  | }else{ | 
|  | i = b->cbi; | 
|  | if(q0 > b->cq) | 
|  | i++; | 
|  | } | 
|  | addblock(b, i, m); | 
|  | sizecache(b, m); | 
|  | runemove(b->c, s, m); | 
|  | b->cq = q0; | 
|  | b->cbi = i; | 
|  | b->cnc = m; | 
|  | goto Tail; | 
|  | } | 
|  | /* | 
|  | * Split the block; cut off the right side and | 
|  | * let go of it. | 
|  | */ | 
|  | m = b->cnc-off; | 
|  | if(m > 0){ | 
|  | i = b->cbi+1; | 
|  | addblock(b, i, m); | 
|  | diskwrite(disk, &b->bl[i], b->c+off, m); | 
|  | b->cnc -= m; | 
|  | } | 
|  | /* | 
|  | * Now at end of block.  Take as much input | 
|  | * as possible and tack it on end of block. | 
|  | */ | 
|  | m = min(n, Maxblock-b->cnc); | 
|  | sizecache(b, b->cnc+m); | 
|  | runemove(b->c+b->cnc, s, m); | 
|  | b->cnc += m; | 
|  | Tail: | 
|  | b->nc += m; | 
|  | q0 += m; | 
|  | s += m; | 
|  | n -= m; | 
|  | b->cdirty = TRUE; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | bufdelete(Buffer *b, uint q0, uint q1) | 
|  | { | 
|  | uint m, n, off; | 
|  |  | 
|  | if(!(q0<=q1 && q0<=b->nc && q1<=b->nc)) | 
|  | error("internal error: bufdelete"); | 
|  | while(q1 > q0){ | 
|  | setcache(b, q0); | 
|  | off = q0-b->cq; | 
|  | if(q1 > b->cq+b->cnc) | 
|  | n = b->cnc - off; | 
|  | else | 
|  | n = q1-q0; | 
|  | m = b->cnc - (off+n); | 
|  | if(m > 0) | 
|  | runemove(b->c+off, b->c+off+n, m); | 
|  | b->cnc -= n; | 
|  | b->cdirty = TRUE; | 
|  | q1 -= n; | 
|  | b->nc -= n; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | bufloader(void *v, uint q0, Rune *r, int nr) | 
|  | { | 
|  | bufinsert(v, q0, r, nr); | 
|  | return nr; | 
|  | } | 
|  |  | 
|  | uint | 
|  | loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *arg) | 
|  | { | 
|  | char *p; | 
|  | Rune *r; | 
|  | int l, m, n, nb, nr; | 
|  | uint q1; | 
|  |  | 
|  | p = emalloc((Maxblock+UTFmax+1)*sizeof p[0]); | 
|  | r = runemalloc(Maxblock); | 
|  | m = 0; | 
|  | n = 1; | 
|  | q1 = q0; | 
|  | /* | 
|  | * At top of loop, may have m bytes left over from | 
|  | * last pass, possibly representing a partial rune. | 
|  | */ | 
|  | while(n > 0){ | 
|  | n = read(fd, p+m, Maxblock); | 
|  | if(n < 0){ | 
|  | warning(nil, "read error in Buffer.load"); | 
|  | break; | 
|  | } | 
|  | m += n; | 
|  | p[m] = 0; | 
|  | l = m; | 
|  | if(n > 0) | 
|  | l -= UTFmax; | 
|  | cvttorunes(p, l, r, &nb, &nr, nulls); | 
|  | memmove(p, p+nb, m-nb); | 
|  | m -= nb; | 
|  | q1 += (*f)(arg, q1, r, nr); | 
|  | } | 
|  | free(p); | 
|  | free(r); | 
|  | return q1-q0; | 
|  | } | 
|  |  | 
|  | uint | 
|  | bufload(Buffer *b, uint q0, int fd, int *nulls) | 
|  | { | 
|  | if(q0 > b->nc) | 
|  | error("internal error: bufload"); | 
|  | return loadfile(fd, q0, nulls, bufloader, b); | 
|  | } | 
|  |  | 
|  | void | 
|  | bufread(Buffer *b, uint q0, Rune *s, uint n) | 
|  | { | 
|  | uint m; | 
|  |  | 
|  | if(!(q0<=b->nc && q0+n<=b->nc)) | 
|  | error("bufread: internal error"); | 
|  |  | 
|  | while(n > 0){ | 
|  | setcache(b, q0); | 
|  | m = min(n, b->cnc-(q0-b->cq)); | 
|  | runemove(s, b->c+(q0-b->cq), m); | 
|  | q0 += m; | 
|  | s += m; | 
|  | n -= m; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | bufreset(Buffer *b) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | b->nc = 0; | 
|  | b->cnc = 0; | 
|  | b->cq = 0; | 
|  | b->cdirty = 0; | 
|  | b->cbi = 0; | 
|  | /* delete backwards to avoid n² behavior */ | 
|  | for(i=b->nbl-1; --i>=0; ) | 
|  | delblock(b, i); | 
|  | } | 
|  |  | 
|  | void | 
|  | bufclose(Buffer *b) | 
|  | { | 
|  | bufreset(b); | 
|  | free(b->c); | 
|  | b->c = nil; | 
|  | b->cnc = 0; | 
|  | free(b->bl); | 
|  | b->bl = nil; | 
|  | b->nbl = 0; | 
|  | } |