xref: /inferno-os/libtk/tindx.c (revision 6e425a9de8c003b5a733621a6b6730ec3cc902b8)
137da2899SCharles.Forsyth #include "lib9.h"
237da2899SCharles.Forsyth #include "draw.h"
337da2899SCharles.Forsyth #include "tk.h"
437da2899SCharles.Forsyth #include "textw.h"
537da2899SCharles.Forsyth 
637da2899SCharles.Forsyth #define istring u.string
737da2899SCharles.Forsyth #define iwin u.win
837da2899SCharles.Forsyth #define imark u.mark
937da2899SCharles.Forsyth #define iline u.line
1037da2899SCharles.Forsyth 
1137da2899SCharles.Forsyth /* debugging */
1237da2899SCharles.Forsyth extern int tktdbg;
1337da2899SCharles.Forsyth extern void tktprinttext(TkText*);
1437da2899SCharles.Forsyth extern void tktprintindex(TkTindex*);
1537da2899SCharles.Forsyth extern void tktprintitem(TkTitem*);
1637da2899SCharles.Forsyth extern void tktprintline(TkTline*);
1737da2899SCharles.Forsyth 
1837da2899SCharles.Forsyth char*
tktindparse(Tk * tk,char ** pspec,TkTindex * ans)1937da2899SCharles.Forsyth tktindparse(Tk *tk, char **pspec, TkTindex *ans)
2037da2899SCharles.Forsyth {
2137da2899SCharles.Forsyth 	int m, n, done, neg, modstart;
2237da2899SCharles.Forsyth 	char *s, *mod;
2337da2899SCharles.Forsyth 	TkTline *lend;
2437da2899SCharles.Forsyth 	TkText *tkt;
2537da2899SCharles.Forsyth 	char *buf;
2637da2899SCharles.Forsyth 
2737da2899SCharles.Forsyth 	buf = mallocz(Tkmaxitem, 0);
2837da2899SCharles.Forsyth 	if(buf == nil)
2937da2899SCharles.Forsyth 		return TkNomem;
3037da2899SCharles.Forsyth 
3137da2899SCharles.Forsyth 	tkt = TKobj(TkText, tk);
3237da2899SCharles.Forsyth 	lend = &tkt->end;
3337da2899SCharles.Forsyth 
3437da2899SCharles.Forsyth 	*pspec = tkword(tk->env->top, *pspec, buf, buf+Tkmaxitem, nil);
3537da2899SCharles.Forsyth 	modstart = 0;
3637da2899SCharles.Forsyth 	for(mod = buf; *mod != '\0'; mod++)
3737da2899SCharles.Forsyth 		if(*mod == ' ' || *mod == '-' || *mod == '+') {
3837da2899SCharles.Forsyth 			modstart = *mod;
3937da2899SCharles.Forsyth 			*mod = '\0';
4037da2899SCharles.Forsyth 			break;
4137da2899SCharles.Forsyth 		}
4237da2899SCharles.Forsyth 
4337da2899SCharles.Forsyth 	/*
4437da2899SCharles.Forsyth 	 * XXX there's a problem here - if either coordinate is negative
4537da2899SCharles.Forsyth 	 * which shouldn't be precluded, then the above scanning code
4637da2899SCharles.Forsyth 	 * will break up the coordinate pair, so @-23,45 for example
4737da2899SCharles.Forsyth 	 * yields a bad index, when it should probably return the index
4837da2899SCharles.Forsyth 	 * of the character at the start of the line containing y=45.
4937da2899SCharles.Forsyth 	 * i've seen this cause wm/sh to crash.
5037da2899SCharles.Forsyth 	 */
5137da2899SCharles.Forsyth 	if(strcmp(buf, "end") == 0)
5237da2899SCharles.Forsyth 		tktendind(tkt, ans);
5337da2899SCharles.Forsyth 	else
5437da2899SCharles.Forsyth 	if(*buf == '@') {
5537da2899SCharles.Forsyth 		/* by coordinates */
5637da2899SCharles.Forsyth 
5737da2899SCharles.Forsyth 		s = strchr(buf, ',');
5837da2899SCharles.Forsyth 		if(s == nil) {
5937da2899SCharles.Forsyth 			free(buf);
6037da2899SCharles.Forsyth 			return TkBadix;
6137da2899SCharles.Forsyth 		}
6237da2899SCharles.Forsyth 		*s = '\0';
6337da2899SCharles.Forsyth 		m = atoi(buf+1);
6437da2899SCharles.Forsyth 		n = atoi(s+1);
6537da2899SCharles.Forsyth 		tktxyind(tk, m, n, ans);
6637da2899SCharles.Forsyth 	}
6737da2899SCharles.Forsyth 	else
6837da2899SCharles.Forsyth 	if(*buf >= '0' && *buf <= '9') {
6937da2899SCharles.Forsyth 		/* line.char */
7037da2899SCharles.Forsyth 
7137da2899SCharles.Forsyth 		s = strchr(buf, '.');
7237da2899SCharles.Forsyth 		if(s == nil) {
7337da2899SCharles.Forsyth 			free(buf);
7437da2899SCharles.Forsyth 			return TkBadix;
7537da2899SCharles.Forsyth 		}
7637da2899SCharles.Forsyth 		*s = '\0';
7737da2899SCharles.Forsyth 		m = atoi(buf);
7837da2899SCharles.Forsyth 		n = atoi(s+1);
7937da2899SCharles.Forsyth 
8037da2899SCharles.Forsyth 		if(m < 1)
8137da2899SCharles.Forsyth 			m = 1;
8237da2899SCharles.Forsyth 
8337da2899SCharles.Forsyth 		tktstartind(tkt, ans);
8437da2899SCharles.Forsyth 
8537da2899SCharles.Forsyth 		while(--m > 0 && ans->line->next != lend)
8637da2899SCharles.Forsyth 			tktadjustind(tkt, TkTbyline, ans);
8737da2899SCharles.Forsyth 
8837da2899SCharles.Forsyth 		while(n-- > 0 && ans->item->kind != TkTnewline)
8937da2899SCharles.Forsyth 			tktadjustind(tkt, TkTbychar, ans);
9037da2899SCharles.Forsyth 	}
9137da2899SCharles.Forsyth 	else
9237da2899SCharles.Forsyth 	if(*buf == '.') {
9337da2899SCharles.Forsyth 		/* window */
9437da2899SCharles.Forsyth 
9537da2899SCharles.Forsyth 		tktstartind(tkt, ans);
9637da2899SCharles.Forsyth 
9737da2899SCharles.Forsyth 		while(ans->line != lend) {
9837da2899SCharles.Forsyth 			if(ans->item->kind == TkTwin &&
9937da2899SCharles.Forsyth 			   ans->item->iwin->sub != nil &&
10037da2899SCharles.Forsyth 			   ans->item->iwin->sub->name != nil &&
10137da2899SCharles.Forsyth 			   strcmp(ans->item->iwin->sub->name->name, buf) == 0)
10237da2899SCharles.Forsyth 				break;
10337da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbyitem, ans))
10437da2899SCharles.Forsyth 				ans->line = lend;
10537da2899SCharles.Forsyth 		}
10637da2899SCharles.Forsyth 		if(ans->line == lend) {
10737da2899SCharles.Forsyth 			free(buf);
10837da2899SCharles.Forsyth 			return TkBadix;
10937da2899SCharles.Forsyth 		}
11037da2899SCharles.Forsyth 	}
11137da2899SCharles.Forsyth 	else {
11237da2899SCharles.Forsyth 		s = strchr(buf, '.');
11337da2899SCharles.Forsyth 		if(s == nil) {
11437da2899SCharles.Forsyth 			if(tktmarkind(tk, buf, ans) == 0) {
11537da2899SCharles.Forsyth 				free(buf);
11637da2899SCharles.Forsyth 				return TkBadix;
11737da2899SCharles.Forsyth 			}
11837da2899SCharles.Forsyth 		}
11937da2899SCharles.Forsyth 		else {
12037da2899SCharles.Forsyth 			/* tag.first or tag.last */
12137da2899SCharles.Forsyth 
12237da2899SCharles.Forsyth 			*s = '\0';
12337da2899SCharles.Forsyth 			if(strcmp(s+1, "first") == 0) {
12437da2899SCharles.Forsyth 				if(tkttagind(tk, buf, 1, ans) == 0) {
12537da2899SCharles.Forsyth 					free(buf);
12637da2899SCharles.Forsyth 					return TkBadix;
12737da2899SCharles.Forsyth 				}
12837da2899SCharles.Forsyth 			}
12937da2899SCharles.Forsyth 			else
13037da2899SCharles.Forsyth 			if(strcmp(s+1, "last") == 0) {
13137da2899SCharles.Forsyth 				if(tkttagind(tk, buf, 0, ans) == 0) {
13237da2899SCharles.Forsyth 					free(buf);
13337da2899SCharles.Forsyth 					return TkBadix;
13437da2899SCharles.Forsyth 				}
13537da2899SCharles.Forsyth 			}
13637da2899SCharles.Forsyth 			else {
13737da2899SCharles.Forsyth 				free(buf);
13837da2899SCharles.Forsyth 				return TkBadix;
13937da2899SCharles.Forsyth 			}
14037da2899SCharles.Forsyth 		}
14137da2899SCharles.Forsyth 	}
14237da2899SCharles.Forsyth 
14337da2899SCharles.Forsyth 	if(modstart == 0) {
14437da2899SCharles.Forsyth 		free(buf);
14537da2899SCharles.Forsyth 		return nil;
14637da2899SCharles.Forsyth 	}
14737da2899SCharles.Forsyth 
14837da2899SCharles.Forsyth 	*mod = modstart;
14937da2899SCharles.Forsyth 	while(*mod == ' ')
15037da2899SCharles.Forsyth 		mod++;
15137da2899SCharles.Forsyth 
15237da2899SCharles.Forsyth 	while(*mod != '\0') {
15337da2899SCharles.Forsyth 		done = 0;
15437da2899SCharles.Forsyth 		switch(*mod) {
15537da2899SCharles.Forsyth 		case '+':
15637da2899SCharles.Forsyth 		case '-':
15737da2899SCharles.Forsyth 			neg = (*mod == '-');
15837da2899SCharles.Forsyth 			mod++;
15937da2899SCharles.Forsyth 			while(*mod == ' ')
16037da2899SCharles.Forsyth 				mod++;
16137da2899SCharles.Forsyth 			n = strtol(mod, &mod, 10);
16237da2899SCharles.Forsyth 			while(*mod == ' ')
16337da2899SCharles.Forsyth 				mod++;
16437da2899SCharles.Forsyth 			while(n-- > 0) {
16537da2899SCharles.Forsyth 				if(*mod == 'c')
16637da2899SCharles.Forsyth 					tktadjustind(tkt, neg? TkTbycharback : TkTbychar, ans);
16737da2899SCharles.Forsyth 				else
16837da2899SCharles.Forsyth 				if(*mod == 'l')
16937da2899SCharles.Forsyth 					tktadjustind(tkt, neg? TkTbylineback : TkTbyline, ans);
17037da2899SCharles.Forsyth 				else
17137da2899SCharles.Forsyth 					done = 1;
17237da2899SCharles.Forsyth 			}
17337da2899SCharles.Forsyth 			break;
17437da2899SCharles.Forsyth 		case 'l':
17537da2899SCharles.Forsyth 			if(strncmp(mod, "lines", 5) == 0)
17637da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbylinestart, ans);
17737da2899SCharles.Forsyth 			else
17837da2899SCharles.Forsyth 			if(strncmp(mod, "linee", 5) == 0)
17937da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbylineend, ans);
18037da2899SCharles.Forsyth 			else
18137da2899SCharles.Forsyth 				done = 1;
18237da2899SCharles.Forsyth 			break;
18337da2899SCharles.Forsyth 		case 'w':
18437da2899SCharles.Forsyth 			if(strncmp(mod, "words", 5) == 0)
18537da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbywordstart, ans);
18637da2899SCharles.Forsyth 			else
18737da2899SCharles.Forsyth 			if(strncmp(mod, "worde", 5) == 0)
18837da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbywordend, ans);
18937da2899SCharles.Forsyth 			else
19037da2899SCharles.Forsyth 				done = 1;
19137da2899SCharles.Forsyth 			break;
19237da2899SCharles.Forsyth 		default:
19337da2899SCharles.Forsyth 				done = 1;
19437da2899SCharles.Forsyth 		}
19537da2899SCharles.Forsyth 
19637da2899SCharles.Forsyth 		if(done)
19737da2899SCharles.Forsyth 			break;
19837da2899SCharles.Forsyth 
19937da2899SCharles.Forsyth 		while(tkiswordchar(*mod))
20037da2899SCharles.Forsyth 			mod++;
20137da2899SCharles.Forsyth 		while(*mod == ' ')
20237da2899SCharles.Forsyth 			mod++;
20337da2899SCharles.Forsyth 	}
20437da2899SCharles.Forsyth 
20537da2899SCharles.Forsyth 	free(buf);
20637da2899SCharles.Forsyth 	return nil;
20737da2899SCharles.Forsyth }
20837da2899SCharles.Forsyth 
20937da2899SCharles.Forsyth int
tktisbreak(int c)21037da2899SCharles.Forsyth tktisbreak(int c)
21137da2899SCharles.Forsyth {
21237da2899SCharles.Forsyth 	/* unicode rules suggest / as well but that would split dates, and URLs might as well char. wrap */
21337da2899SCharles.Forsyth 	return c == ' ' || c == '\t' || c == '\n' || c == '-' || c == ',';
21437da2899SCharles.Forsyth 	/* previously included . but would probably need more then to handle ." */
21537da2899SCharles.Forsyth }
21637da2899SCharles.Forsyth 
21737da2899SCharles.Forsyth /*
21837da2899SCharles.Forsyth  * Adjust the index p by units (one of TkTbyitem, etc.).
21937da2899SCharles.Forsyth  * The TkTbychar units mean that the final point should rest on a
22037da2899SCharles.Forsyth  * "character" (in text widget index space; i.e., a newline, a rune,
22137da2899SCharles.Forsyth  * and an embedded window are each 1 character, but marks and contlines are not).
22237da2899SCharles.Forsyth  *
22337da2899SCharles.Forsyth  * Indexes may not point in the tkt->start or tkt->end lines (which have
22437da2899SCharles.Forsyth  * no items); tktadjustind sticks at the beginning or end of the buffer.
22537da2899SCharles.Forsyth  *
22637da2899SCharles.Forsyth  * Return 1 if the index changes at all, 0 otherwise.
22737da2899SCharles.Forsyth  */
22837da2899SCharles.Forsyth int
tktadjustind(TkText * tkt,int units,TkTindex * p)22937da2899SCharles.Forsyth tktadjustind(TkText *tkt, int units, TkTindex *p)
23037da2899SCharles.Forsyth {
23137da2899SCharles.Forsyth 	int n, opos, count, c;
23237da2899SCharles.Forsyth 	TkTitem *i, *it, *oit;
23337da2899SCharles.Forsyth 	TkTindex q;
23437da2899SCharles.Forsyth 
23537da2899SCharles.Forsyth 	oit = p->item;
23637da2899SCharles.Forsyth 	opos = p->pos;
23737da2899SCharles.Forsyth 	count = 1;
23837da2899SCharles.Forsyth 
23937da2899SCharles.Forsyth 	switch(units) {
24037da2899SCharles.Forsyth 	case TkTbyitemback:
24137da2899SCharles.Forsyth 		it = p->item;
24237da2899SCharles.Forsyth 		p->item = p->line->items;
24337da2899SCharles.Forsyth 		p->pos = 0;
24437da2899SCharles.Forsyth 		if(it == p->item) {
24537da2899SCharles.Forsyth 			if(p->line->prev != &tkt->start) {
24637da2899SCharles.Forsyth 				p->line = p->line->prev;
24737da2899SCharles.Forsyth 				p->item = tktlastitem(p->line->items);
24837da2899SCharles.Forsyth 			}
24937da2899SCharles.Forsyth 		}
25037da2899SCharles.Forsyth 		else {
25137da2899SCharles.Forsyth 			while(p->item->next != it) {
25237da2899SCharles.Forsyth 				p->item = p->item->next;
25337da2899SCharles.Forsyth 				if(tktdbg && p->item == nil) {
25437da2899SCharles.Forsyth 					print("tktadjustind: botch 1\n");
25537da2899SCharles.Forsyth 					break;
25637da2899SCharles.Forsyth 				}
25737da2899SCharles.Forsyth 			}
25837da2899SCharles.Forsyth 		}
25937da2899SCharles.Forsyth 		break;
26037da2899SCharles.Forsyth 
26137da2899SCharles.Forsyth 	case TkTbyitem:
26237da2899SCharles.Forsyth 		p->pos = 0;
26337da2899SCharles.Forsyth 		i = p->item->next;
26437da2899SCharles.Forsyth 		if(i == nil) {
26537da2899SCharles.Forsyth 			if(p->line->next != &tkt->end) {
26637da2899SCharles.Forsyth 				p->line = p->line->next;
26737da2899SCharles.Forsyth 				p->item = p->line->items;
26837da2899SCharles.Forsyth 			}
26937da2899SCharles.Forsyth 		}
27037da2899SCharles.Forsyth 		else
27137da2899SCharles.Forsyth 			p->item = i;
27237da2899SCharles.Forsyth 		break;
27337da2899SCharles.Forsyth 
27437da2899SCharles.Forsyth 	case TkTbytlineback:
27537da2899SCharles.Forsyth 		if(p->line->prev != &tkt->start)
27637da2899SCharles.Forsyth 			p->line = p->line->prev;
27737da2899SCharles.Forsyth 		p->item = p->line->items;
27837da2899SCharles.Forsyth 		p->pos = 0;
27937da2899SCharles.Forsyth 		break;
28037da2899SCharles.Forsyth 
28137da2899SCharles.Forsyth 	case TkTbytline:
28237da2899SCharles.Forsyth 		if(p->line->next != &tkt->end)
28337da2899SCharles.Forsyth 			p->line = p->line->next;
28437da2899SCharles.Forsyth 		p->item = p->line->items;
28537da2899SCharles.Forsyth 		p->pos = 0;
28637da2899SCharles.Forsyth 		break;
28737da2899SCharles.Forsyth 
28837da2899SCharles.Forsyth 	case TkTbycharstart:
28937da2899SCharles.Forsyth 		count = 0;
29037da2899SCharles.Forsyth 	case TkTbychar:
29137da2899SCharles.Forsyth 		while(count > 0) {
29237da2899SCharles.Forsyth 			i = p->item;
29337da2899SCharles.Forsyth 			n = tktposcount(i) - p->pos;
29437da2899SCharles.Forsyth 			if(count >= n) {
29537da2899SCharles.Forsyth 				if(tktadjustind(tkt, TkTbyitem, p))
29637da2899SCharles.Forsyth 					count -= n;
29737da2899SCharles.Forsyth 				else
29837da2899SCharles.Forsyth 					break;
29937da2899SCharles.Forsyth 			}
30037da2899SCharles.Forsyth 			else {
30137da2899SCharles.Forsyth 				p->pos += count;
30237da2899SCharles.Forsyth 				break;
30337da2899SCharles.Forsyth 			}
30437da2899SCharles.Forsyth 		}
30537da2899SCharles.Forsyth 		while(p->item->kind == TkTmark || p->item->kind == TkTcontline)
30637da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbyitem, p))
30737da2899SCharles.Forsyth 				break;
30837da2899SCharles.Forsyth 		break;
30937da2899SCharles.Forsyth 	case TkTbycharback:
31037da2899SCharles.Forsyth 		count = -1;
31137da2899SCharles.Forsyth 		while(count < 0) {
31237da2899SCharles.Forsyth 			if(p->pos + count >= 0) {
31337da2899SCharles.Forsyth 				p->pos += count;
31437da2899SCharles.Forsyth 				count = 0;
31537da2899SCharles.Forsyth 			}
31637da2899SCharles.Forsyth 			else {
31737da2899SCharles.Forsyth 				count += p->pos;
31837da2899SCharles.Forsyth 				if(!tktadjustind(tkt, TkTbyitemback, p))
31937da2899SCharles.Forsyth 					break;
32037da2899SCharles.Forsyth 				n = tktposcount(p->item);
32137da2899SCharles.Forsyth 				p->pos = n;
32237da2899SCharles.Forsyth 			}
32337da2899SCharles.Forsyth 		}
32437da2899SCharles.Forsyth 		break;
32537da2899SCharles.Forsyth 
32637da2899SCharles.Forsyth 	case TkTbylineback:
32737da2899SCharles.Forsyth 		count = -1;
32837da2899SCharles.Forsyth 		/* fall through */
32937da2899SCharles.Forsyth 	case TkTbyline:
33037da2899SCharles.Forsyth 		n = tktlinepos(tkt, p);
33137da2899SCharles.Forsyth 		while(count > 0) {
33237da2899SCharles.Forsyth 			if(p->line->next == &tkt->end) {
33337da2899SCharles.Forsyth 				count = 0;
33437da2899SCharles.Forsyth 				break;
33537da2899SCharles.Forsyth 			}
33637da2899SCharles.Forsyth 			if(p->line->flags&TkTlast)
33737da2899SCharles.Forsyth 				count--;
33837da2899SCharles.Forsyth 			p->line = p->line->next;
33937da2899SCharles.Forsyth 		}
34037da2899SCharles.Forsyth 		while(count < 0 && p->line->prev != &tkt->start) {
34137da2899SCharles.Forsyth 			if(p->line->flags&TkTfirst)
34237da2899SCharles.Forsyth 				count++;
34337da2899SCharles.Forsyth 			p->line = p->line->prev;
34437da2899SCharles.Forsyth 		}
34537da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbylinestart, p);
34637da2899SCharles.Forsyth 		while(n > 0) {
34737da2899SCharles.Forsyth 			if(p->item->kind == TkTnewline)
34837da2899SCharles.Forsyth 				break;
34937da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbychar, p))
35037da2899SCharles.Forsyth 				break;
35137da2899SCharles.Forsyth 			n--;
35237da2899SCharles.Forsyth 		}
35337da2899SCharles.Forsyth 		break;
35437da2899SCharles.Forsyth 
35537da2899SCharles.Forsyth 	case TkTbylinestart:
35637da2899SCharles.Forsyth 		/* note: can call this with only p->line set correctly  in *p */
35737da2899SCharles.Forsyth 
35837da2899SCharles.Forsyth 		while(!(p->line->flags&TkTfirst))
35937da2899SCharles.Forsyth 			p->line = p->line->prev;
36037da2899SCharles.Forsyth 		p->item = p->line->items;
36137da2899SCharles.Forsyth 		p->pos = 0;
36237da2899SCharles.Forsyth 		break;
36337da2899SCharles.Forsyth 
36437da2899SCharles.Forsyth 	case TkTbylineend:
36537da2899SCharles.Forsyth 		while(p->item->kind != TkTnewline)
36637da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbychar, p))
36737da2899SCharles.Forsyth 				break;
36837da2899SCharles.Forsyth 		break;
36937da2899SCharles.Forsyth 
37037da2899SCharles.Forsyth 	case TkTbywordstart:
37137da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbycharstart, p);
37237da2899SCharles.Forsyth 		q = *p;
37337da2899SCharles.Forsyth 		c = tktindrune(p);
37437da2899SCharles.Forsyth 		while(tkiswordchar(c)) {
37537da2899SCharles.Forsyth 			q = *p;
37637da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbycharback, p))
37737da2899SCharles.Forsyth 				break;
37837da2899SCharles.Forsyth 			c = tktindrune(p);
37937da2899SCharles.Forsyth 		}
38037da2899SCharles.Forsyth 		*p = q;
38137da2899SCharles.Forsyth 		break;
38237da2899SCharles.Forsyth 
38337da2899SCharles.Forsyth 	case TkTbywordend:
38437da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbycharstart, p);
38537da2899SCharles.Forsyth 		if(p->item->kind == TkTascii || p->item->kind == TkTrune) {
38637da2899SCharles.Forsyth 			c = tktindrune(p);
38737da2899SCharles.Forsyth 			if(tkiswordchar(c)) {
38837da2899SCharles.Forsyth 				do {
38937da2899SCharles.Forsyth 					if(!tktadjustind(tkt, TkTbychar, p))
39037da2899SCharles.Forsyth 						break;
39137da2899SCharles.Forsyth 					c = tktindrune(p);
39237da2899SCharles.Forsyth 				} while(tkiswordchar(c));
39337da2899SCharles.Forsyth 			}
39437da2899SCharles.Forsyth 			else
39537da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbychar, p);
39637da2899SCharles.Forsyth 		}
39737da2899SCharles.Forsyth 		else if(!(p->item->kind == TkTnewline && p->line->next == &tkt->end))
39837da2899SCharles.Forsyth 			tktadjustind(tkt, TkTbychar, p);
39937da2899SCharles.Forsyth 
40037da2899SCharles.Forsyth 		break;
40137da2899SCharles.Forsyth 
40237da2899SCharles.Forsyth 	case TkTbywrapstart:
40337da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbycharstart, p);
40437da2899SCharles.Forsyth 		q = *p;
40537da2899SCharles.Forsyth 		c = tktindrune(p);
40637da2899SCharles.Forsyth 		while(!tktisbreak(c)) {
40737da2899SCharles.Forsyth 			q = *p;
40837da2899SCharles.Forsyth 			if(!tktadjustind(tkt, TkTbycharback, p))
40937da2899SCharles.Forsyth 				break;
41037da2899SCharles.Forsyth 			c = tktindrune(p);
41137da2899SCharles.Forsyth 		}
41237da2899SCharles.Forsyth 		*p = q;
41337da2899SCharles.Forsyth 		break;
41437da2899SCharles.Forsyth 
41537da2899SCharles.Forsyth 	case TkTbywrapend:
41637da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbycharstart, p);
41737da2899SCharles.Forsyth 		if(p->item->kind == TkTascii || p->item->kind == TkTrune) {
41837da2899SCharles.Forsyth 			c = tktindrune(p);
41937da2899SCharles.Forsyth 			if(!tktisbreak(c)) {
42037da2899SCharles.Forsyth 				do {
42137da2899SCharles.Forsyth 					if(!tktadjustind(tkt, TkTbychar, p))
42237da2899SCharles.Forsyth 						break;
42337da2899SCharles.Forsyth 					c = tktindrune(p);
42437da2899SCharles.Forsyth 				} while(!tktisbreak(c) && (p->item->kind == TkTascii || p->item->kind == TkTrune));
42537da2899SCharles.Forsyth 				while(tktisbreak(c) && tktadjustind(tkt, TkTbychar, p))
42637da2899SCharles.Forsyth 					c = tktindrune(p);	/* could limit it */
42737da2899SCharles.Forsyth 			}
42837da2899SCharles.Forsyth 			else
42937da2899SCharles.Forsyth 				tktadjustind(tkt, TkTbychar, p);
43037da2899SCharles.Forsyth 		}
43137da2899SCharles.Forsyth 		else if(!(p->item->kind == TkTnewline && p->line->next == &tkt->end))
43237da2899SCharles.Forsyth 			tktadjustind(tkt, TkTbychar, p);
43337da2899SCharles.Forsyth 
43437da2899SCharles.Forsyth 		break;
43537da2899SCharles.Forsyth 	}
43637da2899SCharles.Forsyth 	return (p->item != oit || p->pos != opos);
43737da2899SCharles.Forsyth }
43837da2899SCharles.Forsyth 
43937da2899SCharles.Forsyth /* return 1 if advancing i1 by item eventually hits i2 */
44037da2899SCharles.Forsyth int
tktindbefore(TkTindex * i1,TkTindex * i2)44137da2899SCharles.Forsyth tktindbefore(TkTindex *i1, TkTindex *i2)
44237da2899SCharles.Forsyth {
44337da2899SCharles.Forsyth 	int ans;
44437da2899SCharles.Forsyth 	TkTitem *i;
44537da2899SCharles.Forsyth 	TkTline *l1, *l2;
44637da2899SCharles.Forsyth 
44737da2899SCharles.Forsyth 	ans = 0;
44837da2899SCharles.Forsyth 	l1 = i1->line;
44937da2899SCharles.Forsyth 	l2 = i2->line;
45037da2899SCharles.Forsyth 
45137da2899SCharles.Forsyth 	if(l1 == l2) {
45237da2899SCharles.Forsyth 		if(i1->item == i2->item)
45337da2899SCharles.Forsyth 			ans = (i1->pos < i2->pos);
45437da2899SCharles.Forsyth 		else {
45537da2899SCharles.Forsyth 			for(i = i1->item; i != nil; i = i->next)
45637da2899SCharles.Forsyth 				if(i->next == i2->item) {
45737da2899SCharles.Forsyth 					ans = 1;
45837da2899SCharles.Forsyth 					break;
45937da2899SCharles.Forsyth 				}
46037da2899SCharles.Forsyth 		}
46137da2899SCharles.Forsyth 	}
46237da2899SCharles.Forsyth 	else {
46337da2899SCharles.Forsyth 		if(l1->orig.y < l2->orig.y)
46437da2899SCharles.Forsyth 			ans = 1;
46537da2899SCharles.Forsyth 		else
46637da2899SCharles.Forsyth 		if(l1->orig.y == l2->orig.y) {
46737da2899SCharles.Forsyth 			for(; l1 != nil; l1 = l1->next) {
46837da2899SCharles.Forsyth 				if(l1->next == l2) {
46937da2899SCharles.Forsyth 					ans = 1;
47037da2899SCharles.Forsyth 					break;
47137da2899SCharles.Forsyth 				}
47237da2899SCharles.Forsyth 				if(l1->orig.y > l2->orig.y)
47337da2899SCharles.Forsyth 					break;
47437da2899SCharles.Forsyth 			}
47537da2899SCharles.Forsyth 		}
47637da2899SCharles.Forsyth 	}
47737da2899SCharles.Forsyth 
47837da2899SCharles.Forsyth 	return ans;
47937da2899SCharles.Forsyth }
48037da2899SCharles.Forsyth 
48137da2899SCharles.Forsyth /*
48237da2899SCharles.Forsyth  * This comparison only cares which characters the indices are before.
48337da2899SCharles.Forsyth  * So two marks should be called "equal" (and not "less" or "greater")
48437da2899SCharles.Forsyth  * if they are adjacent.
48537da2899SCharles.Forsyth  */
48637da2899SCharles.Forsyth int
tktindcompare(TkText * tkt,TkTindex * i1,int op,TkTindex * i2)48737da2899SCharles.Forsyth tktindcompare(TkText *tkt, TkTindex *i1, int op, TkTindex *i2)
48837da2899SCharles.Forsyth {
48937da2899SCharles.Forsyth 	int eq, ans;
49037da2899SCharles.Forsyth 	TkTindex x1, x2;
49137da2899SCharles.Forsyth 
49237da2899SCharles.Forsyth 	x1 = *i1;
49337da2899SCharles.Forsyth 	x2 = *i2;
49437da2899SCharles.Forsyth 
49537da2899SCharles.Forsyth 	/* skip over any marks, contlines, to see if on same character */
49637da2899SCharles.Forsyth 	tktadjustind(tkt, TkTbycharstart, &x1);
49737da2899SCharles.Forsyth 	tktadjustind(tkt, TkTbycharstart, &x2);
49837da2899SCharles.Forsyth 	eq = (x1.item == x2.item && x1.pos == x2.pos);
49937da2899SCharles.Forsyth 
50037da2899SCharles.Forsyth 	switch(op) {
50137da2899SCharles.Forsyth 	case TkEq:
50237da2899SCharles.Forsyth 		ans = eq;
50337da2899SCharles.Forsyth 		break;
50437da2899SCharles.Forsyth 	case TkNeq:
50537da2899SCharles.Forsyth 		ans = !eq;
50637da2899SCharles.Forsyth 		break;
50737da2899SCharles.Forsyth 	case TkLte:
50837da2899SCharles.Forsyth 		ans = eq || tktindbefore(i1, i2);
50937da2899SCharles.Forsyth 		break;
51037da2899SCharles.Forsyth 	case TkLt:
51137da2899SCharles.Forsyth 		ans = !eq && tktindbefore(i1, i2);
51237da2899SCharles.Forsyth 		break;
51337da2899SCharles.Forsyth 	case TkGte:
51437da2899SCharles.Forsyth 		ans = eq || tktindbefore(i2, i1);
51537da2899SCharles.Forsyth 		break;
51637da2899SCharles.Forsyth 	case TkGt:
51737da2899SCharles.Forsyth 		ans = !eq && tktindbefore(i2, i1);
51837da2899SCharles.Forsyth 		break;
51937da2899SCharles.Forsyth 	default:
520*6e425a9dSCharles.Forsyth 		ans = 0;	/* not reached */
521*6e425a9dSCharles.Forsyth 		break;
52237da2899SCharles.Forsyth 	};
52337da2899SCharles.Forsyth 
52437da2899SCharles.Forsyth 	return ans;
52537da2899SCharles.Forsyth }
52637da2899SCharles.Forsyth 
52737da2899SCharles.Forsyth void
tktstartind(TkText * tkt,TkTindex * ans)52837da2899SCharles.Forsyth tktstartind(TkText *tkt, TkTindex *ans)
52937da2899SCharles.Forsyth {
53037da2899SCharles.Forsyth 	ans->line = tkt->start.next;
53137da2899SCharles.Forsyth 	ans->item = ans->line->items;
53237da2899SCharles.Forsyth 	ans->pos = 0;
53337da2899SCharles.Forsyth }
53437da2899SCharles.Forsyth 
53537da2899SCharles.Forsyth void
tktendind(TkText * tkt,TkTindex * ans)53637da2899SCharles.Forsyth tktendind(TkText *tkt, TkTindex *ans)
53737da2899SCharles.Forsyth {
53837da2899SCharles.Forsyth 	ans->line = tkt->end.prev;
53937da2899SCharles.Forsyth 	ans->item = tktlastitem(ans->line->items);
54037da2899SCharles.Forsyth 	ans->pos = 0;
54137da2899SCharles.Forsyth }
54237da2899SCharles.Forsyth 
54337da2899SCharles.Forsyth void
tktitemind(TkTitem * it,TkTindex * ans)54437da2899SCharles.Forsyth tktitemind(TkTitem *it, TkTindex *ans)
54537da2899SCharles.Forsyth {
54637da2899SCharles.Forsyth 	ans->item = it;
54737da2899SCharles.Forsyth 	ans->line = tktitemline(it);
54837da2899SCharles.Forsyth 	ans->pos = 0;
54937da2899SCharles.Forsyth }
55037da2899SCharles.Forsyth 
55137da2899SCharles.Forsyth /*
55237da2899SCharles.Forsyth  * Fill ans with the item that (x,y) (in V space) is over.
55337da2899SCharles.Forsyth  * Return 0 if it is over the first half of the width,
55437da2899SCharles.Forsyth  * and 1 if it is over the second half.
55537da2899SCharles.Forsyth  */
55637da2899SCharles.Forsyth int
tktxyind(Tk * tk,int x,int y,TkTindex * ans)55737da2899SCharles.Forsyth tktxyind(Tk *tk, int x, int y, TkTindex *ans)
55837da2899SCharles.Forsyth {
55937da2899SCharles.Forsyth 	int n, w, secondhalf, k;
56037da2899SCharles.Forsyth 	Point p, q;
56137da2899SCharles.Forsyth 	TkTitem *i;
56237da2899SCharles.Forsyth 	TkText *tkt;
56337da2899SCharles.Forsyth 
56437da2899SCharles.Forsyth  	tkt = TKobj(TkText, tk);
56537da2899SCharles.Forsyth 	tktstartind(tkt, ans);
56637da2899SCharles.Forsyth 	secondhalf = 0;
56737da2899SCharles.Forsyth 
56837da2899SCharles.Forsyth 	/* (x,y), p, q in V space */
56937da2899SCharles.Forsyth 	p = subpt(ans->line->orig, tkt->deltatv);
57037da2899SCharles.Forsyth 	q = subpt(ans->line->next->orig, tkt->deltatv);
57137da2899SCharles.Forsyth 	while(ans->line->next != &tkt->end) {
57237da2899SCharles.Forsyth 		if(q.y > y)
57337da2899SCharles.Forsyth 			break;
57437da2899SCharles.Forsyth 		tktadjustind(tkt, TkTbytline, ans);
57537da2899SCharles.Forsyth 		p = q;
57637da2899SCharles.Forsyth 		q = subpt(ans->line->next->orig, tkt->deltatv);
57737da2899SCharles.Forsyth 	}
57837da2899SCharles.Forsyth 	if (ans->line->next == &tkt->end) {
57937da2899SCharles.Forsyth 		Point ep = subpt(tkt->end.orig, tkt->deltatv);
58037da2899SCharles.Forsyth 		if (ep.y < y)
58137da2899SCharles.Forsyth 			x = 1000000;
58237da2899SCharles.Forsyth 	}
58337da2899SCharles.Forsyth 
58437da2899SCharles.Forsyth 	while(ans->item->next != nil) {
58537da2899SCharles.Forsyth 		i = ans->item;
58637da2899SCharles.Forsyth 		w = i->width;
58737da2899SCharles.Forsyth 		if(p.x+w > x) {
58837da2899SCharles.Forsyth 			n = tktposcount(i);
58937da2899SCharles.Forsyth 			if(n > 1) {
59037da2899SCharles.Forsyth 				for(k = 0; k < n; k++) {
59137da2899SCharles.Forsyth 					/* probably wrong w.r.t tag tabs */
59237da2899SCharles.Forsyth 					w = tktdispwidth(tk, nil, i, nil, p.x, k, 1);
59337da2899SCharles.Forsyth 					if(p.x+w > x) {
59437da2899SCharles.Forsyth 						ans->pos = k;
59537da2899SCharles.Forsyth 						break;
59637da2899SCharles.Forsyth 					}
59737da2899SCharles.Forsyth 					p.x += w;
59837da2899SCharles.Forsyth 				}
59937da2899SCharles.Forsyth 			}
60037da2899SCharles.Forsyth 			secondhalf = (p.x + w/2 <= x);
60137da2899SCharles.Forsyth 			break;
60237da2899SCharles.Forsyth 		}
60337da2899SCharles.Forsyth 		p.x += w;
60437da2899SCharles.Forsyth 		if(!tktadjustind(tkt, TkTbyitem, ans))
60537da2899SCharles.Forsyth 			break;
60637da2899SCharles.Forsyth 	}
60737da2899SCharles.Forsyth 	tktadjustind(tkt, TkTbycharstart, ans);
60837da2899SCharles.Forsyth 	return secondhalf;
60937da2899SCharles.Forsyth }
61037da2899SCharles.Forsyth 
611