xref: /inferno-os/libtk/tmark.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
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* tktmarkgravity(Tk*, char*, char**);
12 static char* tktmarknames(Tk*, char*, char**);
13 static char* tktmarknext(Tk*, char*, char**);
14 static char* tktmarkprevious(Tk*, char*, char**);
15 static char* tktmarkset(Tk*, char*, char**);
16 static char* tktmarkunset(Tk*, char*, char**);
17 
18 TkCmdtab
19 tktmarkcmd[] =
20 {
21 	"gravity",		tktmarkgravity,
22 	"names",		tktmarknames,
23 	"next",		tktmarknext,
24 	"previous",	tktmarkprevious,
25 	"set",		tktmarkset,
26 	"unset",		tktmarkunset,
27 	nil
28 };
29 
30 char*
tktaddmarkinfo(TkText * tkt,char * name,TkTmarkinfo ** ret)31 tktaddmarkinfo(TkText *tkt, char *name, TkTmarkinfo **ret)
32 {
33 	TkTmarkinfo *mi;
34 
35 	mi = malloc(sizeof(TkTmarkinfo));
36 	if(mi == nil)
37 		return TkNomem;
38 
39 	mi->name = strdup(name);
40 	if(mi->name == nil) {
41 		free(mi);
42 		return TkNomem;
43 	}
44 	mi->gravity = Tkright;
45 	mi->cur = nil;
46 	mi->next = tkt->marks;
47 	tkt->marks = mi;
48 	*ret = mi;
49 	return nil;
50 }
51 
52 void
tktfreemarks(TkTmarkinfo * m)53 tktfreemarks(TkTmarkinfo *m)
54 {
55 	TkTmarkinfo *n;
56 
57 	while(m != nil) {
58 		n = m->next;
59 		free(m->name);
60 		free(m);
61 		m = n;
62 	}
63 }
64 
65 TkTmarkinfo *
tktfindmark(TkTmarkinfo * m,char * name)66 tktfindmark(TkTmarkinfo *m, char *name)
67 {
68 	while(m != nil) {
69 		if(strcmp(m->name, name) == 0)
70 			return m;
71 		m = m->next;
72 	}
73 	return nil;
74 }
75 
76 int
tktmarkind(Tk * tk,char * name,TkTindex * ans)77 tktmarkind(Tk *tk, char *name, TkTindex *ans)
78 {
79 	TkTmarkinfo *mk;
80 	TkText *tkt = TKobj(TkText, tk);
81 
82 	if(strcmp(name, "current") == 0) {
83 		tktxyind(tk, tkt->current.x, tkt->current.y, ans);
84 		return 1;
85 	}
86 
87 	mk = tktfindmark(tkt->marks, name);
88 	if(mk == nil || mk->cur == nil)
89 		return 0;
90 
91 	ans->item = mk->cur;
92 	ans->line = tktitemline(ans->item);
93 	ans->pos = 0;
94 	return 1;
95 }
96 
97 char*
tktmarkparse(Tk * tk,char ** parg,TkTmarkinfo ** ret)98 tktmarkparse(Tk *tk, char **parg, TkTmarkinfo **ret)
99 {
100 	char *e, *buf;
101 	TkText *tkt = TKobj(TkText, tk);
102 
103 	buf = mallocz(Tkmaxitem, 0);
104 	if(buf == nil)
105 		return TkNomem;
106 
107 	*parg = tkword(tk->env->top, *parg, buf, buf+Tkmaxitem, nil);
108 	if(*buf == '\0') {
109 		free(buf);
110 		return TkOparg;
111 	}
112 
113 	*ret = tktfindmark(tkt->marks, buf);
114 	if(*ret == nil) {
115 		e = tktaddmarkinfo(tkt, buf, ret);
116 		if(e != nil) {
117 			free(buf);
118 			return e;
119 		}
120 	}
121 	free(buf);
122 
123 	return nil;
124 }
125 
126 /*
127  * Insert mark before ixnew, first removing it from old place, if any.
128  * Make sure ixnew continues to point after mark.
129  */
130 char*
tktmarkmove(Tk * tk,TkTmarkinfo * m,TkTindex * ixnew)131 tktmarkmove(Tk *tk, TkTmarkinfo *m, TkTindex *ixnew)
132 {
133 	char *e;
134 	int deleted, split;
135 	TkTitem *i;
136 	TkTindex ix, pix;
137 	TkText *tkt = TKobj(TkText, tk);
138 
139 	deleted = 0;
140 	if(m->cur != nil) {
141 		if(m->cur == ixnew->item)
142 			return nil;
143 		ix.item = m->cur;
144 		ix.line = tktitemline(m->cur);
145 		ix.pos = 0;
146 		tktremitem(tkt, &ix);
147 		deleted = 1;
148 	}
149 
150 	/* XXX - Tad: memory leak on 'i' if something fails later? */
151 	e = tktnewitem(TkTmark, 0, &i);
152 	if(e != nil)
153 		return e;
154 
155 	i->imark = m;
156 	m->cur = i;
157 
158 	/* keep adjacent marks sorted: all rights, then all lefts */
159 	if(m->gravity == Tkright) {
160 		while(ixnew->item->kind == TkTmark && ixnew->item->imark->gravity == Tkleft)
161 			if(!tktadjustind(tkt, TkTbyitem, ixnew))
162 				break;
163 	}
164 	else {
165 		for(;;) {
166 			pix = *ixnew;
167 			if(!tktadjustind(tkt, TkTbyitemback, &pix))
168 				break;
169 			if(pix.item->kind == TkTmark && pix.item->imark->gravity == Tkright)
170 				*ixnew = pix;
171 			else
172 				break;
173 		}
174 	}
175 
176 	split = (ixnew->pos > 0);
177 	e = tktsplititem(ixnew);
178 	if(e != nil)
179 		return e;
180 
181 	e = tktiteminsert(tkt, ixnew, i);
182 	if(e != nil)
183 		return nil;
184 
185 	if(strcmp(m->name, "insert") == 0 || split) {
186 		if(deleted && ix.line != ixnew->line) {
187 			tktfixgeom(tk, tktprevwrapline(tk, ix.line), ix.line, 0);
188 			/*
189 			 * this is ok only because tktfixgeom cannot
190 			 * free mark items, and we know that i is a mark item.
191 			 */
192 			ixnew->item = i;
193 			ixnew->line = tktitemline(i);
194 			ixnew->pos = 0;
195 		}
196 		tktfixgeom(tk, tktprevwrapline(tk, ixnew->line), ixnew->line, 0);
197 		tktextsize(tk, 1);
198 	}
199 
200 	ixnew->item = i;
201 	ixnew->line = tktitemline(i);
202 	ixnew->pos = 0;
203 	return nil;
204 }
205 
206 /* Text Mark Commands (+ means implemented)
207 	+gravity
208 	+names
209 	+next
210 	+previous
211 	+set
212 	+unset
213 */
214 
215 static char*
tktmarkgravity(Tk * tk,char * arg,char ** val)216 tktmarkgravity(Tk *tk, char *arg, char **val)
217 {
218 	char *e;
219 	TkTmarkinfo *m;
220 	char *buf;
221 
222 	e = tktmarkparse(tk, &arg, &m);
223 	if(e != nil)
224 		return e;
225 
226 	if(*arg == '\0')
227 		return tkvalue(val, (m->gravity & Tkleft)? "left" : "right");
228 	else {
229 		buf = mallocz(Tkmaxitem, 0);
230 		if(buf == nil)
231 			return TkNomem;
232 		tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
233 		if(strcmp(buf, "left") == 0)
234 			m->gravity = Tkleft;
235 		else
236 		if(strcmp(buf, "right") == 0)
237 			m->gravity = Tkright;
238 		else {
239 			free(buf);
240 			return TkBadcm;
241 		}
242 		free(buf);
243 	}
244 	return nil;
245 }
246 
247 static char*
tktmarknames(Tk * tk,char * arg,char ** val)248 tktmarknames(Tk *tk, char *arg, char **val)
249 {
250 	char *r, *fmt;
251 	TkTmarkinfo *m;
252 	TkText *tkt = TKobj(TkText, tk);
253 
254 	USED(arg);
255 
256 	fmt = "%s";
257 	for(m = tkt->marks; m != nil; m = m->next) {
258 		r = tkvalue(val, fmt, m->name);
259 		if(r != nil)
260 			return r;
261 		fmt = " %s";
262 	}
263 	return nil;
264 }
265 
266 static char*
tktmarknext(Tk * tk,char * arg,char ** val)267 tktmarknext(Tk *tk, char *arg, char **val)
268 {
269 	char *e;
270 	TkTmarkinfo *mix;
271 	TkTindex ix, ixend;
272 	TkText *tkt = TKobj(TkText, tk);
273 
274 	/* special behavior if specified index is a mark name */
275 	mix = tktfindmark(tkt->marks, arg);
276 
277 	e = tktindparse(tk, &arg, &ix);
278 	if(e != nil)
279 		return e;
280 
281 	if(mix != nil)
282 		tktadjustind(tkt, TkTbyitem, &ix);
283 
284 	/* special behavior if index is 'end' */
285 	tktendind(tkt, &ixend);
286 	if(tktindcompare(tkt, &ix, TkEq, &ixend)) {
287 		do {
288 			tktadjustind(tkt, TkTbyitemback, &ix);
289 		} while(ix.item->kind == TkTmark);
290 	}
291 
292 	do {
293 		if(ix.item->kind == TkTmark)
294 			return tkvalue(val, "%s", ix.item->imark->name);
295 
296 	} while(tktadjustind(tkt, TkTbyitem, &ix));
297 
298 	return nil;
299 }
300 
301 static char*
tktmarkprevious(Tk * tk,char * arg,char ** val)302 tktmarkprevious(Tk *tk, char *arg, char **val)
303 {
304 	char *e;
305 	TkTindex ix;
306 	TkText *tkt = TKobj(TkText, tk);
307 
308 	e = tktindparse(tk, &arg, &ix);
309 	if(e != nil)
310 		return e;
311 
312 	while(tktadjustind(tkt, TkTbyitemback, &ix)) {
313 		if(ix.item->kind == TkTmark)
314 			return tkvalue(val, "%s", ix.item->imark->name);
315 	}
316 
317 	return nil;
318 }
319 
320 /* XXX - Tad: possible memory leak here */
321 static char*
tktmarkset(Tk * tk,char * arg,char ** val)322 tktmarkset(Tk *tk, char *arg, char **val)
323 {
324 	char *e;
325 	TkTmarkinfo *m;
326 	TkTindex ixnew;
327 
328 	USED(val);
329 
330 	e = tktmarkparse(tk, &arg, &m);
331 	if(e != nil)
332 		return e;
333 	e = tktindparse(tk, &arg, &ixnew);
334 	if(e != nil)
335 		return e;
336 
337 	return tktmarkmove(tk, m, &ixnew);
338 }
339 
340 static char*
tktmarkunset(Tk * tk,char * arg,char ** val)341 tktmarkunset(Tk *tk, char *arg, char **val)
342 {
343 	TkText *tkt;
344 	TkTmarkinfo *m, **p;
345 	TkTindex ix;
346 	char *e;
347 	int resize;
348 
349 	USED(val);
350 
351 	tkt = TKobj(TkText, tk);
352 
353 	e = tktmarkparse(tk, &arg, &m);
354 	if(e != nil)
355 		return e;
356 
357 	resize = 0;
358 	while(m != nil) {
359 		if(strcmp(m->name, "insert") == 0 || strcmp(m->name, "current") == 0)
360 			return TkBadvl;
361 
362 		if(m->cur != nil) {
363 			ix.item = m->cur;
364 			ix.line = tktitemline(m->cur);
365 			ix.pos = 0;
366 			tktremitem(tkt, &ix);
367 			tktfixgeom(tk, tktprevwrapline(tk, ix.line), ix.line, 0);
368 			resize = 1;
369 		}
370 
371 		for(p = &tkt->marks; *p != nil; p = &(*p)->next) {
372 			if(*p == m) {
373 				*p = m->next;
374 				break;
375 			}
376 		}
377 		m->next = nil;
378 		tktfreemarks(m);
379 
380 		if(*arg != '\0') {
381 			e = tktmarkparse(tk, &arg, &m);
382 			if(e != nil)
383 				return e;
384 		}
385 		else
386 			m = nil;
387 	}
388 	if (resize)
389 		tktextsize(tk, 1);
390 	return nil;
391 }
392 
393