1 /* radare - LGPL - Copyright 2013-2020 - pancake */
2 
3 #include <r_cons.h>
4 #include <r_util/r_assert.h>
5 
6 #define useUtf8 (r_cons_singleton ()->use_utf8)
7 #define useUtf8Curvy (r_cons_singleton ()->use_utf8_curvy)
8 
9 #define W(y) r_cons_canvas_write (c, y)
10 #define G(x, y) r_cons_canvas_gotoxy (c, x, y)
11 
__isAnsiSequence(const char * s)12 static inline bool __isAnsiSequence(const char *s) {
13 	return s && s[0] == 033 && s[1] == '[';
14 }
15 
__getAnsiPiece(const char * p,char * chr)16 static int __getAnsiPiece(const char *p, char *chr) {
17 	const char *q = p;
18 	if (!p) {
19 		return 0;
20 	}
21 	while (p && *p && *p != '\n' && ! __isAnsiSequence (p)) {
22 		p++;
23 	}
24 	if (chr) {
25 		*chr = *p;
26 	}
27 	return p - q;
28 }
29 
attribute_free_kv(HtUPKv * kv)30 static void attribute_free_kv(HtUPKv *kv) {
31 	free (kv->value);
32 }
33 
__attributeAt(RConsCanvas * c,int loc)34 static const char *__attributeAt(RConsCanvas *c, int loc) {
35 	if (!c->color) {
36 		return NULL;
37 	}
38 	return ht_up_find (c->attrs, loc, NULL);
39 }
40 
__stampAttribute(RConsCanvas * c,int loc,int length)41 static void __stampAttribute(RConsCanvas *c, int loc, int length) {
42 	if (!c->color) {
43 		return;
44 	}
45 	int i;
46 	ht_up_update (c->attrs, loc, (void *)c->attr);
47 	for (i = 1; i < length; i++) {
48 		ht_up_delete (c->attrs, loc + i);
49 	}
50 }
51 
52 /* check for ANSI sequences and use them as attr */
set_attr(RConsCanvas * c,const char * s)53 static const char *set_attr(RConsCanvas *c, const char *s) {
54 	if (!c || !s) {
55 		return NULL;
56 	}
57 	const char *p = s;
58 
59 	while (__isAnsiSequence (p)) {
60 		p += 2;
61 		while (*p && *p != 'J' && *p != 'm' && *p != 'H') {
62 			p++;
63 		}
64 		p++;
65 	}
66 
67 	const int slen = p - s;
68 	if (slen > 0) {
69 		RStrBuf tmp;
70 		r_strbuf_init (&tmp);
71 		r_strbuf_append_n (&tmp, s, slen);
72 		c->attr = r_str_constpool_get (&c->constpool, r_strbuf_get (&tmp));
73 		r_strbuf_fini (&tmp);
74 	}
75 	return p;
76 }
77 
__getUtf8Length(const char * s,int n)78 static int __getUtf8Length(const char *s, int n) {
79 	int i = 0, j = 0, fullwidths = 0;
80 	while (s[i] && n > 0) {
81 		if ((s[i] & 0xc0) != 0x80) {
82 			j++;
83 			if (r_str_char_fullwidth (s + i, n)) {
84 				fullwidths++;
85 			}
86 		}
87 		n--;
88 		i++;
89 	}
90 	return j + fullwidths;
91 }
92 
__getUtf8Length2(const char * s,int n,int left)93 static int __getUtf8Length2(const char *s, int n, int left) {
94 	int i = 0, fullwidths = 0;
95 	while (n > -1 && i < left && s[i]) {
96 		if (r_str_char_fullwidth (s + i, left - i)) {
97 			fullwidths++;
98 		}
99 		if ((s[i] & 0xc0) != 0x80) {
100 			n--;
101 		}
102 		i++;
103 	}
104 	i -= fullwidths;
105 	return n == -1 ? i - 1 : i;
106 }
107 
__expandLine(RConsCanvas * c,int real_len,int utf8_len)108 static bool __expandLine(RConsCanvas *c, int real_len, int utf8_len) {
109 	if (real_len == 0) {
110 		return true;
111 	}
112 	int buf_utf8_len = __getUtf8Length2 (c->b[c->y] + c->x, utf8_len, c->blen[c->y] - c->x);
113 	int goback = R_MAX (0, (buf_utf8_len - utf8_len));
114 	int padding = (real_len - utf8_len) - goback;
115 
116 	if (padding) {
117 		if (padding > 0 && c->blen[c->y] + padding > c->bsize[c->y]) {
118 			int newsize = R_MAX (c->bsize[c->y] * 1.5, c->blen[c->y] + padding);
119 			char * newline = realloc (c->b[c->y], sizeof (*c->b[c->y])*(newsize));
120 			if (!newline) {
121 				return false;
122 			}
123 			memset (newline + c->bsize[c->y], 0, newsize - c->bsize[c->y]);
124 			c->b[c->y] = newline;
125 			c->bsize[c->y] = newsize;
126 		}
127 		int size = R_MAX (c->blen[c->y] - c->x - goback, 0);
128 		char *start = c->b[c->y] + c->x + goback;
129 		char *tmp = malloc (size);
130 		if (!tmp) {
131 			return false;
132 		}
133 		memcpy (tmp, start, size);
134 		if (padding < 0) {
135 			int lap = R_MAX (0, c->b[c->y] - (start + padding));
136 			memcpy (start + padding + lap,  tmp + lap, size - lap);
137 			free (tmp);
138 			c->blen[c->y] += padding;
139 			return true;
140 		}
141 		memcpy (start + padding, tmp, size);
142 		free (tmp);
143 		c->blen[c->y] += padding;
144 	}
145 	return true;
146 }
147 
r_cons_canvas_free(RConsCanvas * c)148 R_API void r_cons_canvas_free(RConsCanvas *c) {
149 	if (!c) {
150 		return;
151 	}
152 	if (c->b) {
153 		int y;
154 		for (y = 0; y < c->h; y++) {
155 			free (c->b[y]);
156 		}
157 		free (c->b);
158 	}
159 	free (c->bsize);
160 	free (c->blen);
161 	ht_up_free (c->attrs);
162 	r_str_constpool_fini (&c->constpool);
163 	free (c);
164 }
165 
attribute_delete_cb(void * user,const ut64 key,const void * value)166 static bool attribute_delete_cb(void *user, const ut64 key, const void *value) {
167 	HtUP *ht = (HtUP *)user;
168 	ht_up_delete (ht, key);
169 	return true;
170 }
171 
r_cons_canvas_clear(RConsCanvas * c)172 R_API void r_cons_canvas_clear(RConsCanvas *c) {
173 	r_return_if_fail (c && c->b);
174 	int y;
175 	for (y = 0; y < c->h; y++) {
176 		memset (c->b[y], '\n', c->bsize[y]);
177 	}
178 
179 	ht_up_foreach (c->attrs, attribute_delete_cb, c->attrs);
180 }
181 
r_cons_canvas_gotoxy(RConsCanvas * c,int x,int y)182 R_API bool r_cons_canvas_gotoxy(RConsCanvas *c, int x, int y) {
183 	bool ret = true;
184 	if (!c) {
185 		return 0;
186 	}
187 	y += c->sy;
188 	x += c->sx;
189 
190 	if (y > c->h * 2) {
191 		return false;
192 	}
193 	if (y >= c->h) {
194 		y = c->h - 1;
195 		ret = false;
196 	}
197 	if (y < 0) {
198 		y = 0;
199 		ret = false;
200 	}
201 	if (x < 0) {
202 		//c->x = 0;
203 		ret = false;
204 	}
205 	if (x > c->blen[y] * 2) {
206 		return false;
207 	}
208 	if (x >= c->blen[y]) {
209 		c->x = c->blen[y];
210 		ret = false;
211 	}
212 	if (x < c->blen[y] && x >= 0) {
213 		c->x = x;
214 	}
215 	if (y < c->h) {
216 		c->y = y;
217 	}
218 	return ret;
219 }
220 
r_cons_canvas_new(int w,int h)221 R_API RConsCanvas *r_cons_canvas_new(int w, int h) {
222 	if (w < 1 || h < 1) {
223 		return NULL;
224 	}
225 	RConsCanvas *c = R_NEW0 (RConsCanvas);
226 	if (!c) {
227 		return NULL;
228 	}
229 	c->bsize = NULL;
230 	c->blen = NULL;
231 	int i = 0;
232 	c->color = 0;
233 	c->sx = 0;
234 	c->sy = 0;
235 	c->b = malloc (sizeof *c->b * h);
236 	if (!c->b) {
237 		goto beach;
238 	}
239 	c->blen = malloc (sizeof *c->blen * h);
240 	if (!c->blen) {
241 		goto beach;
242 	}
243 	c->bsize = malloc (sizeof *c->bsize * h);
244 	if (!c->bsize) {
245 		goto beach;
246 	}
247 	for (i = 0; i < h; i++) {
248 		c->b[i] = malloc (w + 1);
249 		c->blen[i] = w;
250 		c->bsize[i] = w + 1;
251 		if (!c->b[i]) {
252 			goto beach;
253 		}
254 	}
255 	c->w = w;
256 	c->h = h;
257 	c->x = c->y = 0;
258 	if (!r_str_constpool_init (&c->constpool)) {
259 		goto beach;
260 	}
261 	c->attrs = ht_up_new ((HtUPDupValue)strdup, attribute_free_kv, NULL);
262 	if (!c->attrs) {
263 		goto beach;
264 	}
265 	c->attr = Color_RESET;
266 	r_cons_canvas_clear (c);
267 	return c;
268 beach:
269 	r_str_constpool_fini (&c->constpool);
270 	int j;
271 	for (j = 0; j < i; j++) {
272 		free (c->b[j]);
273 	}
274 	free (c->bsize);
275 	free (c->blen);
276 	free (c->b);
277 	free (c);
278 	return NULL;
279 }
280 
r_cons_canvas_write(RConsCanvas * c,const char * s)281 R_API void r_cons_canvas_write(RConsCanvas *c, const char *s) {
282 	if (!c || !s || !*s || !R_BETWEEN (0, c->y, c->h - 1) || !R_BETWEEN (0, c->x, c->w - 1)) {
283 		return;
284 	}
285 
286 	char ch;
287 	int left, slen, attr_len, piece_len;
288 	int orig_x = c->x, attr_x = c->x;
289 
290 	c->x = __getUtf8Length2 (c->b[c->y], c->x, c->blen[c->y]);
291 
292 	/* split the string into pieces of non-ANSI chars and print them normally,
293 	** using the ANSI chars to set the attr of the canvas */
294 	r_cons_break_push (NULL, NULL);
295 	do {
296 		const char *s_part = set_attr (c, s);
297 		ch = 0;
298 		piece_len = __getAnsiPiece (s_part, &ch);
299 		if (piece_len == 0 && ch == '\0' && s_part == s) {
300 			break;
301 		}
302 		left = c->blen[c->y] - c->x;
303 		slen = piece_len;
304 
305 		if (piece_len > left) {
306 			int utf8_piece_len = __getUtf8Length (s_part, piece_len);
307 			if (utf8_piece_len > c->w - attr_x) {
308 				slen = left;
309 			}
310 		}
311 
312 		int real_len = r_str_nlen (s_part, slen);
313 		int utf8_len = __getUtf8Length (s_part, slen);
314 
315 		if (!__expandLine (c, real_len, utf8_len)) {
316 			break;
317 		}
318 
319 		if (G (c->x - c->sx, c->y - c->sy)) {
320 			memcpy (c->b[c->y] + c->x, s_part, slen);
321 		}
322 
323 		attr_len = slen <= 0 && s_part != s? 1: utf8_len;
324 		if (attr_len > 0 && attr_x < c->blen[c->y]) {
325 			__stampAttribute (c, c->y * c->w + attr_x, attr_len);
326 		}
327 
328 		s = s_part;
329 		if (ch == '\n') {
330 			c->attr = Color_RESET;
331 			__stampAttribute (c, c->y * c->w + attr_x, 0);
332 			c->y++;
333 			s++;
334 			if (*s == '\0' || c->y  >= c->h) {
335 				break;
336 			}
337 			c->x = __getUtf8Length2 (c->b[c->y], orig_x, c->blen[c->y]);
338 			attr_x = orig_x;
339 		} else {
340 			c->x += slen;
341 			attr_x += utf8_len;
342 		}
343 		s += piece_len;
344 	} while (*s && !r_cons_is_breaked ());
345 	r_cons_break_pop ();
346 	c->x = orig_x;
347 }
348 
r_cons_canvas_to_string(RConsCanvas * c)349 R_API char *r_cons_canvas_to_string(RConsCanvas *c) {
350 	r_return_val_if_fail (c, NULL);
351 
352 	int x, y, olen = 0, attr_x = 0;
353 	bool is_first = true;
354 
355 	for (y = 0; y < c->h; y++) {
356 		olen += c->blen[y] + 1;
357 	}
358 	char *o = calloc (1, olen * 4 * CONS_MAX_ATTR_SZ);
359 	if (!o) {
360 		return NULL;
361 	}
362 	if (!olen) {
363 		free (o);
364 		return NULL;
365 	}
366 
367 	olen = 0;
368 	for (y = 0; y < c->h; y++) {
369 		if (!is_first) {
370 			o[olen++] = '\n';
371 		}
372 		is_first = false;
373 		attr_x = 0;
374 		for (x = 0; x < c->blen[y]; x++) {
375 			if ((c->b[y][x] & 0xc0) != 0x80) {
376 				const char *atr = __attributeAt (c, y * c->w + attr_x);
377 				if (atr) {
378 					size_t len = strlen (atr);
379 					memcpy (o + olen, atr, len);
380 					olen += len;
381 				}
382 				attr_x++;
383 				if (r_str_char_fullwidth (c->b[y] + x, c->blen[y] - x)) {
384 					attr_x++;
385 				}
386 			}
387 			if (!c->b[y][x] || c->b[y][x] == '\n') {
388 				o[olen++] = ' ';
389 				continue;
390 			}
391 			const char *rune = r_cons_get_rune ((const ut8)c->b[y][x]);
392 			if (rune) {
393 				size_t rune_len = strlen (rune);
394 				memcpy (o + olen, rune, rune_len + 1);
395 				olen += rune_len;
396 			} else {
397 				o[olen++] = c->b[y][x];
398 			}
399 		}
400 		while (olen > 0 && o[olen - 1] == ' ') {
401 			o[--olen] = '\0';
402 		}
403 	}
404 	o[olen] = '\0';
405 	return o;
406 }
407 
r_cons_canvas_print_region(RConsCanvas * c)408 R_API void r_cons_canvas_print_region(RConsCanvas *c) {
409 	char *o = r_cons_canvas_to_string (c);
410 	if (o) {
411 		r_str_trim_tail (o);
412 		if (*o) {
413 			r_cons_strcat (o);
414 		}
415 		free (o);
416 	}
417 }
418 
r_cons_canvas_print(RConsCanvas * c)419 R_API void r_cons_canvas_print(RConsCanvas *c) {
420 	char *o = r_cons_canvas_to_string (c);
421 	if (o) {
422 		r_cons_strcat (o);
423 		free (o);
424 	}
425 }
426 
r_cons_canvas_resize(RConsCanvas * c,int w,int h)427 R_API int r_cons_canvas_resize(RConsCanvas *c, int w, int h) {
428 	if (!c || w < 0 || h <= 0) {
429 		return false;
430 	}
431 	int *newblen = realloc (c->blen, sizeof *c->blen * h);
432 	if (!newblen) {
433 		r_cons_canvas_free (c);
434 		return false;
435 	}
436 	c->blen = newblen;
437 	int *newbsize = realloc (c->bsize, sizeof *c->bsize * h);
438 	if (!newbsize) {
439 		r_cons_canvas_free (c);
440 		return false;
441 	}
442 	c->bsize = newbsize;
443 	char **newb = realloc (c->b, sizeof *c->b * h);
444 	if (!newb) {
445 		r_cons_canvas_free (c);
446 		return false;
447 	}
448 	c->b = newb;
449 	int i;
450 	char *newline = NULL;
451 	for (i = 0; i < h; i++) {
452 		if (i < c->h) {
453 			newline = realloc (c->b[i], sizeof *c->b[i] * (w + 1));
454 		} else {
455 			newline = malloc (w + 1);
456 		}
457 		c->blen[i] = w;
458 		c->bsize[i] = w + 1;
459 		if (!newline) {
460 			size_t j;
461 			for (j = 0; j <= i; j++) {
462 				free (c->b[i]);
463 			}
464 			ht_up_free (c->attrs);
465 			free (c->blen);
466 			free (c->bsize);
467 			free (c->b);
468 			free (c);
469 			return false;
470 		}
471 		c->b[i] = newline;
472 	}
473 	c->w = w;
474 	c->h = h;
475 	c->x = 0;
476 	c->y = 0;
477 	r_cons_canvas_clear (c);
478 	return true;
479 }
480 
481 #include <math.h>
482 #define PI 3.1415
r_cons_canvas_circle(RConsCanvas * c,int x,int y,int w,int h,const char * color)483 R_API void r_cons_canvas_circle(RConsCanvas *c, int x, int y, int w, int h, const char *color) {
484 	if (color) {
485 		c->attr = color;
486 	}
487 	double xfactor = 1; //(double)w / (double)h;
488 	double yfactor = (double)h / 24; // 0.8; // 24  10
489 	double size = w;
490 	float a = 0.0;
491 	double s = size / 2;
492 	while (a < (2 * PI)) {
493 		double sa = r_num_sin (a);
494 		double ca = r_num_cos (a);
495 		double cx = s * ca + (size / 2);
496 		double cy = s * sa + (size / 4);
497 		int X = x + (xfactor * cx) - 2;
498 		int Y = y + ((yfactor/2) * cy);
499 		if (G (X, Y)) {
500 			W ("=");
501 		}
502 		a += 0.1;
503 	}
504 	if (color) {
505 		c->attr = Color_RESET;
506 	}
507 }
508 
r_cons_canvas_box(RConsCanvas * c,int x,int y,int w,int h,const char * color)509 R_API void r_cons_canvas_box(RConsCanvas *c, int x, int y, int w, int h, const char *color) {
510 	const char *hline = useUtf8? RUNECODESTR_LINE_HORIZ : "-";
511 	const char *vtmp = useUtf8? RUNECODESTR_LINE_VERT : "|";
512 	RStrBuf *vline = r_strbuf_new (NULL);
513 	r_strbuf_appendf (vline, Color_RESET"%s%s", color, vtmp);
514 	const char *tl_corner = useUtf8 ? (useUtf8Curvy ? RUNECODESTR_CURVE_CORNER_TL : RUNECODESTR_CORNER_TL) : ".";
515 	const char *tr_corner = useUtf8 ? (useUtf8Curvy ? RUNECODESTR_CURVE_CORNER_TR : RUNECODESTR_CORNER_TR) : ".";
516 	const char *bl_corner = useUtf8 ? (useUtf8Curvy ? RUNECODESTR_CURVE_CORNER_BL : RUNECODESTR_CORNER_BL) : "`";
517 	const char *br_corner = useUtf8 ? (useUtf8Curvy ? RUNECODESTR_CURVE_CORNER_BR : RUNECODESTR_CORNER_BR) : "'";
518 	int i, x_mod;
519 	int roundcorners = 0;
520 	char *row = NULL, *row_ptr;
521 
522 	if (w < 1 || h < 1) {
523 		return;
524 	}
525 	if (color) {
526 		c->attr = color;
527 	}
528 	if (!c->color) {
529 		c->attr = Color_RESET;
530 	}
531 	row = malloc (w + 1);
532 	if (!row) {
533 		return;
534 	}
535 	row[0] = roundcorners? '.': tl_corner[0];
536 	if (w > 2) {
537 		memset (row + 1, hline[0], w - 2);
538 	}
539 	if (w > 1) {
540 		row[w - 1] = roundcorners? '.': tr_corner[0];
541 	}
542 	row[w] = 0;
543 
544 	row_ptr = row;
545 	x_mod = x;
546 	if (x < -c->sx) {
547 		x_mod = R_MIN (-c->sx, x_mod + w);
548 		row_ptr += x_mod - x;
549 	}
550 	if (G (x_mod, y)) {
551 		W (row_ptr);
552 	}
553 	if (G (x_mod, y + h - 1)) {
554 		row[0] = roundcorners? '\'': bl_corner[0];
555 		row[w - 1] = roundcorners? '\'': br_corner[0];
556 		W (row_ptr);
557 	}
558 	for (i = 1; i < h - 1; i++) {
559 		if (G (x, y + i)) {
560 			W (r_strbuf_get (vline));
561 		}
562 		if (G (x + w - 1, y + i)) {
563 			W (r_strbuf_get (vline));
564 		}
565 	}
566 	free (row);
567 	r_strbuf_free (vline);
568 	if (color) {
569 		c->attr = Color_RESET;
570 	}
571 }
572 
r_cons_canvas_fill(RConsCanvas * c,int x,int y,int w,int h,char ch)573 R_API void r_cons_canvas_fill(RConsCanvas *c, int x, int y, int w, int h, char ch) {
574 	int i;
575 	if (w < 0) {
576 		return;
577 	}
578 	char *row = malloc (w + 1);
579 	if (!row) {
580 		return;
581 	}
582 	memset (row, ch, w);
583 	row[w] = 0;
584 	for (i = 0; i < h; i++) {
585 		if (G (x, y + i)) {
586 			W (row);
587 		}
588 	}
589 	free (row);
590 }
591 
r_cons_canvas_line(RConsCanvas * c,int x,int y,int x2,int y2,RCanvasLineStyle * style)592 R_API void r_cons_canvas_line(RConsCanvas *c, int x, int y, int x2, int y2, RCanvasLineStyle *style) {
593 	if (c->linemode) {
594 		r_cons_canvas_line_square (c, x, y, x2, y2, style);
595 	} else {
596 		r_cons_canvas_line_diagonal (c, x, y, x2, y2, style);
597 	}
598 }
599