#include #include #include #include #include #include #include #include #include #include <9p.h> #include "gui.h" /* CAUTION: * Tblock routines always measures sizes in runes. * Below, pos means a position measured in runes, * buf off means an offset measured in (UTF8) bytes. * * Iteration is usually done with blookseek and * routines accepting block, n, ... args. There, n * is the position within a Tblock node. * For complex navigation, packblock() ensures that * all the block is contiguous. */ enum { Ndump = 30, Nmin = 1024, }; int blockdebug; int Tfmt(Fmt* f) { Tblock* b; b = va_arg(f->args, Tblock*); if (b == nil) return fmtprint(f, "\n"); else{ return fmtprint(f, "block: %d of %d runes [%.*S%s]\n", b->nr, b->ar, b->nr > Ndump ? Ndump : b->nr, b->r, b->nr > Ndump ? "..." : ""); } } void blockdump(Tblock* b) { int i; for(i = 0; b; i++){ print("%p:%d %T\n", b->block0, i, b); b = b->next; } print("\n"); } Tblock* newblock(Rune* r, int nr) { Tblock* b; b = emalloc9p(sizeof(Tblock)); memset(b, 0, sizeof(Tblock)); b->block0 = b; if (r && !nr){ free(r); r = nil; } if (r){ b->r = r; b->nr= b->ar = nr; } else { b->nr = 0; b->ar = Nblock; b->r = emalloc9p(Nblock*sizeof(Rune)); } return b; } void blockfree(Tblock* b) { Tblock* nb; while(b){ nb = b; b = b->next; assert(nb->ar || nb->r == nil); free(nb->r); free(nb); } } long blocklen(Tblock* b) { long l; l = 0; while(b){ assert(b->nr <= b->ar); l += b->nr; b = b->next; } return l; } void packblock(Tblock* b) { Tblock* nb; long l; if (b->next == nil) return; l = blocklen(b); if (l >= b->ar){ b->r = erealloc9p(b->r, (l + Nblock) * sizeof(Rune)); b->ar= l + Nblock; } for(nb = b->next; nb != nil; nb = nb->next){ if (nb->nr > 0){ memmove(&b->r[b->nr], nb->r, nb->nr*sizeof(Rune)); b->nr += nb->nr; } } b->r[b->nr] = 0; blockfree(b->next); b->next = nil; } long blocksize(Tblock* b) { long l; l = 0; while(b){ if (b->nr) l += runenlen(b->r, b->nr); b = b->next; } return l; } long pos2off(Tblock* b, long pos) { long n, i; if (pos < 0) return pos; for(n = 0; pos > 0 && b != nil; b = b->next) for (i = 0; pos > 0 && i < b->nr; i++){ n += runelen(b->r[i]); pos--; } return n; } long off2pos(Tblock* b, long off) { long n, i; if (off < 0) return off; for(n = 0; off > 0 && b != nil; b = b->next) for (i = 0; off > 0 && i < b->nr; i++){ off -= runelen(b->r[i]); n++; } return n; } /* It's ok to seek to blocklen(b). That places us * right after the last valid rune. * It's not ok to seek past that value. */ Tblock* blockseek(Tblock* b, int *np, int off) { Tblock**lb; assert(off >= 0); bdprint("blockseek %d →", off); lb = nil; do { if (b->nr == 0 && lb){ *lb = b->next; free(b->r); free(b); b = *lb; continue; } if (off <= b->nr) break; off -= b->nr; lb = &b->next; b = b->next; } while(b); *np = off; while(b && lb && b->nr == 0){ *lb = b->next; free(b->r); free(b); b = *lb; } assert(!b || (*np >= 0 && *np <= b->nr)); // i == b->nr is ok. bdprint("#%d of %T\n", off, b); return b; } static void _blockget(Tblock* b, int off, int len, Rune* rp, int del) { int edel; int ndel; int n; assert(off >= 0 && off <= b->ar); bdprint("_blockget %d %d from %T\n", off, len, b); do { edel = off + len; assert(edel > 0); if (edel > b->nr) edel = b->nr; n = b->nr - edel; ndel = edel - off; if (ndel > 0 && rp){ memmove(rp, b->r + off, ndel*sizeof(Rune)); rp += ndel; } if (del){ if (n > 0) memmove(b->r + off, b->r + edel, n*sizeof(Rune)); b->nr -= ndel; } len -= ndel; off = 0; b = b->next; } while(b && len > 0); } void blockget(Tblock* b, int n, int len, Rune* rp) { if (len) _blockget(b, n, len, rp, 0); } static Rune* putinblock(Tblock* b, int off, Rune* r, int len) { int ntrail; assert(b->nr + len <= b->ar); ntrail = b->nr - off; if (ntrail > 0) memmove(b->r + (off + len), b->r + off, ntrail*sizeof(Rune)); memmove(b->r + off, r, len*sizeof(Rune)); b->nr += len; return r + len; } static void split(Tblock* b, int off) { Tblock* t; Rune* r; int nr; assert(off >= 0 && off <= b->ar); if (b->nr > off){ nr = b->nr - off; if (nr < Nmin) nr = Nmin; r = emalloc9p(nr * sizeof(Rune)); memmove(r, b->r + off, (b->nr - off) * sizeof(Rune)); t = newblock(r, b->nr - off); t->ar = nr; t->block0 = b->block0; b->nr = off; t->next = b->next; b->next = t; } } void blockins(Tblock* b, int off, Rune* r, int len) { int n; Tblock* t; Rune* ar; int an; assert(off >= 0 && off <= b->nr); bdprint("blockins %d [%.*S] into %T\n", off, b->nr, r, b); assert(len > 0); if (b->nr + len <= b->ar){ putinblock(b, off, r, len); return; } split(b, off); do { n = len; if (b->nr == 0 && b->ar){ if (n > b->ar) n = b->ar; r = putinblock(b, 0, r, n); } else { if (n < Nmin) an = Nmin; else an = n; ar = emalloc9p(an * sizeof(Rune)); memmove(ar, r, n * sizeof(Rune)); t = newblock(ar, n); t->ar = an; t->block0 = b->block0; t->next = b->next; b->next = t; b = t; } len -= n; } while(len > 0); } void blockcat(Tblock* b, Tblock* nb) { blockfree(b->next); b->next = nb; while(nb){ nb->block0 = b->block0; nb = nb->next; } } void blockdel(Tblock* b, int n, int len, Rune* rp) { _blockget(b, n, len, rp, 1); } Tblock* str2block(char* s) { Rune* r; int nr; r = utf2runes(s, nil, nil); nr= runeslen(r); return newblock(r, nr); } char* block2str(Tblock* bl, long* l) { char* s; char* bs; int n; Tblock* b; int i; long nc, nr; n = blocksize(bl); s = malloc(n+1); s[n] = 0; bs = s; nc = 0; for(b = bl; b != nil; b = b->next){ for (i = 0; i < b->nr; i++){ nr = runetochar(bs, b->r+i); bs += nr; nc += nr; } } *bs = 0; if (l) *l = nc; return s; } Rune* utf2runes(char* s, int* lp, int* nlines) { Rune* r; int n; int l; int wc; char* se; int lc; if (!lp) l = utflen(s); else { if (*lp) l = utfnlen(s, *lp); else l = utflen(s); *lp = 0; } if (nlines) *nlines = 0; r = emalloc9p(sizeof(Rune)*(l+1)); setmalloctag(r, getcallerpc(&s)); se = s; lc = 0; for(n = 0; n < l; n++){ wc = chartorune(r+n, se); se += wc; if ((r[n] == '\n' || ++lc > 80) && nlines){ lc = 0; (*nlines)++; } } r[l] = 0; if (lp) *lp = se - s; return r; } int runeslen(Rune* r) { int n; for (n = 0; *r; r++, n++) ; return n; }