1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 #include "textw.h"
5
6 #define istring u.string
7 #define iwin u.win
8 #define imark u.mark
9 #define iline u.line
10
11 static char* tkttagadd(Tk*, char*, char**);
12 static char* tkttagbind(Tk*, char*, char**);
13 static char* tkttagcget(Tk*, char*, char**);
14 static char* tkttagconfigure(Tk*, char*, char**);
15 static char* tkttagdelete(Tk*, char*, char**);
16 static char* tkttaglower(Tk*, char*, char**);
17 static char* tkttagnames(Tk*, char*, char**);
18 static char* tkttagnextrange(Tk*, char*, char**);
19 static char* tkttagprevrange(Tk*, char*, char**);
20 static char* tkttagraise(Tk*, char*, char**);
21 static char* tkttagranges(Tk*, char*, char**);
22 static char* tkttagremove(Tk*, char*, char**);
23
24 #define O(t, e) ((long)(&((t*)0)->e))
25
26 #define TKTEO (O(TkTtaginfo, env))
27 static
28 TkOption tagopts[] =
29 {
30 "borderwidth",
31 OPTnndist, O(TkTtaginfo, opts[TkTborderwidth]), nil,
32 "justify",
33 OPTstab, O(TkTtaginfo, opts[TkTjustify]), tkjustify,
34 "lineheight",
35 OPTnndist, O(TkTtaginfo, opts[TkTlineheight]), IAUX(TKTEO),
36 "lmargin1",
37 OPTdist, O(TkTtaginfo, opts[TkTlmargin1]), IAUX(TKTEO),
38 "lmargin2",
39 OPTdist, O(TkTtaginfo, opts[TkTlmargin2]), IAUX(TKTEO),
40 "lmargin3",
41 OPTdist, O(TkTtaginfo, opts[TkTlmargin3]), IAUX(TKTEO),
42 "rmargin",
43 OPTdist, O(TkTtaginfo, opts[TkTrmargin]), IAUX(TKTEO),
44 "spacing1",
45 OPTnndist, O(TkTtaginfo, opts[TkTspacing1]), IAUX(TKTEO),
46 "spacing2",
47 OPTnndist, O(TkTtaginfo, opts[TkTspacing2]), IAUX(TKTEO),
48 "spacing3",
49 OPTnndist, O(TkTtaginfo, opts[TkTspacing3]), IAUX(TKTEO),
50 "offset",
51 OPTdist, O(TkTtaginfo, opts[TkToffset]), IAUX(TKTEO),
52 "underline",
53 OPTstab, O(TkTtaginfo, opts[TkTunderline]), tkbool,
54 "overstrike",
55 OPTstab, O(TkTtaginfo, opts[TkToverstrike]), tkbool,
56 "relief",
57 OPTstab, O(TkTtaginfo, opts[TkTrelief]), tkrelief,
58 "tabs",
59 OPTtabs, O(TkTtaginfo, tabs), IAUX(TKTEO),
60 "wrap",
61 OPTstab, O(TkTtaginfo, opts[TkTwrap]), tkwrap,
62 nil,
63 };
64
65 static
66 TkOption tagenvopts[] =
67 {
68 "foreground", OPTcolr, O(TkTtaginfo, env), IAUX(TkCforegnd),
69 "background", OPTcolr, O(TkTtaginfo, env), IAUX(TkCbackgnd),
70 "fg", OPTcolr, O(TkTtaginfo, env), IAUX(TkCforegnd),
71 "bg", OPTcolr, O(TkTtaginfo, env), IAUX(TkCbackgnd),
72 "font", OPTfont, O(TkTtaginfo, env), nil,
73 nil
74 };
75
76 TkCmdtab
77 tkttagcmd[] =
78 {
79 "add", tkttagadd,
80 "bind", tkttagbind,
81 "cget", tkttagcget,
82 "configure", tkttagconfigure,
83 "delete", tkttagdelete,
84 "lower", tkttaglower,
85 "names", tkttagnames,
86 "nextrange", tkttagnextrange,
87 "prevrange", tkttagprevrange,
88 "raise", tkttagraise,
89 "ranges", tkttagranges,
90 "remove", tkttagremove,
91 nil
92 };
93
94 int
tktanytags(TkTitem * it)95 tktanytags(TkTitem *it)
96 {
97 int i;
98
99 if(it->tagextra == 0)
100 return (it->tags[0] != 0);
101 for(i = 0; i <= it->tagextra; i++)
102 if(it->tags[i] != 0)
103 return 1;
104 return 0;
105 }
106
107 int
tktsametags(TkTitem * i1,TkTitem * i2)108 tktsametags(TkTitem *i1, TkTitem *i2)
109 {
110 int i, j;
111
112 for(i = 0; i <= i1->tagextra && i <= i2->tagextra; i++)
113 if(i1->tags[i] != i2->tags[i])
114 return 0;
115 for(j = i; j <= i1->tagextra; j++)
116 if(i1->tags[j] != 0)
117 return 0;
118 for(j = i; j <= i2->tagextra; j++)
119 if(i2->tags[j] != 0)
120 return 0;
121 return 1;
122 }
123
124 int
tkttagset(TkTitem * it,int id)125 tkttagset(TkTitem *it, int id)
126 {
127 int i;
128
129 if(it->tagextra == 0 && it->tags[0] == 0)
130 return 0;
131 for(i = 0; i <= it->tagextra; i++) {
132 if(id < 32)
133 return ((it->tags[i] & (1<<id)) != 0);
134 id -= 32;
135 }
136 return 0;
137 }
138
139 char *
tkttagname(TkText * tkt,int id)140 tkttagname(TkText *tkt, int id)
141 {
142 TkTtaginfo *t;
143
144 for(t = tkt->tags; t != nil; t = t->next) {
145 if(t->id == id)
146 return t->name;
147 }
148 return "";
149 }
150
151 /* return 1 if this actually changes the value */
152 int
tkttagbit(TkTitem * it,int id,int val)153 tkttagbit(TkTitem *it, int id, int val)
154 {
155 int i, changed;
156 ulong z, b;
157
158 changed = 0;
159 for(i = 0; i <= it->tagextra; i++) {
160 if(id < 32) {
161 b = (1<<id);
162 z = it->tags[i];
163 if(val == 0) {
164 if(z & b) {
165 changed = 1;
166 it->tags[i] = z & (~b);
167 }
168 }
169 else {
170 if((z & b) == 0) {
171 changed = 1;
172 it->tags[i] = z | b;
173 }
174 }
175 break;
176 }
177 id -= 32;
178 }
179 return changed;
180 }
181
182 void
tkttagcomb(TkTitem * i1,TkTitem * i2,int add)183 tkttagcomb(TkTitem *i1, TkTitem *i2, int add)
184 {
185 int i;
186
187 for(i = 0; i <= i1->tagextra && i <= i2->tagextra; i++) {
188 if(add == 1)
189 i1->tags[i] |= i2->tags[i];
190 else if(add == 0)
191 /* intersect */
192 i1->tags[i] &= i2->tags[i];
193 else
194 /* subtract */
195 i1->tags[i] &= ~i2->tags[i];
196 }
197 }
198
199 char*
tktaddtaginfo(Tk * tk,char * name,TkTtaginfo ** ret)200 tktaddtaginfo(Tk *tk, char *name, TkTtaginfo **ret)
201 {
202 int i, *ntagp;
203 TkTtaginfo *ti;
204 TkText *tkt, *tktshare;
205
206 tkt = TKobj(TkText, tk);
207 ti = malloc(sizeof(TkTtaginfo));
208 if(ti == nil)
209 return TkNomem;
210
211 ntagp = &tkt->nexttag;
212 if(tkt->tagshare != nil) {
213 tktshare = TKobj(TkText, tkt->tagshare);
214 ntagp = &tktshare->nexttag;
215 }
216 ti->id = *ntagp;
217 ti->name = strdup(name);
218 if(ti->name == nil) {
219 free(ti);
220 return TkNomem;
221 }
222 ti->env = tknewenv(tk->env->top);
223 if(ti->env == nil) {
224 free(ti->name);
225 free(ti);
226 return TkNomem;
227 }
228
229 ti->tabs = nil;
230 for(i = 0; i < TkTnumopts; i++)
231 ti->opts[i] = TkTunset;
232 ti->next = tkt->tags;
233 tkt->tags = ti;
234
235 (*ntagp)++;
236 if(tkt->tagshare)
237 tkt->nexttag = *ntagp;
238
239 *ret = ti;
240 return nil;
241 }
242
243 TkTtaginfo *
tktfindtag(TkTtaginfo * t,char * name)244 tktfindtag(TkTtaginfo *t, char *name)
245 {
246 while(t != nil) {
247 if(strcmp(t->name, name) == 0)
248 return t;
249 t = t->next;
250 }
251 return nil;
252 }
253
254 void
tktfreetags(TkTtaginfo * t)255 tktfreetags(TkTtaginfo *t)
256 {
257 TkTtaginfo *n;
258
259 while(t != nil) {
260 n = t->next;
261 free(t->name);
262 tktfreetabs(t->tabs);
263 tkputenv(t->env);
264 tkfreebind(t->binds);
265 free(t);
266 t = n;
267 }
268 }
269
270 int
tkttagind(Tk * tk,char * name,int first,TkTindex * ans)271 tkttagind(Tk *tk, char *name, int first, TkTindex *ans)
272 {
273 int id;
274 TkTtaginfo *t;
275 TkText *tkt;
276
277 tkt = TKobj(TkText, tk);
278
279 if(strcmp(name, "sel") == 0) {
280 if(tkt->selfirst == nil)
281 return 0;
282 if(first)
283 tktitemind(tkt->selfirst, ans);
284 else
285 tktitemind(tkt->sellast, ans);
286 return 1;
287 }
288
289 t = tktfindtag(tkt->tags, name);
290 if(t == nil)
291 return 0;
292 id = t->id;
293
294 if(first) {
295 tktstartind(tkt, ans);
296 while(!tkttagset(ans->item, id))
297 if(!tktadjustind(tkt, TkTbyitem, ans))
298 return 0;
299 }
300 else {
301 tktendind(tkt, ans);
302 while(!tkttagset(ans->item, id))
303 if(!tktadjustind(tkt, TkTbyitemback, ans))
304 return 0;
305 tktadjustind(tkt, TkTbyitem, ans);
306 }
307
308 return 1;
309 }
310
311 /*
312 * Fill in opts and e, based on info from tags set in it,
313 * using tags order for priority.
314 * If dflt != 0, options not set are filled from tk,
315 * otherwise iInteger options not set by any tag are left 'TkTunset'
316 * and environment values not set are left nil.
317 */
318 void
tkttagopts(Tk * tk,TkTitem * it,int * opts,TkEnv * e,TkTtabstop ** tb,int dflt)319 tkttagopts(Tk *tk, TkTitem *it, int *opts, TkEnv *e, TkTtabstop **tb, int dflt)
320 {
321 int i;
322 int colset;
323 TkEnv *te;
324 TkTtaginfo *tags;
325 TkText *tkt = TKobj(TkText, tk);
326
327 if (tb != nil)
328 *tb = tkt->tabs;
329
330 tags = tkt->tags;
331
332 if(opts != nil)
333 for(i = 0; i < TkTnumopts; i++)
334 opts[i] = TkTunset;
335
336 memset(e, 0, sizeof(TkEnv));
337 e->top = tk->env->top;
338 colset = 0;
339 while(tags != nil) {
340 if(tkttagset(it, tags->id)) {
341 if(opts != nil) {
342 for(i = 0; i < TkTnumopts; i++) {
343 if(opts[i] == TkTunset && tags->opts[i] != TkTunset)
344 opts[i] = tags->opts[i];
345 }
346 }
347
348 te = tags->env;
349 for(i = 0; i < TkNcolor; i++)
350 if(!(colset & (1<<i)) && te->set & (1<<i)) {
351 e->colors[i] = te->colors[i];
352 colset |= 1<<i;
353 }
354
355 if(e->font == nil && te->font != nil)
356 e->font = te->font;
357
358 if (tb != nil && tags->tabs != nil)
359 *tb = tags->tabs;
360 }
361 tags = tags->next;
362 }
363 e->set |= colset;
364 if(dflt) {
365 if(opts != nil) {
366 for(i = 0; i < TkTnumopts; i++)
367 if(opts[i] == TkTunset)
368 opts[i] = tkt->opts[i];
369 }
370 te = tk->env;
371 for(i = 0; i < TkNcolor; i++)
372 if(!(e->set & (1<<i))) {
373 e->colors[i] = te->colors[i];
374 e->set |= 1<<i;
375 }
376 if(e->font == nil)
377 e->font = te->font;
378 }
379 }
380
381 char*
tkttagparse(Tk * tk,char ** parg,TkTtaginfo ** ret)382 tkttagparse(Tk *tk, char **parg, TkTtaginfo **ret)
383 {
384 char *e, *buf;
385 TkText *tkt = TKobj(TkText, tk);
386
387 buf = mallocz(Tkmaxitem, 0);
388 if(buf == nil)
389 return TkNomem;
390 *parg = tkword(tk->env->top, *parg, buf, buf+Tkmaxitem, nil);
391 if(*buf == '\0') {
392 free(buf);
393 return TkOparg;
394 }
395 if(buf[0] >= '0' && buf[0] <= '9'){
396 free(buf);
397 return TkBadtg;
398 }
399
400 *ret = tktfindtag(tkt->tags, buf);
401 if(*ret == nil) {
402 e = tktaddtaginfo(tk, buf, ret);
403 if(e != nil) {
404 free(buf);
405 return e;
406 }
407 }
408 free(buf);
409
410 return nil;
411 }
412
413 int
tkttagnrange(TkText * tkt,int tid,TkTindex * i1,TkTindex * i2,TkTindex * istart,TkTindex * iend)414 tkttagnrange(TkText *tkt, int tid, TkTindex *i1, TkTindex *i2,
415 TkTindex *istart, TkTindex *iend)
416 {
417 int found;
418
419 found = 0;
420 while(i1->line != &tkt->end) {
421 if(i1->item == i2->item && i2->pos == 0)
422 break;
423 if(tkttagset(i1->item, tid)) {
424 if(!found) {
425 found = 1;
426 *istart = *i1;
427 }
428 if(i1->item == i2->item) {
429 /* i2->pos > 0 */
430 *iend = *i2;
431 return 1;
432 }
433 }
434 else
435 if(i1->item == i2->item || (found && i1->item->kind != TkTmark && i1->item->kind != TkTcontline))
436 break;
437 tktadjustind(tkt, TkTbyitem, i1);
438 }
439 if(found)
440 *iend = *i1;
441
442 return found;
443 }
444
445 static int
tkttagprange(TkText * tkt,int tid,TkTindex * i1,TkTindex * i2,TkTindex * istart,TkTindex * iend)446 tkttagprange(TkText *tkt, int tid, TkTindex *i1, TkTindex *i2,
447 TkTindex *istart, TkTindex *iend)
448 {
449 int found;
450
451 found = 0;
452 while(i1->line != &tkt->start && i1->item != i2->item) {
453 tktadjustind(tkt, TkTbyitemback, i1);
454 if(tkttagset(i1->item, tid)) {
455 if(!found) {
456 found = 1;
457 *iend = *i1;
458 }
459 }
460 else
461 if(found && i1->item->kind != TkTmark && i1->item->kind != TkTcontline)
462 break;
463 }
464 if(found) {
465 tktadjustind(tkt, TkTbyitem, i1);
466 *istart = *i1;
467 if(i1->item == i2->item)
468 istart->pos = i2->pos;
469 }
470
471 return found;
472 }
473
474 /* XXX - Tad: potential memory leak on memory allocation failure */
475 char *
tkttagchange(Tk * tk,int tid,TkTindex * i1,TkTindex * i2,int add)476 tkttagchange(Tk *tk, int tid, TkTindex *i1, TkTindex *i2, int add)
477 {
478 char *e;
479 int samei, nextra, j, changed;
480 TkTline *lmin, *lmax;
481 TkTindex ixprev;
482 TkTitem *nit;
483 TkText *tkt = TKobj(TkText, tk);
484
485 if(!tktindbefore(i1, i2))
486 return nil;
487
488 nextra = tid/32;
489 lmin = nil;
490 lmax = nil;
491 tktadjustind(tkt, TkTbycharstart, i1);
492 tktadjustind(tkt, TkTbycharstart, i2);
493 samei = (i1->item == i2->item);
494 if(i2->pos != 0) {
495 e = tktsplititem(i2);
496 if(e != nil)
497 return e;
498 if(samei) {
499 /* split means i1 should now point to previous item */
500 ixprev = *i2;
501 tktadjustind(tkt, TkTbyitemback, &ixprev);
502 i1->item = ixprev.item;
503 }
504 }
505 if(i1->pos != 0) {
506 e = tktsplititem(i1);
507 if(e != nil)
508 return e;
509 }
510 /* now i1 and i2 both point to beginning of non-mark/contline items */
511 if(tid == TkTselid) {
512 /*
513 * Cache location of selection.
514 * Note: there can be only one selection range in widget
515 */
516 if(add) {
517 if(tkt->selfirst != nil)
518 return TkBadsl;
519 tkt->selfirst = i1->item;
520 tkt->sellast = i2->item;
521 }
522 else {
523 tkt->selfirst = nil;
524 tkt->sellast = nil;
525 }
526 }
527 while(i1->item != i2->item) {
528 if(i1->item->kind != TkTmark && i1->item->kind != TkTcontline) {
529 if(tid >= 32 && i1->item->tagextra < nextra) {
530 nit = realloc(i1->item, sizeof(TkTitem) + nextra * sizeof(long));
531 if(nit == nil)
532 return TkNomem;
533 for(j = nit->tagextra+1; j <= nextra; j++)
534 nit->tags[j] = 0;
535 nit->tagextra = nextra;
536 if(i1->line->items == i1->item)
537 i1->line->items = nit;
538 else {
539 ixprev = *i1;
540 tktadjustind(tkt, TkTbyitemback, &ixprev);
541 ixprev.item->next = nit;
542 }
543 /* check nit against cached items */
544 if(tkt->selfirst == i1->item)
545 tkt->selfirst = nit;
546 if(tkt->sellast == i1->item)
547 tkt->sellast = nit;
548 i1->item = nit;
549 }
550 changed = tkttagbit(i1->item, tid, add);
551 if(lmin == nil) {
552 if(changed) {
553 lmin = i1->line;
554 lmax = lmin;
555 }
556 }
557 else {
558 if(changed)
559 lmax = i1->line;
560 }
561 }
562 if(!tktadjustind(tkt, TkTbyitem, i1))
563 break;
564 }
565 if(lmin != nil) {
566 tktfixgeom(tk, tktprevwrapline(tk, lmin), lmax, 0);
567 tktextsize(tk, 1);
568 }
569 return nil;
570 }
571
572 static char*
tkttagaddrem(Tk * tk,char * arg,int add)573 tkttagaddrem(Tk *tk, char *arg, int add)
574 {
575 char *e;
576 TkText *tkt;
577 TkTtaginfo *ti;
578 TkTindex ix1, ix2;
579
580 tkt = TKobj(TkText, tk);
581
582 e = tkttagparse(tk, &arg, &ti);
583 if(e != nil)
584 return e;
585
586 while(*arg != '\0') {
587 e = tktindparse(tk, &arg, &ix1);
588 if(e != nil)
589 return e;
590 if(*arg != '\0') {
591 e = tktindparse(tk, &arg, &ix2);
592 if(e != nil)
593 return e;
594 }
595 else {
596 ix2 = ix1;
597 tktadjustind(tkt, TkTbychar, &ix2);
598 }
599 if(!tktindbefore(&ix1, &ix2))
600 continue;
601
602 e = tkttagchange(tk, ti->id, &ix1, &ix2, add);
603 if(e != nil)
604 return e;
605 }
606
607 return nil;
608 }
609
610
611 /* Text Tag Command (+ means implemented)
612 +add
613 +bind
614 +cget
615 +configure
616 +delete
617 +lower
618 +names
619 +nextrange
620 +prevrange
621 +raise
622 +ranges
623 +remove
624 */
625
626 static char*
tkttagadd(Tk * tk,char * arg,char ** val)627 tkttagadd(Tk *tk, char *arg, char **val)
628 {
629 USED(val);
630
631 return tkttagaddrem(tk, arg, 1);
632 }
633
634 static char*
tkttagbind(Tk * tk,char * arg,char ** val)635 tkttagbind(Tk *tk, char *arg, char **val)
636 {
637 char *e;
638 Rune r;
639 TkTtaginfo *ti;
640 TkAction *a;
641 int event, mode;
642 char *cmd, buf[Tkmaxitem];
643
644
645 e = tkttagparse(tk, &arg, &ti);
646 if(e != nil)
647 return e;
648
649 arg = tkskip(arg, " \t");
650 if (arg[0] == '\0')
651 return TkBadsq;
652 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
653 if(buf[0] == '<') {
654 event = tkseqparse(buf+1);
655 if(event == -1)
656 return TkBadsq;
657 }
658 else {
659 chartorune(&r, buf);
660 event = TkKey | r;
661 }
662 if(event == 0)
663 return TkBadsq;
664
665 arg = tkskip(arg, " \t");
666 if(*arg == '\0') {
667 for(a = ti->binds; a; a = a->link)
668 if(event == a->event)
669 return tkvalue(val, "%s", a->arg);
670 return nil;
671 }
672
673 mode = TkArepl;
674 if(*arg == '+') {
675 mode = TkAadd;
676 arg++;
677 }
678 else if(*arg == '-'){
679 mode = TkAsub;
680 arg++;
681 }
682
683 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
684 cmd = strdup(buf);
685 if(cmd == nil)
686 return TkNomem;
687 return tkaction(&ti->binds, event, TkDynamic, cmd, mode);
688 }
689
690 static char*
tkttagcget(Tk * tk,char * arg,char ** val)691 tkttagcget(Tk *tk, char *arg, char **val)
692 {
693 char *e;
694 TkTtaginfo *ti;
695 TkOptab tko[3];
696
697 e = tkttagparse(tk, &arg, &ti);
698 if(e != nil)
699 return e;
700
701 tko[0].ptr = ti;
702 tko[0].optab = tagopts;
703 tko[1].ptr = ti;
704 tko[1].optab = tagenvopts;
705 tko[2].ptr = nil;
706
707 return tkgencget(tko, arg, val, tk->env->top);
708 }
709
710 static char*
tkttagconfigure(Tk * tk,char * arg,char ** val)711 tkttagconfigure(Tk *tk, char *arg, char **val)
712 {
713 char *e;
714 TkOptab tko[3];
715 TkTtaginfo *ti;
716 TkTindex ix;
717 TkText *tkt = TKobj(TkText, tk);
718
719 USED(val);
720
721 e = tkttagparse(tk, &arg, &ti);
722 if(e != nil)
723 return e;
724
725 tko[0].ptr = ti;
726 tko[0].optab = tagopts;
727 tko[1].ptr = ti;
728 tko[1].optab = tagenvopts;
729 tko[2].ptr = nil;
730
731 e = tkparse(tk->env->top, arg, tko, nil);
732 if(e != nil)
733 return e;
734
735 if(tkttagind(tk, ti->name, 1, &ix)) {
736 tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
737 tktextsize(tk, 1);
738 }
739
740 return nil;
741 }
742
743 static void
tktunlinktag(TkText * tkt,TkTtaginfo * t)744 tktunlinktag(TkText *tkt, TkTtaginfo *t)
745 {
746 TkTtaginfo *f, **l;
747
748 l = &tkt->tags;
749 for(f = *l; f != nil; f = f->next) {
750 if(f == t) {
751 *l = t->next;
752 return;
753 }
754 l = &f->next;
755 }
756 }
757
758 static char*
tkttagdelete(Tk * tk,char * arg,char ** val)759 tkttagdelete(Tk *tk, char *arg, char **val)
760 {
761 TkText *tkt;
762 TkTtaginfo *t;
763 TkTindex ix;
764 char *e;
765 int found;
766
767 USED(val);
768
769 tkt = TKobj(TkText, tk);
770
771 e = tkttagparse(tk, &arg, &t);
772 if(e != nil)
773 return e;
774
775 found = 0;
776 while(t != nil) {
777 if(t->id == TkTselid)
778 return TkBadvl;
779
780 while(tkttagind(tk, t->name, 1, &ix)) {
781 found = 1;
782 tkttagbit(ix.item, t->id, 0);
783 }
784
785 tktunlinktag(tkt, t);
786 t->next = nil;
787 tktfreetags(t);
788
789 if(*arg != '\0') {
790 e = tkttagparse(tk, &arg, &t);
791 if(e != nil)
792 return e;
793 }
794 else
795 t = nil;
796 }
797 if (found) {
798 tktfixgeom(tk, &tkt->start, tkt->end.prev, 0);
799 tktextsize(tk, 1);
800 }
801
802 return nil;
803 }
804
805 static char*
tkttaglower(Tk * tk,char * arg,char ** val)806 tkttaglower(Tk *tk, char *arg, char **val)
807 {
808 TkText *tkt;
809 TkTindex ix;
810 TkTtaginfo *t, *tbelow, *f, **l;
811 char *e;
812
813 USED(val);
814
815 tkt = TKobj(TkText, tk);
816
817 e = tkttagparse(tk, &arg, &t);
818 if(e != nil)
819 return e;
820
821 if(*arg != '\0') {
822 e = tkttagparse(tk, &arg, &tbelow);
823 if(e != nil)
824 return e;
825 }
826 else
827 tbelow = nil;
828
829 tktunlinktag(tkt, t);
830
831 if(tbelow != nil) {
832 t->next = tbelow->next;
833 tbelow->next = t;
834 }
835 else {
836 l = &tkt->tags;
837 for(f = *l; f != nil; f = f->next)
838 l = &f->next;
839 *l = t;
840 t->next = nil;
841 }
842 if(tkttagind(tk, t->name, 1, &ix)) {
843 tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
844 tktextsize(tk, 1);
845 }
846
847 return nil;
848 }
849
850
851 static char*
tkttagnames(Tk * tk,char * arg,char ** val)852 tkttagnames(Tk *tk, char *arg, char **val)
853 {
854 char *e, *r, *fmt;
855 TkTtaginfo *t;
856 TkTindex i;
857 TkText *tkt = TKobj(TkText, tk);
858 TkTitem *tagit;
859
860 if(*arg != '\0') {
861 e = tktindparse(tk, &arg, &i);
862 if(e != nil)
863 return e;
864 /* make sure we're actually on a character */
865 tktadjustind(tkt, TkTbycharstart, &i);
866 tagit = i.item;
867 }
868 else
869 tagit = nil;
870
871 /* generate in order highest-to-lowest priority (contrary to spec) */
872 fmt = "%s";
873 for(t = tkt->tags; t != nil; t = t->next) {
874 if(tagit == nil || tkttagset(tagit, t->id)) {
875 r = tkvalue(val, fmt, t->name);
876 if(r != nil)
877 return r;
878 fmt = " %s";
879 }
880 }
881 return nil;
882 }
883
884 static char*
tkttagnextrange(Tk * tk,char * arg,char ** val)885 tkttagnextrange(Tk *tk, char *arg, char **val)
886 {
887 char *e;
888 TkTtaginfo *t;
889 TkTindex i1, i2, istart, iend;
890 TkText *tkt = TKobj(TkText, tk);
891
892 e = tkttagparse(tk, &arg, &t);
893 if(e != nil)
894 return e;
895 e = tktindparse(tk, &arg, &i1);
896 if(e != nil)
897 return e;
898 if(*arg != '\0') {
899 e = tktindparse(tk, &arg, &i2);
900 if(e != nil)
901 return e;
902 }
903 else
904 tktendind(tkt, &i2);
905
906 if(tkttagnrange(tkt, t->id, &i1, &i2, &istart, &iend))
907 return tkvalue(val, "%d.%d %d.%d",
908 tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
909 tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
910
911 return nil;
912 }
913
914 static char*
tkttagprevrange(Tk * tk,char * arg,char ** val)915 tkttagprevrange(Tk *tk, char *arg, char **val)
916 {
917 char *e;
918 TkTtaginfo *t;
919 TkTindex i1, i2, istart, iend;
920 TkText *tkt = TKobj(TkText, tk);
921
922 e = tkttagparse(tk, &arg, &t);
923 if(e != nil)
924 return e;
925 e = tktindparse(tk, &arg, &i1);
926 if(e != nil)
927 return e;
928 if(*arg != '\0') {
929 e = tktindparse(tk, &arg, &i2);
930 if(e != nil)
931 return e;
932 }
933 else
934 tktstartind(tkt, &i2);
935
936 if(tkttagprange(tkt, t->id, &i1, &i2, &istart, &iend))
937 return tkvalue(val, "%d.%d %d.%d",
938 tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
939 tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
940
941 return nil;
942 }
943
944 static char*
tkttagraise(Tk * tk,char * arg,char ** val)945 tkttagraise(Tk *tk, char *arg, char **val)
946 {
947 TkText *tkt;
948 TkTindex ix;
949 TkTtaginfo *t, *tabove, *f, **l;
950 char *e;
951
952 USED(val);
953
954 tkt = TKobj(TkText, tk);
955
956 e = tkttagparse(tk, &arg, &t);
957 if(e != nil)
958 return e;
959
960 if(*arg != '\0') {
961 e = tkttagparse(tk, &arg, &tabove);
962 if(e != nil)
963 return e;
964 }
965 else
966 tabove = nil;
967
968 tktunlinktag(tkt, t);
969
970 if(tabove != nil) {
971 l = &tkt->tags;
972 for(f = *l; f != nil; f = f->next) {
973 if(f == tabove) {
974 *l = t;
975 t->next = tabove;
976 break;
977 }
978 l = &f->next;
979 }
980 }
981 else {
982 t->next = tkt->tags;
983 tkt->tags = t;
984 }
985
986 if(tkttagind(tk, t->name, 1, &ix)) {
987 tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
988 tktextsize(tk, 1);
989 }
990 return nil;
991 }
992
993 static char*
tkttagranges(Tk * tk,char * arg,char ** val)994 tkttagranges(Tk *tk, char *arg, char **val)
995 {
996 char *e, *fmt;
997 TkTtaginfo *t;
998 TkTindex i1, i2, istart, iend;
999 TkText *tkt = TKobj(TkText, tk);
1000
1001 e = tkttagparse(tk, &arg, &t);
1002 if(e != nil)
1003 return e;
1004
1005 tktstartind(tkt, &i1);
1006 tktendind(tkt, &i2);
1007
1008 fmt = "%d.%d %d.%d";
1009 while(tkttagnrange(tkt, t->id, &i1, &i2, &istart, &iend)) {
1010 e = tkvalue(val, fmt,
1011 tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
1012 tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
1013 if(e != nil)
1014 return e;
1015
1016 fmt = " %d.%d %d.%d";
1017 i1 = iend;
1018 }
1019
1020 return nil;
1021 }
1022
1023 static char*
tkttagremove(Tk * tk,char * arg,char ** val)1024 tkttagremove(Tk *tk, char *arg, char **val)
1025 {
1026 USED(val);
1027
1028 return tkttagaddrem(tk, arg, 0);
1029 }
1030