summaryrefslogtreecommitdiff
path: root/libtk/ttags.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /libtk/ttags.c
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'libtk/ttags.c')
-rw-r--r--libtk/ttags.c1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/libtk/ttags.c b/libtk/ttags.c
new file mode 100644
index 00000000..e3e2739e
--- /dev/null
+++ b/libtk/ttags.c
@@ -0,0 +1,1029 @@
+#include "lib9.h"
+#include "draw.h"
+#include "tk.h"
+#include "textw.h"
+
+#define istring u.string
+#define iwin u.win
+#define imark u.mark
+#define iline u.line
+
+static char* tkttagadd(Tk*, char*, char**);
+static char* tkttagbind(Tk*, char*, char**);
+static char* tkttagcget(Tk*, char*, char**);
+static char* tkttagconfigure(Tk*, char*, char**);
+static char* tkttagdelete(Tk*, char*, char**);
+static char* tkttaglower(Tk*, char*, char**);
+static char* tkttagnames(Tk*, char*, char**);
+static char* tkttagnextrange(Tk*, char*, char**);
+static char* tkttagprevrange(Tk*, char*, char**);
+static char* tkttagraise(Tk*, char*, char**);
+static char* tkttagranges(Tk*, char*, char**);
+static char* tkttagremove(Tk*, char*, char**);
+
+#define O(t, e) ((long)(&((t*)0)->e))
+
+#define TKTEO (O(TkTtaginfo, env))
+static
+TkOption tagopts[] =
+{
+ "borderwidth",
+ OPTnndist, O(TkTtaginfo, opts[TkTborderwidth]), nil,
+ "justify",
+ OPTstab, O(TkTtaginfo, opts[TkTjustify]), tkjustify,
+ "lineheight",
+ OPTnndist, O(TkTtaginfo, opts[TkTlineheight]), IAUX(TKTEO),
+ "lmargin1",
+ OPTdist, O(TkTtaginfo, opts[TkTlmargin1]), IAUX(TKTEO),
+ "lmargin2",
+ OPTdist, O(TkTtaginfo, opts[TkTlmargin2]), IAUX(TKTEO),
+ "lmargin3",
+ OPTdist, O(TkTtaginfo, opts[TkTlmargin3]), IAUX(TKTEO),
+ "rmargin",
+ OPTdist, O(TkTtaginfo, opts[TkTrmargin]), IAUX(TKTEO),
+ "spacing1",
+ OPTnndist, O(TkTtaginfo, opts[TkTspacing1]), IAUX(TKTEO),
+ "spacing2",
+ OPTnndist, O(TkTtaginfo, opts[TkTspacing2]), IAUX(TKTEO),
+ "spacing3",
+ OPTnndist, O(TkTtaginfo, opts[TkTspacing3]), IAUX(TKTEO),
+ "offset",
+ OPTdist, O(TkTtaginfo, opts[TkToffset]), IAUX(TKTEO),
+ "underline",
+ OPTstab, O(TkTtaginfo, opts[TkTunderline]), tkbool,
+ "overstrike",
+ OPTstab, O(TkTtaginfo, opts[TkToverstrike]), tkbool,
+ "relief",
+ OPTstab, O(TkTtaginfo, opts[TkTrelief]), tkrelief,
+ "tabs",
+ OPTtabs, O(TkTtaginfo, tabs), IAUX(TKTEO),
+ "wrap",
+ OPTstab, O(TkTtaginfo, opts[TkTwrap]), tkwrap,
+ nil,
+};
+
+static
+TkOption tagenvopts[] =
+{
+ "foreground", OPTcolr, O(TkTtaginfo, env), IAUX(TkCforegnd),
+ "background", OPTcolr, O(TkTtaginfo, env), IAUX(TkCbackgnd),
+ "fg", OPTcolr, O(TkTtaginfo, env), IAUX(TkCforegnd),
+ "bg", OPTcolr, O(TkTtaginfo, env), IAUX(TkCbackgnd),
+ "font", OPTfont, O(TkTtaginfo, env), nil,
+ nil
+};
+
+TkCmdtab
+tkttagcmd[] =
+{
+ "add", tkttagadd,
+ "bind", tkttagbind,
+ "cget", tkttagcget,
+ "configure", tkttagconfigure,
+ "delete", tkttagdelete,
+ "lower", tkttaglower,
+ "names", tkttagnames,
+ "nextrange", tkttagnextrange,
+ "prevrange", tkttagprevrange,
+ "raise", tkttagraise,
+ "ranges", tkttagranges,
+ "remove", tkttagremove,
+ nil
+};
+
+int
+tktanytags(TkTitem *it)
+{
+ int i;
+
+ if(it->tagextra == 0)
+ return (it->tags[0] != 0);
+ for(i = 0; i <= it->tagextra; i++)
+ if(it->tags[i] != 0)
+ return 1;
+ return 0;
+}
+
+int
+tktsametags(TkTitem *i1, TkTitem *i2)
+{
+ int i, j;
+
+ for(i = 0; i <= i1->tagextra && i <= i2->tagextra; i++)
+ if(i1->tags[i] != i2->tags[i])
+ return 0;
+ for(j = i; j <= i1->tagextra; j++)
+ if(i1->tags[j] != 0)
+ return 0;
+ for(j = i; j <= i2->tagextra; j++)
+ if(i2->tags[j] != 0)
+ return 0;
+ return 1;
+}
+
+int
+tkttagset(TkTitem *it, int id)
+{
+ int i;
+
+ if(it->tagextra == 0 && it->tags[0] == 0)
+ return 0;
+ for(i = 0; i <= it->tagextra; i++) {
+ if(id < 32)
+ return ((it->tags[i] & (1<<id)) != 0);
+ id -= 32;
+ }
+ return 0;
+}
+
+char *
+tkttagname(TkText *tkt, int id)
+{
+ TkTtaginfo *t;
+
+ for(t = tkt->tags; t != nil; t = t->next) {
+ if(t->id == id)
+ return t->name;
+ }
+ return "";
+}
+
+/* return 1 if this actually changes the value */
+int
+tkttagbit(TkTitem *it, int id, int val)
+{
+ int i, changed;
+ ulong z, b;
+
+ changed = 0;
+ for(i = 0; i <= it->tagextra; i++) {
+ if(id < 32) {
+ b = (1<<id);
+ z = it->tags[i];
+ if(val == 0) {
+ if(z & b) {
+ changed = 1;
+ it->tags[i] = z & (~b);
+ }
+ }
+ else {
+ if((z & b) == 0) {
+ changed = 1;
+ it->tags[i] = z | b;
+ }
+ }
+ break;
+ }
+ id -= 32;
+ }
+ return changed;
+}
+
+void
+tkttagcomb(TkTitem *i1, TkTitem *i2, int add)
+{
+ int i;
+
+ for(i = 0; i <= i1->tagextra && i <= i2->tagextra; i++) {
+ if(add == 1)
+ i1->tags[i] |= i2->tags[i];
+ else if(add == 0)
+ /* intersect */
+ i1->tags[i] &= i2->tags[i];
+ else
+ /* subtract */
+ i1->tags[i] &= ~i2->tags[i];
+ }
+}
+
+char*
+tktaddtaginfo(Tk *tk, char *name, TkTtaginfo **ret)
+{
+ int i, *ntagp;
+ TkTtaginfo *ti;
+ TkText *tkt, *tktshare;
+
+ tkt = TKobj(TkText, tk);
+ ti = malloc(sizeof(TkTtaginfo));
+ if(ti == nil)
+ return TkNomem;
+
+ ntagp = &tkt->nexttag;
+ if(tkt->tagshare != nil) {
+ tktshare = TKobj(TkText, tkt->tagshare);
+ ntagp = &tktshare->nexttag;
+ }
+ ti->id = *ntagp;
+ ti->name = strdup(name);
+ if(ti->name == nil) {
+ free(ti);
+ return TkNomem;
+ }
+ ti->env = tknewenv(tk->env->top);
+ if(ti->env == nil) {
+ free(ti->name);
+ free(ti);
+ return TkNomem;
+ }
+
+ ti->tabs = nil;
+ for(i = 0; i < TkTnumopts; i++)
+ ti->opts[i] = TkTunset;
+ ti->next = tkt->tags;
+ tkt->tags = ti;
+
+ (*ntagp)++;
+ if(tkt->tagshare)
+ tkt->nexttag = *ntagp;
+
+ *ret = ti;
+ return nil;
+}
+
+TkTtaginfo *
+tktfindtag(TkTtaginfo *t, char *name)
+{
+ while(t != nil) {
+ if(strcmp(t->name, name) == 0)
+ return t;
+ t = t->next;
+ }
+ return nil;
+}
+
+void
+tktfreetags(TkTtaginfo *t)
+{
+ TkTtaginfo *n;
+
+ while(t != nil) {
+ n = t->next;
+ free(t->name);
+ tktfreetabs(t->tabs);
+ tkputenv(t->env);
+ tkfreebind(t->binds);
+ free(t);
+ t = n;
+ }
+}
+
+int
+tkttagind(Tk *tk, char *name, int first, TkTindex *ans)
+{
+ int id;
+ TkTtaginfo *t;
+ TkText *tkt;
+
+ tkt = TKobj(TkText, tk);
+
+ if(strcmp(name, "sel") == 0) {
+ if(tkt->selfirst == nil)
+ return 0;
+ if(first)
+ tktitemind(tkt->selfirst, ans);
+ else
+ tktitemind(tkt->sellast, ans);
+ return 1;
+ }
+
+ t = tktfindtag(tkt->tags, name);
+ if(t == nil)
+ return 0;
+ id = t->id;
+
+ if(first) {
+ tktstartind(tkt, ans);
+ while(!tkttagset(ans->item, id))
+ if(!tktadjustind(tkt, TkTbyitem, ans))
+ return 0;
+ }
+ else {
+ tktendind(tkt, ans);
+ while(!tkttagset(ans->item, id))
+ if(!tktadjustind(tkt, TkTbyitemback, ans))
+ return 0;
+ tktadjustind(tkt, TkTbyitem, ans);
+ }
+
+ return 1;
+}
+
+/*
+ * Fill in opts and e, based on info from tags set in it,
+ * using tags order for priority.
+ * If dflt != 0, options not set are filled from tk,
+ * otherwise iInteger options not set by any tag are left 'TkTunset'
+ * and environment values not set are left nil.
+ */
+void
+tkttagopts(Tk *tk, TkTitem *it, int *opts, TkEnv *e, TkTtabstop **tb, int dflt)
+{
+ int i;
+ int colset;
+ TkEnv *te;
+ TkTtaginfo *tags;
+ TkText *tkt = TKobj(TkText, tk);
+
+ if (tb != nil)
+ *tb = tkt->tabs;
+
+ tags = tkt->tags;
+
+ if(opts != nil)
+ for(i = 0; i < TkTnumopts; i++)
+ opts[i] = TkTunset;
+
+ memset(e, 0, sizeof(TkEnv));
+ e->top = tk->env->top;
+ colset = 0;
+ while(tags != nil) {
+ if(tkttagset(it, tags->id)) {
+ if(opts != nil) {
+ for(i = 0; i < TkTnumopts; i++) {
+ if(opts[i] == TkTunset && tags->opts[i] != TkTunset)
+ opts[i] = tags->opts[i];
+ }
+ }
+
+ te = tags->env;
+ for(i = 0; i < TkNcolor; i++)
+ if(!(colset & (1<<i)) && te->set & (1<<i)) {
+ e->colors[i] = te->colors[i];
+ colset |= 1<<i;
+ }
+
+ if(e->font == nil && te->font != nil)
+ e->font = te->font;
+
+ if (tb != nil && tags->tabs != nil)
+ *tb = tags->tabs;
+ }
+ tags = tags->next;
+ }
+ e->set |= colset;
+ if(dflt) {
+ if(opts != nil) {
+ for(i = 0; i < TkTnumopts; i++)
+ if(opts[i] == TkTunset)
+ opts[i] = tkt->opts[i];
+ }
+ te = tk->env;
+ for(i = 0; i < TkNcolor; i++)
+ if(!(e->set & (1<<i))) {
+ e->colors[i] = te->colors[i];
+ e->set |= 1<<i;
+ }
+ if(e->font == nil)
+ e->font = te->font;
+ }
+}
+
+char*
+tkttagparse(Tk *tk, char **parg, TkTtaginfo **ret)
+{
+ char *e, *buf;
+ TkText *tkt = TKobj(TkText, tk);
+
+ buf = mallocz(Tkmaxitem, 0);
+ if(buf == nil)
+ return TkNomem;
+ *parg = tkword(tk->env->top, *parg, buf, buf+Tkmaxitem, nil);
+ if(*buf == '\0') {
+ free(buf);
+ return TkOparg;
+ }
+ if(buf[0] >= '0' && buf[0] <= '9'){
+ free(buf);
+ return TkBadtg;
+ }
+
+ *ret = tktfindtag(tkt->tags, buf);
+ if(*ret == nil) {
+ e = tktaddtaginfo(tk, buf, ret);
+ if(e != nil) {
+ free(buf);
+ return e;
+ }
+ }
+ free(buf);
+
+ return nil;
+}
+
+int
+tkttagnrange(TkText *tkt, int tid, TkTindex *i1, TkTindex *i2,
+ TkTindex *istart, TkTindex *iend)
+{
+ int found;
+
+ found = 0;
+ while(i1->line != &tkt->end) {
+ if(i1->item == i2->item && i2->pos == 0)
+ break;
+ if(tkttagset(i1->item, tid)) {
+ if(!found) {
+ found = 1;
+ *istart = *i1;
+ }
+ if(i1->item == i2->item) {
+ /* i2->pos > 0 */
+ *iend = *i2;
+ return 1;
+ }
+ }
+ else
+ if(i1->item == i2->item || (found && i1->item->kind != TkTmark && i1->item->kind != TkTcontline))
+ break;
+ tktadjustind(tkt, TkTbyitem, i1);
+ }
+ if(found)
+ *iend = *i1;
+
+ return found;
+}
+
+static int
+tkttagprange(TkText *tkt, int tid, TkTindex *i1, TkTindex *i2,
+ TkTindex *istart, TkTindex *iend)
+{
+ int found;
+
+ found = 0;
+ while(i1->line != &tkt->start && i1->item != i2->item) {
+ tktadjustind(tkt, TkTbyitemback, i1);
+ if(tkttagset(i1->item, tid)) {
+ if(!found) {
+ found = 1;
+ *iend = *i1;
+ }
+ }
+ else
+ if(found && i1->item->kind != TkTmark && i1->item->kind != TkTcontline)
+ break;
+ }
+ if(found) {
+ tktadjustind(tkt, TkTbyitem, i1);
+ *istart = *i1;
+ if(i1->item == i2->item)
+ istart->pos = i2->pos;
+ }
+
+ return found;
+}
+
+/* XXX - Tad: potential memory leak on memory allocation failure */
+char *
+tkttagchange(Tk *tk, int tid, TkTindex *i1, TkTindex *i2, int add)
+{
+ char *e;
+ int samei, nextra, j, changed;
+ TkTline *lmin, *lmax;
+ TkTindex ixprev;
+ TkTitem *nit;
+ TkText *tkt = TKobj(TkText, tk);
+
+ if(!tktindbefore(i1, i2))
+ return nil;
+
+ nextra = tid/32;
+ lmin = nil;
+ lmax = nil;
+ tktadjustind(tkt, TkTbycharstart, i1);
+ tktadjustind(tkt, TkTbycharstart, i2);
+ samei = (i1->item == i2->item);
+ if(i2->pos != 0) {
+ e = tktsplititem(i2);
+ if(e != nil)
+ return e;
+ if(samei) {
+ /* split means i1 should now point to previous item */
+ ixprev = *i2;
+ tktadjustind(tkt, TkTbyitemback, &ixprev);
+ i1->item = ixprev.item;
+ }
+ }
+ if(i1->pos != 0) {
+ e = tktsplititem(i1);
+ if(e != nil)
+ return e;
+ }
+ /* now i1 and i2 both point to beginning of non-mark/contline items */
+ if(tid == TkTselid) {
+ /*
+ * Cache location of selection.
+ * Note: there can be only one selection range in widget
+ */
+ if(add) {
+ if(tkt->selfirst != nil)
+ return TkBadsl;
+ tkt->selfirst = i1->item;
+ tkt->sellast = i2->item;
+ }
+ else {
+ tkt->selfirst = nil;
+ tkt->sellast = nil;
+ }
+ }
+ while(i1->item != i2->item) {
+ if(i1->item->kind != TkTmark && i1->item->kind != TkTcontline) {
+ if(tid >= 32 && i1->item->tagextra < nextra) {
+ nit = realloc(i1->item, sizeof(TkTitem) + nextra * sizeof(long));
+ if(nit == nil)
+ return TkNomem;
+ for(j = nit->tagextra+1; j <= nextra; j++)
+ nit->tags[j] = 0;
+ nit->tagextra = nextra;
+ if(i1->line->items == i1->item)
+ i1->line->items = nit;
+ else {
+ ixprev = *i1;
+ tktadjustind(tkt, TkTbyitemback, &ixprev);
+ ixprev.item->next = nit;
+ }
+ /* check nit against cached items */
+ if(tkt->selfirst == i1->item)
+ tkt->selfirst = nit;
+ if(tkt->sellast == i1->item)
+ tkt->sellast = nit;
+ i1->item = nit;
+ }
+ changed = tkttagbit(i1->item, tid, add);
+ if(lmin == nil) {
+ if(changed) {
+ lmin = i1->line;
+ lmax = lmin;
+ }
+ }
+ else {
+ if(changed)
+ lmax = i1->line;
+ }
+ }
+ if(!tktadjustind(tkt, TkTbyitem, i1))
+ break;
+ }
+ if(lmin != nil) {
+ tktfixgeom(tk, tktprevwrapline(tk, lmin), lmax, 0);
+ tktextsize(tk, 1);
+ }
+ return nil;
+}
+
+static char*
+tkttagaddrem(Tk *tk, char *arg, int add)
+{
+ char *e;
+ TkText *tkt;
+ TkTtaginfo *ti;
+ TkTindex ix1, ix2;
+
+ tkt = TKobj(TkText, tk);
+
+ e = tkttagparse(tk, &arg, &ti);
+ if(e != nil)
+ return e;
+
+ while(*arg != '\0') {
+ e = tktindparse(tk, &arg, &ix1);
+ if(e != nil)
+ return e;
+ if(*arg != '\0') {
+ e = tktindparse(tk, &arg, &ix2);
+ if(e != nil)
+ return e;
+ }
+ else {
+ ix2 = ix1;
+ tktadjustind(tkt, TkTbychar, &ix2);
+ }
+ if(!tktindbefore(&ix1, &ix2))
+ continue;
+
+ e = tkttagchange(tk, ti->id, &ix1, &ix2, add);
+ if(e != nil)
+ return e;
+ }
+
+ return nil;
+}
+
+
+/* Text Tag Command (+ means implemented)
+ +add
+ +bind
+ +cget
+ +configure
+ +delete
+ +lower
+ +names
+ +nextrange
+ +prevrange
+ +raise
+ +ranges
+ +remove
+*/
+
+static char*
+tkttagadd(Tk *tk, char *arg, char **val)
+{
+ USED(val);
+
+ return tkttagaddrem(tk, arg, 1);
+}
+
+static char*
+tkttagbind(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ Rune r;
+ TkTtaginfo *ti;
+ TkAction *a;
+ int event, mode;
+ char *cmd, buf[Tkmaxitem];
+
+
+ e = tkttagparse(tk, &arg, &ti);
+ if(e != nil)
+ return e;
+
+ arg = tkskip(arg, " \t");
+ if (arg[0] == '\0')
+ return TkBadsq;
+ arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
+ if(buf[0] == '<') {
+ event = tkseqparse(buf+1);
+ if(event == -1)
+ return TkBadsq;
+ }
+ else {
+ chartorune(&r, buf);
+ event = TkKey | r;
+ }
+ if(event == 0)
+ return TkBadsq;
+
+ arg = tkskip(arg, " \t");
+ if(*arg == '\0') {
+ for(a = ti->binds; a; a = a->link)
+ if(event == a->event)
+ return tkvalue(val, "%s", a->arg);
+ return nil;
+ }
+
+ mode = TkArepl;
+ if(*arg == '+') {
+ mode = TkAadd;
+ arg++;
+ }
+ else if(*arg == '-'){
+ mode = TkAsub;
+ arg++;
+ }
+
+ tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
+ cmd = strdup(buf);
+ if(cmd == nil)
+ return TkNomem;
+ return tkaction(&ti->binds, event, TkDynamic, cmd, mode);
+}
+
+static char*
+tkttagcget(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkTtaginfo *ti;
+ TkOptab tko[3];
+
+ e = tkttagparse(tk, &arg, &ti);
+ if(e != nil)
+ return e;
+
+ tko[0].ptr = ti;
+ tko[0].optab = tagopts;
+ tko[1].ptr = ti;
+ tko[1].optab = tagenvopts;
+ tko[2].ptr = nil;
+
+ return tkgencget(tko, arg, val, tk->env->top);
+}
+
+static char*
+tkttagconfigure(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkOptab tko[3];
+ TkTtaginfo *ti;
+ TkTindex ix;
+ TkText *tkt = TKobj(TkText, tk);
+
+ USED(val);
+
+ e = tkttagparse(tk, &arg, &ti);
+ if(e != nil)
+ return e;
+
+ tko[0].ptr = ti;
+ tko[0].optab = tagopts;
+ tko[1].ptr = ti;
+ tko[1].optab = tagenvopts;
+ tko[2].ptr = nil;
+
+ e = tkparse(tk->env->top, arg, tko, nil);
+ if(e != nil)
+ return e;
+
+ if(tkttagind(tk, ti->name, 1, &ix)) {
+ tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
+ tktextsize(tk, 1);
+ }
+
+ return nil;
+}
+
+static void
+tktunlinktag(TkText *tkt, TkTtaginfo *t)
+{
+ TkTtaginfo *f, **l;
+
+ l = &tkt->tags;
+ for(f = *l; f != nil; f = f->next) {
+ if(f == t) {
+ *l = t->next;
+ return;
+ }
+ l = &f->next;
+ }
+}
+
+static char*
+tkttagdelete(Tk *tk, char *arg, char **val)
+{
+ TkText *tkt;
+ TkTtaginfo *t;
+ TkTindex ix;
+ char *e;
+ int found;
+
+ USED(val);
+
+ tkt = TKobj(TkText, tk);
+
+ e = tkttagparse(tk, &arg, &t);
+ if(e != nil)
+ return e;
+
+ found = 0;
+ while(t != nil) {
+ if(t->id == TkTselid)
+ return TkBadvl;
+
+ while(tkttagind(tk, t->name, 1, &ix)) {
+ found = 1;
+ tkttagbit(ix.item, t->id, 0);
+ }
+
+ tktunlinktag(tkt, t);
+ t->next = nil;
+ tktfreetags(t);
+
+ if(*arg != '\0') {
+ e = tkttagparse(tk, &arg, &t);
+ if(e != nil)
+ return e;
+ }
+ else
+ t = nil;
+ }
+ if (found) {
+ tktfixgeom(tk, &tkt->start, tkt->end.prev, 0);
+ tktextsize(tk, 1);
+ }
+
+ return nil;
+}
+
+static char*
+tkttaglower(Tk *tk, char *arg, char **val)
+{
+ TkText *tkt;
+ TkTindex ix;
+ TkTtaginfo *t, *tbelow, *f, **l;
+ char *e;
+
+ USED(val);
+
+ tkt = TKobj(TkText, tk);
+
+ e = tkttagparse(tk, &arg, &t);
+ if(e != nil)
+ return e;
+
+ if(*arg != '\0') {
+ e = tkttagparse(tk, &arg, &tbelow);
+ if(e != nil)
+ return e;
+ }
+ else
+ tbelow = nil;
+
+ tktunlinktag(tkt, t);
+
+ if(tbelow != nil) {
+ t->next = tbelow->next;
+ tbelow->next = t;
+ }
+ else {
+ l = &tkt->tags;
+ for(f = *l; f != nil; f = f->next)
+ l = &f->next;
+ *l = t;
+ t->next = nil;
+ }
+ if(tkttagind(tk, t->name, 1, &ix)) {
+ tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
+ tktextsize(tk, 1);
+ }
+
+ return nil;
+}
+
+
+static char*
+tkttagnames(Tk *tk, char *arg, char **val)
+{
+ char *e, *r, *fmt;
+ TkTtaginfo *t;
+ TkTindex i;
+ TkText *tkt = TKobj(TkText, tk);
+ TkTitem *tagit;
+
+ if(*arg != '\0') {
+ e = tktindparse(tk, &arg, &i);
+ if(e != nil)
+ return e;
+ /* make sure we're actually on a character */
+ tktadjustind(tkt, TkTbycharstart, &i);
+ tagit = i.item;
+ }
+ else
+ tagit = nil;
+
+ /* generate in order highest-to-lowest priority (contrary to spec) */
+ fmt = "%s";
+ for(t = tkt->tags; t != nil; t = t->next) {
+ if(tagit == nil || tkttagset(tagit, t->id)) {
+ r = tkvalue(val, fmt, t->name);
+ if(r != nil)
+ return r;
+ fmt = " %s";
+ }
+ }
+ return nil;
+}
+
+static char*
+tkttagnextrange(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkTtaginfo *t;
+ TkTindex i1, i2, istart, iend;
+ TkText *tkt = TKobj(TkText, tk);
+
+ e = tkttagparse(tk, &arg, &t);
+ if(e != nil)
+ return e;
+ e = tktindparse(tk, &arg, &i1);
+ if(e != nil)
+ return e;
+ if(*arg != '\0') {
+ e = tktindparse(tk, &arg, &i2);
+ if(e != nil)
+ return e;
+ }
+ else
+ tktendind(tkt, &i2);
+
+ if(tkttagnrange(tkt, t->id, &i1, &i2, &istart, &iend))
+ return tkvalue(val, "%d.%d %d.%d",
+ tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
+ tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
+
+ return nil;
+}
+
+static char*
+tkttagprevrange(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkTtaginfo *t;
+ TkTindex i1, i2, istart, iend;
+ TkText *tkt = TKobj(TkText, tk);
+
+ e = tkttagparse(tk, &arg, &t);
+ if(e != nil)
+ return e;
+ e = tktindparse(tk, &arg, &i1);
+ if(e != nil)
+ return e;
+ if(*arg != '\0') {
+ e = tktindparse(tk, &arg, &i2);
+ if(e != nil)
+ return e;
+ }
+ else
+ tktstartind(tkt, &i2);
+
+ if(tkttagprange(tkt, t->id, &i1, &i2, &istart, &iend))
+ return tkvalue(val, "%d.%d %d.%d",
+ tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
+ tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
+
+ return nil;
+}
+
+static char*
+tkttagraise(Tk *tk, char *arg, char **val)
+{
+ TkText *tkt;
+ TkTindex ix;
+ TkTtaginfo *t, *tabove, *f, **l;
+ char *e;
+
+ USED(val);
+
+ tkt = TKobj(TkText, tk);
+
+ e = tkttagparse(tk, &arg, &t);
+ if(e != nil)
+ return e;
+
+ if(*arg != '\0') {
+ e = tkttagparse(tk, &arg, &tabove);
+ if(e != nil)
+ return e;
+ }
+ else
+ tabove = nil;
+
+ tktunlinktag(tkt, t);
+
+ if(tabove != nil) {
+ l = &tkt->tags;
+ for(f = *l; f != nil; f = f->next) {
+ if(f == tabove) {
+ *l = t;
+ t->next = tabove;
+ break;
+ }
+ l = &f->next;
+ }
+ }
+ else {
+ t->next = tkt->tags;
+ tkt->tags = t;
+ }
+
+ if(tkttagind(tk, t->name, 1, &ix)) {
+ tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
+ tktextsize(tk, 1);
+ }
+ return nil;
+}
+
+static char*
+tkttagranges(Tk *tk, char *arg, char **val)
+{
+ char *e, *fmt;
+ TkTtaginfo *t;
+ TkTindex i1, i2, istart, iend;
+ TkText *tkt = TKobj(TkText, tk);
+
+ e = tkttagparse(tk, &arg, &t);
+ if(e != nil)
+ return e;
+
+ tktstartind(tkt, &i1);
+ tktendind(tkt, &i2);
+
+ fmt = "%d.%d %d.%d";
+ while(tkttagnrange(tkt, t->id, &i1, &i2, &istart, &iend)) {
+ e = tkvalue(val, fmt,
+ tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
+ tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
+ if(e != nil)
+ return e;
+
+ fmt = " %d.%d %d.%d";
+ i1 = iend;
+ }
+
+ return nil;
+}
+
+static char*
+tkttagremove(Tk *tk, char *arg, char **val)
+{
+ USED(val);
+
+ return tkttagaddrem(tk, arg, 0);
+}