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