1 /* radare2 - LGPL - Copyright 2008-2020 - pancake, Jody Frankowski */
2
3 #include <r_cons.h>
4 #include <r_util.h>
5 #include <r_util/r_print.h>
6 #include <limits.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11
12 #define COUNT_LINES 1
13 #define CTX(x) I.context->x
14
15 R_LIB_VERSION (r_cons);
16
17 static RConsContext r_cons_context_default = {{{{0}}}};
18 static RCons r_cons_instance = {0};
19 #define I r_cons_instance
20
21 //this structure goes into cons_stack when r_cons_push/pop
22 typedef struct {
23 char *buf;
24 int buf_len;
25 int buf_size;
26 RConsGrep *grep;
27 } RConsStack;
28
29 typedef struct {
30 bool breaked;
31 RConsEvent event_interrupt;
32 void *event_interrupt_data;
33 } RConsBreakStack;
34
35 static void cons_grep_reset(RConsGrep *grep);
36
break_stack_free(void * ptr)37 static void break_stack_free(void *ptr) {
38 RConsBreakStack *b = (RConsBreakStack*)ptr;
39 free (b);
40 }
41
cons_stack_free(void * ptr)42 static void cons_stack_free(void *ptr) {
43 RConsStack *s = (RConsStack *)ptr;
44 free (s->buf);
45 if (s->grep) {
46 R_FREE (s->grep->str);
47 CTX (grep.str) = NULL;
48 }
49 free (s->grep);
50 free (s);
51 }
52
cons_stack_dump(bool recreate)53 static RConsStack *cons_stack_dump(bool recreate) {
54 RConsStack *data = R_NEW0 (RConsStack);
55 if (data) {
56 if (CTX (buffer)) {
57 data->buf = CTX (buffer);
58 data->buf_len = CTX (buffer_len);
59 data->buf_size = CTX (buffer_sz);
60 }
61 data->grep = R_NEW0 (RConsGrep);
62 if (data->grep) {
63 memcpy (data->grep, &I.context->grep, sizeof (RConsGrep));
64 if (I.context->grep.str) {
65 data->grep->str = strdup (I.context->grep.str);
66 }
67 }
68 if (recreate && I.context->buffer_sz > 0) {
69 I.context->buffer = malloc (I.context->buffer_sz);
70 if (!I.context->buffer) {
71 I.context->buffer = data->buf;
72 free (data);
73 return NULL;
74 }
75 } else {
76 I.context->buffer = NULL;
77 }
78 }
79 return data;
80 }
81
cons_stack_load(RConsStack * data,bool free_current)82 static void cons_stack_load(RConsStack *data, bool free_current) {
83 r_return_if_fail (data);
84 if (free_current) {
85 free (I.context->buffer);
86 }
87 I.context->buffer = data->buf;
88 data->buf = NULL;
89 I.context->buffer_len = data->buf_len;
90 I.context->buffer_sz = data->buf_size;
91 if (data->grep) {
92 free (I.context->grep.str);
93 memcpy (&I.context->grep, data->grep, sizeof (RConsGrep));
94 }
95 }
96
cons_context_init(RConsContext * context,R_NULLABLE RConsContext * parent)97 static void cons_context_init(RConsContext *context, R_NULLABLE RConsContext *parent) {
98 context->breaked = false;
99 context->cmd_depth = R_CONS_CMD_DEPTH + 1;
100 context->error = r_strbuf_new ("");
101 context->errmode = R_CONS_ERRMODE_ECHO;
102 context->buffer = NULL;
103 context->buffer_sz = 0;
104 context->lastEnabled = true;
105 context->buffer_len = 0;
106 context->is_interactive = false;
107 context->cons_stack = r_stack_newf (6, cons_stack_free);
108 context->break_stack = r_stack_newf (6, break_stack_free);
109 context->event_interrupt = NULL;
110 context->event_interrupt_data = NULL;
111 context->pageable = true;
112 context->log_callback = NULL;
113
114 if (parent) {
115 context->color_mode = parent->color_mode;
116 r_cons_pal_copy (context, parent);
117 } else {
118 context->color_mode = COLOR_MODE_DISABLED;
119 r_cons_pal_init (context);
120 }
121
122 cons_grep_reset (&context->grep);
123 }
124
cons_context_deinit(RConsContext * context)125 static void cons_context_deinit(RConsContext *context) {
126 R_FREE (context->error);
127 r_stack_free (context->cons_stack);
128 context->cons_stack = NULL;
129 r_stack_free (context->break_stack);
130 context->break_stack = NULL;
131 r_cons_pal_free (context);
132 }
133
__break_signal(int sig)134 static void __break_signal(int sig) {
135 r_cons_context_break (&r_cons_context_default);
136 }
137
__cons_write_ll(const char * buf,int len)138 static inline void __cons_write_ll(const char *buf, int len) {
139 #if __WINDOWS__
140 if (I.vtmode) {
141 (void) write (I.fdout, buf, len);
142 } else {
143 if (I.fdout == 1) {
144 r_cons_w32_print (buf, len, false);
145 } else {
146 (void) write (I.fdout, buf, len);
147 }
148 }
149 #else
150 if (I.fdout < 1) {
151 I.fdout = 1;
152 }
153 (void) write (I.fdout, buf, len);
154 #endif
155 }
156
__cons_write(const char * obuf,int olen)157 static inline void __cons_write(const char *obuf, int olen) {
158 const size_t bucket = 64 * 1024;
159 size_t i;
160 if (olen < 0) {
161 olen = strlen (obuf);
162 }
163 for (i = 0; (i + bucket) < olen; i += bucket) {
164 __cons_write_ll (obuf + i, bucket);
165 }
166 if (i < olen) {
167 __cons_write_ll (obuf + i, olen - i);
168 }
169 }
170
r_cons_color_random(ut8 alpha)171 R_API RColor r_cons_color_random(ut8 alpha) {
172 RColor rcolor = {0};
173 if (I.context->color_mode > COLOR_MODE_16) {
174 rcolor.r = r_num_rand (0xff);
175 rcolor.g = r_num_rand (0xff);
176 rcolor.b = r_num_rand (0xff);
177 rcolor.a = alpha;
178 return rcolor;
179 }
180 int r = r_num_rand (16);
181 switch (r) {
182 case 0: case 1: rcolor = (RColor) RColor_RED; break;
183 case 2: case 3: rcolor = (RColor) RColor_WHITE; break;
184 case 4: case 5: rcolor = (RColor) RColor_GREEN; break;
185 case 6: case 7: rcolor = (RColor) RColor_MAGENTA; break;
186 case 8: case 9: rcolor = (RColor) RColor_YELLOW; break;
187 case 10: case 11: rcolor = (RColor) RColor_CYAN; break;
188 case 12: case 13: rcolor = (RColor) RColor_BLUE; break;
189 case 14: case 15: rcolor = (RColor) RColor_GRAY; break;
190 }
191 if (r & 1) {
192 rcolor.attr = R_CONS_ATTR_BOLD;
193 }
194 return rcolor;
195 }
196
r_cons_color(int fg,int r,int g,int b)197 R_API void r_cons_color(int fg, int r, int g, int b) {
198 int k;
199 r = R_DIM (r, 0, 255);
200 g = R_DIM (g, 0, 255);
201 b = R_DIM (b, 0, 255);
202 if (r == g && g == b) { // b&w
203 k = 232 + (int)(((r+g+b)/3)/10.3);
204 } else {
205 r = (int)(r / 42.6);
206 g = (int)(g / 42.6);
207 b = (int)(b / 42.6);
208 k = 16 + (r * 36) + (g * 6) + b;
209 }
210 r_cons_printf ("\x1b[%d;5;%dm", fg? 48: 38, k);
211 }
212
r_cons_println(const char * str)213 R_API void r_cons_println(const char* str) {
214 r_cons_print (str);
215 r_cons_newline ();
216 }
217
r_cons_printat(const char * str,int x,char y)218 R_API void r_cons_printat(const char *str, int x, char y) {
219 int i, o, len;
220 int h, w = r_cons_get_size (&h);
221 int lines = 0;
222 for (o = i = len = 0; str[i]; i++, len++) {
223 if (str[i] == '\n') {
224 r_cons_gotoxy (x, y + lines);
225 int wlen = R_MIN (len, w);
226 r_cons_memcat (str + o, wlen);
227 o = i + 1;
228 len = 0;
229 lines++;
230 }
231 }
232 if (len > 0) {
233 r_cons_gotoxy (x, y + lines);
234 r_cons_memcat (str + o, len);
235 }
236 }
237
r_cons_strcat_justify(const char * str,int j,char c)238 R_API void r_cons_strcat_justify(const char *str, int j, char c) {
239 int i, o, len;
240 for (o = i = len = 0; str[i]; i++, len++) {
241 if (str[i] == '\n') {
242 r_cons_memset (' ', j);
243 if (c) {
244 r_cons_memset (c, 1);
245 r_cons_memset (' ', 1);
246 }
247 r_cons_memcat (str + o, len);
248 if (str[o + len] == '\n') {
249 r_cons_newline ();
250 }
251 o = i + 1;
252 len = 0;
253 }
254 }
255 if (len > 1) {
256 r_cons_memcat (str + o, len);
257 }
258 }
259
r_cons_strcat_at(const char * _str,int x,char y,int w,int h)260 R_API void r_cons_strcat_at(const char *_str, int x, char y, int w, int h) {
261 int i, o, len;
262 int cols = 0;
263 int rows = 0;
264 if (x < 0 || y < 0) {
265 int H, W = r_cons_get_size (&H);
266 if (x < 0) {
267 x += W;
268 }
269 if (y < 0) {
270 y += H;
271 }
272 }
273 char *str = r_str_ansi_crop (_str, 0, 0, w + 1, h);
274 r_cons_strcat (R_CONS_CURSOR_SAVE);
275 for (o = i = len = 0; str[i]; i++, len++) {
276 if (w < 0 || rows > w) {
277 break;
278 }
279 if (str[i] == '\n') {
280 r_cons_gotoxy (x, y + rows);
281 int ansilen = r_str_ansi_len (str + o);
282 cols = R_MIN (w, ansilen);
283 const char *end = r_str_ansi_chrn (str + o, cols);
284 cols = end - str + o;
285 r_cons_memcat (str + o, R_MIN (len, cols));
286 o = i + 1;
287 len = 0;
288 rows++;
289 }
290 }
291 if (len > 1) {
292 r_cons_gotoxy (x, y + rows);
293 r_cons_memcat (str + o, len);
294 }
295 r_cons_strcat (Color_RESET);
296 r_cons_strcat (R_CONS_CURSOR_RESTORE);
297 free (str);
298 }
299
r_cons_singleton(void)300 R_API RCons *r_cons_singleton(void) {
301 return &I;
302 }
303
r_cons_break_clear(void)304 R_API void r_cons_break_clear(void) {
305 I.context->breaked = false;
306 }
307
r_cons_context_break_push(RConsContext * context,RConsBreak cb,void * user,bool sig)308 R_API void r_cons_context_break_push(RConsContext *context, RConsBreak cb, void *user, bool sig) {
309 if (!context->break_stack) {
310 return;
311 }
312
313 //if we don't have any element in the stack start the signal
314 RConsBreakStack *b = R_NEW0 (RConsBreakStack);
315 if (!b) {
316 return;
317 }
318 if (r_stack_is_empty (context->break_stack)) {
319 #if __UNIX__
320 if (sig && r_cons_context_is_main ()) {
321 r_sys_signal (SIGINT, __break_signal);
322 }
323 #endif
324 context->breaked = false;
325 }
326 //save the actual state
327 b->event_interrupt = context->event_interrupt;
328 b->event_interrupt_data = context->event_interrupt_data;
329 r_stack_push (context->break_stack, b);
330 //configure break
331 context->event_interrupt = cb;
332 context->event_interrupt_data = user;
333 }
334
r_cons_context_break_pop(RConsContext * context,bool sig)335 R_API void r_cons_context_break_pop(RConsContext *context, bool sig) {
336 if (!context->break_stack) {
337 return;
338 }
339 //restore old state
340 RConsBreakStack *b = NULL;
341 b = r_stack_pop (context->break_stack);
342 if (b) {
343 context->event_interrupt = b->event_interrupt;
344 context->event_interrupt_data = b->event_interrupt_data;
345 break_stack_free (b);
346 } else {
347 //there is not more elements in the stack
348 #if __UNIX__
349 if (sig && r_cons_context_is_main ()) {
350 r_sys_signal (SIGINT, SIG_IGN);
351 }
352 #endif
353 context->breaked = false;
354 }
355 }
356
r_cons_break_push(RConsBreak cb,void * user)357 R_API void r_cons_break_push(RConsBreak cb, void *user) {
358 r_cons_context_break_push (I.context, cb, user, true);
359 }
360
r_cons_break_pop(void)361 R_API void r_cons_break_pop(void) {
362 r_cons_context_break_pop (I.context, true);
363 }
364
r_cons_is_interactive(void)365 R_API bool r_cons_is_interactive(void) {
366 return I.context->is_interactive;
367 }
368
r_cons_default_context_is_interactive(void)369 R_API bool r_cons_default_context_is_interactive(void) {
370 return r_cons_context_default.is_interactive;
371 }
372
r_cons_is_breaked(void)373 R_API bool r_cons_is_breaked(void) {
374 if (I.cb_break) {
375 I.cb_break (I.user);
376 }
377 if (I.timeout) {
378 if (r_time_now_mono () > I.timeout) {
379 I.context->breaked = true;
380 eprintf ("\nTimeout!\n");
381 I.timeout = 0;
382 }
383 }
384 return I.context->breaked;
385 }
386
r_cons_line(int x,int y,int x2,int y2,int ch)387 R_API void r_cons_line(int x, int y, int x2, int y2, int ch) {
388 char chstr[2] = {ch, 0};
389 int X, Y;
390 for (X = x; X < x2; X++) {
391 for (Y = y; Y < y2; Y++) {
392 r_cons_gotoxy (X, Y);
393 r_cons_print (chstr);
394 }
395 }
396 }
397
r_cons_get_cur_line(void)398 R_API int r_cons_get_cur_line(void) {
399 int curline = 0;
400 #if __WINDOWS__
401 POINT point;
402 if (GetCursorPos (&point)) {
403 curline = point.y;
404 }
405 #endif
406 #if __UNIX__
407 char buf[8];
408 struct termios save,raw;
409 // flush the Arrow keys escape keys which was messing up the output
410 fflush (stdout);
411 (void) tcgetattr (0, &save);
412 cfmakeraw (&raw);
413 (void) tcsetattr (0, TCSANOW, &raw);
414 if (isatty (fileno (stdin))) {
415 if (write (1, R_CONS_GET_CURSOR_POSITION, sizeof (R_CONS_GET_CURSOR_POSITION)) != -1) {
416 if (read (0, buf, sizeof (buf)) != sizeof (buf)) {
417 if (isdigit ((unsigned char)buf[2])) {
418 curline = (buf[2] - '0');
419 } if (isdigit ((unsigned char)buf[3])) {
420 curline = curline * 10 + (buf[3] - '0');
421 }
422 }
423 }
424 }
425 (void) tcsetattr (0, TCSANOW, &save);
426 #endif
427 return curline;
428 }
429
r_cons_break_timeout(int timeout)430 R_API void r_cons_break_timeout(int timeout) {
431 I.timeout = (timeout && !I.timeout)
432 ? r_time_now_mono () + ((ut64) timeout << 20) : 0;
433 }
434
r_cons_break_end(void)435 R_API void r_cons_break_end(void) {
436 I.context->breaked = false;
437 I.timeout = 0;
438 #if __UNIX__
439 r_sys_signal (SIGINT, SIG_IGN);
440 #endif
441 if (!r_stack_is_empty (I.context->break_stack)) {
442 // free all the stack
443 r_stack_free (I.context->break_stack);
444 // create another one
445 I.context->break_stack = r_stack_newf (6, break_stack_free);
446 I.context->event_interrupt_data = NULL;
447 I.context->event_interrupt = NULL;
448 }
449 }
450
r_cons_sleep_begin(void)451 R_API void *r_cons_sleep_begin(void) {
452 if (!I.cb_sleep_begin) {
453 return NULL;
454 }
455 return I.cb_sleep_begin (I.user);
456 }
457
r_cons_sleep_end(void * user)458 R_API void r_cons_sleep_end(void *user) {
459 if (I.cb_sleep_end) {
460 I.cb_sleep_end (I.user, user);
461 }
462 }
463
464 #if __WINDOWS__
465 static HANDLE h;
__w32_control(DWORD type)466 static BOOL __w32_control(DWORD type) {
467 if (type == CTRL_C_EVENT) {
468 __break_signal (2); // SIGINT
469 eprintf ("{ctrl+c} pressed.\n");
470 return true;
471 }
472 return false;
473 }
474 #elif __UNIX__
475 volatile sig_atomic_t sigwinchFlag;
resize(int sig)476 static void resize(int sig) {
477 sigwinchFlag = 1;
478 }
479 #endif
resizeWin(void)480 void resizeWin(void) {
481 if (I.event_resize) {
482 I.event_resize (I.event_data);
483 }
484 }
485
r_cons_set_click(int x,int y)486 R_API void r_cons_set_click(int x, int y) {
487 I.click_x = x;
488 I.click_y = y;
489 I.click_set = true;
490 I.mouse_event = 1;
491 }
492
r_cons_get_click(int * x,int * y)493 R_API bool r_cons_get_click(int *x, int *y) {
494 if (x) {
495 *x = I.click_x;
496 }
497 if (y) {
498 *y = I.click_y;
499 }
500 bool set = I.click_set;
501 I.click_set = false;
502 return set;
503 }
504
r_cons_enable_highlight(const bool enable)505 R_API void r_cons_enable_highlight(const bool enable) {
506 I.enable_highlight = enable;
507 }
508
r_cons_enable_mouse(const bool enable)509 R_API bool r_cons_enable_mouse(const bool enable) {
510 if ((I.mouse && enable) || (!I.mouse && !enable)) {
511 return I.mouse;
512 }
513 #if __WINDOWS__
514 if (I.vtmode == 2) {
515 #endif
516 const char *click = enable
517 ? "\x1b[?1000;1006;1015h"
518 : "\x1b[?1001r"
519 "\x1b[?1000l";
520 // : "\x1b[?1000;1006;1015l";
521 // const char *old = enable ? "\x1b[?1001s" "\x1b[?1000h" : "\x1b[?1001r" "\x1b[?1000l";
522 bool enabled = I.mouse;
523 const size_t click_len = strlen (click);
524 if (write (2, click, click_len) != click_len) {
525 return false;
526 }
527 I.mouse = enable;
528 return enabled;
529 #if __WINDOWS__
530 }
531 DWORD mode;
532 HANDLE h;
533 bool enabled = I.mouse;
534 h = GetStdHandle (STD_INPUT_HANDLE);
535 GetConsoleMode (h, &mode);
536 mode |= ENABLE_EXTENDED_FLAGS;
537 mode = enable
538 ? (mode | ENABLE_MOUSE_INPUT) & ~ENABLE_QUICK_EDIT_MODE
539 : (mode & ~ENABLE_MOUSE_INPUT) | ENABLE_QUICK_EDIT_MODE;
540 if (SetConsoleMode (h, mode)) {
541 I.mouse = enable;
542 }
543 return enabled;
544 #else
545 return false;
546 #endif
547 }
548
549 // Stub function that cb_main_output gets pointed to in util/log.c by r_cons_new
550 // This allows Cutter to set per-task logging redirection
r_cons_new(void)551 R_API RCons *r_cons_new(void) {
552 I.refcnt++;
553 if (I.refcnt != 1) {
554 return &I;
555 }
556 I.rgbstr = r_cons_rgb_str_off;
557 I.line = r_line_new ();
558 I.enable_highlight = true;
559 I.highlight = NULL;
560 I.is_wine = -1;
561 I.fps = 0;
562 I.blankline = true;
563 I.teefile = NULL;
564 I.fix_columns = 0;
565 I.fix_rows = 0;
566 I.mouse_event = 0;
567 I.force_rows = 0;
568 I.force_columns = 0;
569 I.event_resize = NULL;
570 I.event_data = NULL;
571 I.noflush = false;
572 I.linesleep = 0;
573 I.fdin = stdin;
574 I.fdout = 1;
575 I.break_lines = false;
576 I.lines = 0;
577
578 I.context = &r_cons_context_default;
579 cons_context_init (I.context, NULL);
580
581 r_cons_get_size (&I.pagesize);
582 I.num = NULL;
583 I.null = 0;
584 #if __WINDOWS__
585 I.old_cp = GetConsoleOutputCP ();
586 I.vtmode = r_cons_is_vtcompat ();
587 #else
588 I.vtmode = 2;
589 #endif
590 #if EMSCRIPTEN
591 /* do nothing here :? */
592 #elif __UNIX__
593 tcgetattr (0, &I.term_buf);
594 memcpy (&I.term_raw, &I.term_buf, sizeof (I.term_raw));
595 I.term_raw.c_iflag &= ~(BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
596 I.term_raw.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
597 I.term_raw.c_cflag &= ~(CSIZE|PARENB);
598 I.term_raw.c_cflag |= CS8;
599 I.term_raw.c_cc[VMIN] = 1; // Solaris stuff hehe
600 r_sys_signal (SIGWINCH, resize);
601 #elif __WINDOWS__
602 h = GetStdHandle (STD_INPUT_HANDLE);
603 GetConsoleMode (h, &I.term_buf);
604 I.term_raw = 0;
605 if (!SetConsoleCtrlHandler ((PHANDLER_ROUTINE)__w32_control, TRUE)) {
606 eprintf ("r_cons: Cannot set control console handler\n");
607 }
608 #endif
609 I.pager = NULL; /* no pager by default */
610 I.mouse = 0;
611 I.show_vals = false;
612 r_cons_reset ();
613 r_cons_rgb_init ();
614
615 r_print_set_is_interrupted_cb (r_cons_is_breaked);
616
617 return &I;
618 }
619
r_cons_free(void)620 R_API RCons *r_cons_free(void) {
621 #if __WINDOWS__
622 r_cons_enable_mouse (false);
623 if (I.old_cp) {
624 (void)SetConsoleOutputCP (I.old_cp);
625 // chcp doesn't pick up the code page switch for some reason
626 (void)r_sys_cmdf ("chcp %u > NUL", I.old_cp);
627 }
628 #endif
629 I.refcnt--;
630 if (I.refcnt != 0) {
631 return NULL;
632 }
633 if (I.line) {
634 r_line_free ();
635 I.line = NULL;
636 }
637 R_FREE (I.context->buffer);
638 R_FREE (I.break_word);
639 cons_context_deinit (I.context);
640 R_FREE (I.context->lastOutput);
641 I.context->lastLength = 0;
642 R_FREE (I.pager);
643 return NULL;
644 }
645
646 #define MOAR (4096 * 8)
palloc(int moar)647 static bool palloc(int moar) {
648 void *temp;
649 if (moar <= 0) {
650 return false;
651 }
652 if (!I.context->buffer) {
653 int new_sz;
654 if ((INT_MAX - MOAR) < moar) {
655 return false;
656 }
657 new_sz = moar + MOAR;
658 temp = calloc (1, new_sz);
659 if (temp) {
660 I.context->buffer_sz = new_sz;
661 I.context->buffer = temp;
662 I.context->buffer[0] = '\0';
663 }
664 } else if (moar + I.context->buffer_len > I.context->buffer_sz) {
665 char *new_buffer;
666 int old_buffer_sz = I.context->buffer_sz;
667 if ((INT_MAX - MOAR - moar) < I.context->buffer_sz) {
668 return false;
669 }
670 I.context->buffer_sz += moar + MOAR;
671 new_buffer = realloc (I.context->buffer, I.context->buffer_sz);
672 if (new_buffer) {
673 I.context->buffer = new_buffer;
674 } else {
675 I.context->buffer_sz = old_buffer_sz;
676 return false;
677 }
678 }
679 return true;
680 }
681
r_cons_eof(void)682 R_API int r_cons_eof(void) {
683 return feof (I.fdin);
684 }
685
r_cons_gotoxy(int x,int y)686 R_API void r_cons_gotoxy(int x, int y) {
687 #if __WINDOWS__
688 r_cons_w32_gotoxy (1, x, y);
689 #else
690 r_cons_printf ("\x1b[%d;%dH", y, x);
691 #endif
692 }
693
r_cons_print_clear(void)694 R_API void r_cons_print_clear(void) {
695 r_cons_strcat ("\x1b[0;0H\x1b[0m");
696 }
697
r_cons_fill_line(void)698 R_API void r_cons_fill_line(void) {
699 char *p, white[1024];
700 int cols = I.columns - 1;
701 if (cols < 1) {
702 return;
703 }
704 p = (cols >= sizeof (white))
705 ? malloc (cols + 1): white;
706 if (p) {
707 memset (p, ' ', cols);
708 p[cols] = 0;
709 r_cons_strcat (p);
710 if (white != p) {
711 free (p);
712 }
713 }
714 }
715
r_cons_clear_line(int std_err)716 R_API void r_cons_clear_line(int std_err) {
717 #if __WINDOWS__
718 if (I.vtmode) {
719 fprintf (std_err? stderr: stdout,"%s", R_CONS_CLEAR_LINE);
720 } else {
721 char white[1024];
722 memset (&white, ' ', sizeof (white));
723 if (I.columns > 0 && I.columns < sizeof (white)) {
724 white[I.columns - 1] = 0;
725 } else if (I.columns == 0) {
726 white[0] = 0;
727 } else {
728 white[sizeof (white) - 1] = 0; // HACK
729 }
730 fprintf (std_err? stderr: stdout, "\r%s\r", white);
731 }
732 #else
733 fprintf (std_err? stderr: stdout,"%s", R_CONS_CLEAR_LINE);
734 #endif
735 fflush (std_err? stderr: stdout);
736 }
737
r_cons_clear00(void)738 R_API void r_cons_clear00(void) {
739 r_cons_clear ();
740 r_cons_gotoxy (0, 0);
741 }
742
r_cons_reset_colors(void)743 R_API void r_cons_reset_colors(void) {
744 r_cons_strcat (Color_RESET_BG Color_RESET);
745 }
746
r_cons_clear(void)747 R_API void r_cons_clear(void) {
748 I.lines = 0;
749 #if __WINDOWS__
750 r_cons_w32_clear ();
751 #else
752 r_cons_strcat (Color_RESET R_CONS_CLEAR_SCREEN);
753 #endif
754 }
755
cons_grep_reset(RConsGrep * grep)756 static void cons_grep_reset(RConsGrep *grep) {
757 R_FREE (grep->str);
758 ZERO_FILL (*grep);
759 grep->line = -1;
760 grep->sort = -1;
761 grep->sort_invert = false;
762 }
763
r_cons_reset(void)764 R_API void r_cons_reset(void) {
765 if (I.context->buffer) {
766 I.context->buffer[0] = '\0';
767 }
768 I.context->buffer_len = 0;
769 I.lines = 0;
770 I.lastline = I.context->buffer;
771 cons_grep_reset (&I.context->grep);
772 CTX (pageable) = true;
773 }
774
r_cons_get_buffer(void)775 R_API const char *r_cons_get_buffer(void) {
776 //check len otherwise it will return trash
777 return I.context->buffer_len? I.context->buffer : NULL;
778 }
779
r_cons_get_buffer_len(void)780 R_API int r_cons_get_buffer_len(void) {
781 return I.context->buffer_len;
782 }
783
r_cons_filter(void)784 R_API void r_cons_filter(void) {
785 /* grep */
786 if (I.filter || I.context->grep.nstrings > 0 || I.context->grep.tokens_used || I.context->grep.less || I.context->grep.json) {
787 (void)r_cons_grepbuf ();
788 I.filter = false;
789 }
790 /* html */
791 if (I.is_html) {
792 int newlen = 0;
793 char *input = r_str_ndup (I.context->buffer, I.context->buffer_len);
794 char *res = r_cons_html_filter (input, &newlen);
795 free (I.context->buffer);
796 I.context->buffer = res;
797 I.context->buffer_len = newlen;
798 I.context->buffer_sz = newlen;
799 free (input);
800 }
801 if (I.was_html) {
802 I.is_html = true;
803 I.was_html = false;
804 }
805 }
806
r_cons_push(void)807 R_API void r_cons_push(void) {
808 if (!I.context->cons_stack) {
809 return;
810 }
811 RConsStack *data = cons_stack_dump (true);
812 if (!data) {
813 return;
814 }
815 r_stack_push (I.context->cons_stack, data);
816 I.context->buffer_len = 0;
817 if (I.context->buffer) {
818 memset (I.context->buffer, 0, I.context->buffer_sz);
819 }
820 }
821
r_cons_pop(void)822 R_API void r_cons_pop(void) {
823 if (!I.context->cons_stack) {
824 return;
825 }
826 RConsStack *data = (RConsStack *)r_stack_pop (I.context->cons_stack);
827 if (!data) {
828 return;
829 }
830 cons_stack_load (data, true);
831 cons_stack_free ((void *)data);
832 }
833
r_cons_context_new(R_NULLABLE RConsContext * parent)834 R_API RConsContext *r_cons_context_new(R_NULLABLE RConsContext *parent) {
835 RConsContext *context = R_NEW0 (RConsContext);
836 if (!context) {
837 return NULL;
838 }
839 cons_context_init (context, parent);
840 return context;
841 }
842
r_cons_context_free(RConsContext * context)843 R_API void r_cons_context_free(RConsContext *context) {
844 if (!context) {
845 return;
846 }
847 cons_context_deinit (context);
848 free (context);
849 }
850
r_cons_context_load(RConsContext * context)851 R_API void r_cons_context_load(RConsContext *context) {
852 I.context = context;
853 }
854
r_cons_context_reset(void)855 R_API void r_cons_context_reset(void) {
856 I.context = &r_cons_context_default;
857 }
858
r_cons_context_is_main(void)859 R_API bool r_cons_context_is_main(void) {
860 return I.context == &r_cons_context_default;
861 }
862
r_cons_context_break(RConsContext * context)863 R_API void r_cons_context_break(RConsContext *context) {
864 if (!context) {
865 return;
866 }
867 context->breaked = true;
868 if (context->event_interrupt) {
869 context->event_interrupt (context->event_interrupt_data);
870 }
871 }
872
r_cons_last(void)873 R_API void r_cons_last(void) {
874 if (!CTX (lastEnabled)) {
875 return;
876 }
877 CTX (lastMode) = true;
878 r_cons_memcat (CTX (lastOutput), CTX (lastLength));
879 }
880
lastMatters(void)881 static bool lastMatters(void) {
882 return (I.context->buffer_len > 0) \
883 && (CTX (lastEnabled) && !I.filter && I.context->grep.nstrings < 1 && \
884 !I.context->grep.tokens_used && !I.context->grep.less && \
885 !I.context->grep.json && !I.is_html);
886 }
887
r_cons_echo(const char * msg)888 R_API void r_cons_echo(const char *msg) {
889 static RStrBuf *echodata = NULL; // TODO: move into RConsInstance? maybe nope
890 if (msg) {
891 if (echodata) {
892 r_strbuf_append (echodata, msg);
893 r_strbuf_append (echodata, "\n");
894 } else {
895 echodata = r_strbuf_new (msg);
896 }
897 } else {
898 if (echodata) {
899 char *data = r_strbuf_drain (echodata);
900 r_cons_strcat (data);
901 r_cons_newline ();
902 echodata = NULL;
903 free (data);
904 }
905 }
906 }
907
r_cons_eflush(void)908 R_API void r_cons_eflush(void) {
909 char *s = r_cons_errstr ();
910 if (s) {
911 eprintf ("%s", s);
912 free (s);
913 }
914 }
915
r_cons_flush(void)916 R_API void r_cons_flush(void) {
917 const char *tee = I.teefile;
918 if (I.noflush) {
919 return;
920 }
921 if (I.context->errmode == R_CONS_ERRMODE_FLUSH) {
922 r_cons_eflush ();
923 }
924 if (I.null) {
925 r_cons_reset ();
926 return;
927 }
928 if (lastMatters () && !CTX (lastMode)) {
929 // snapshot of the output
930 if (CTX (buffer_len) > CTX (lastLength)) {
931 free (CTX (lastOutput));
932 CTX (lastOutput) = malloc (CTX (buffer_len) + 1);
933 }
934 CTX (lastLength) = CTX (buffer_len);
935 memcpy (CTX (lastOutput), CTX (buffer), CTX (buffer_len));
936 } else {
937 CTX (lastMode) = false;
938 }
939 r_cons_filter ();
940 if (r_cons_is_interactive () && I.fdout == 1) {
941 /* Use a pager if the output doesn't fit on the terminal window. */
942 if (CTX (pageable) && CTX (buffer) && I.pager && *I.pager && CTX (buffer_len) > 0 && r_str_char_count (CTX (buffer), '\n') >= I.rows) {
943 I.context->buffer[I.context->buffer_len - 1] = 0;
944 if (!strcmp (I.pager, "..")) {
945 char *str = r_str_ndup (CTX (buffer), CTX (buffer_len));
946 CTX (pageable) = false;
947 r_cons_less_str (str, NULL);
948 r_cons_reset ();
949 free (str);
950 return;
951 } else {
952 r_sys_cmd_str_full (I.pager, CTX (buffer), NULL, NULL, NULL);
953 r_cons_reset ();
954 }
955 } else if (I.context->buffer_len > CONS_MAX_USER) {
956 #if COUNT_LINES
957 int i, lines = 0;
958 for (i = 0; I.context->buffer[i]; i++) {
959 if (I.context->buffer[i] == '\n') {
960 lines ++;
961 }
962 }
963 if (lines > 0 && !r_cons_yesno ('n',"Do you want to print %d lines? (y/N)", lines)) {
964 r_cons_reset ();
965 return;
966 }
967 #else
968 char buf[8];
969 r_num_units (buf, sizeof (buf), I.context->buffer_len);
970 if (!r_cons_yesno ('n', "Do you want to print %s chars? (y/N)", buf)) {
971 r_cons_reset ();
972 return;
973 }
974 #endif
975 // fix | more | less problem
976 r_cons_set_raw (true);
977 }
978 }
979 if (tee && *tee) {
980 FILE *d = r_sandbox_fopen (tee, "a+");
981 if (d) {
982 if (I.context->buffer_len != fwrite (I.context->buffer, 1, I.context->buffer_len, d)) {
983 eprintf ("r_cons_flush: fwrite: error (%s)\n", tee);
984 }
985 fclose (d);
986 } else {
987 eprintf ("Cannot write on '%s'\n", tee);
988 }
989 }
990 r_cons_highlight (I.highlight);
991
992 // is_html must be a filter, not a write endpoint
993 if (r_cons_is_interactive () && !r_sandbox_enable (false)) {
994 if (I.linesleep > 0 && I.linesleep < 1000) {
995 int i = 0;
996 int pagesize = R_MAX (1, I.pagesize);
997 char *ptr = I.context->buffer;
998 char *nl = strchr (ptr, '\n');
999 int len = I.context->buffer_len;
1000 I.context->buffer[I.context->buffer_len] = 0;
1001 r_cons_break_push (NULL, NULL);
1002 while (nl && !r_cons_is_breaked ()) {
1003 __cons_write (ptr, nl - ptr + 1);
1004 if (I.linesleep && !(i % pagesize)) {
1005 r_sys_usleep (I.linesleep * 1000);
1006 }
1007 ptr = nl + 1;
1008 nl = strchr (ptr, '\n');
1009 i++;
1010 }
1011 __cons_write (ptr, I.context->buffer + len - ptr);
1012 r_cons_break_pop ();
1013 } else {
1014 __cons_write (I.context->buffer, I.context->buffer_len);
1015 }
1016 } else {
1017 __cons_write (I.context->buffer, I.context->buffer_len);
1018 }
1019
1020 r_cons_reset ();
1021 if (I.newline) {
1022 eprintf ("\n");
1023 I.newline = false;
1024 }
1025 }
1026
r_cons_visual_flush(void)1027 R_API void r_cons_visual_flush(void) {
1028 if (I.noflush) {
1029 return;
1030 }
1031 r_cons_highlight (I.highlight);
1032 if (!I.null) {
1033 /* TODO: this ifdef must go in the function body */
1034 #if __WINDOWS__
1035 if (I.vtmode) {
1036 r_cons_visual_write (I.context->buffer);
1037 } else {
1038 r_cons_w32_print (I.context->buffer, I.context->buffer_len, true);
1039 }
1040 #else
1041 r_cons_visual_write (I.context->buffer);
1042 #endif
1043 }
1044 r_cons_reset ();
1045 if (I.fps) {
1046 r_cons_print_fps (0);
1047 }
1048 }
1049
r_cons_print_fps(int col)1050 R_API void r_cons_print_fps(int col) {
1051 int fps = 0, w = r_cons_get_size (NULL);
1052 static ut64 prev = 0LL; //r_time_now_mono ();
1053 fps = 0;
1054 if (prev) {
1055 ut64 now = r_time_now_mono ();
1056 st64 diff = (st64)(now - prev);
1057 if (diff <= 0) {
1058 fps = 0;
1059 } else {
1060 fps = (diff < 1000000)? (1000000.0 / diff): 0;
1061 }
1062 prev = now;
1063 } else {
1064 prev = r_time_now_mono ();
1065 }
1066 if (col < 1) {
1067 col = 12;
1068 }
1069 #ifdef __WINDOWS__
1070 if (I.vtmode) {
1071 eprintf ("\x1b[0;%dH[%d FPS] \n", w - col, fps);
1072 } else {
1073 r_cons_w32_gotoxy (2, w - col, 0);
1074 eprintf (" [%d FPS] \n", fps);
1075 }
1076 #else
1077 eprintf ("\x1b[0;%dH[%d FPS] \n", w - col, fps);
1078 #endif
1079 }
1080
real_strlen(const char * ptr,int len)1081 static int real_strlen(const char *ptr, int len) {
1082 int utf8len = r_str_len_utf8 (ptr);
1083 int ansilen = r_str_ansi_len (ptr);
1084 int diff = len - utf8len;
1085 if (diff > 0) {
1086 diff--;
1087 }
1088 return ansilen - diff;
1089 }
1090
r_cons_visual_write(char * buffer)1091 R_API void r_cons_visual_write(char *buffer) {
1092 char white[1024];
1093 int cols = I.columns;
1094 int alen, plen, lines = I.rows;
1095 bool break_lines = I.break_lines;
1096 const char *endptr;
1097 char *nl, *ptr = buffer, *pptr;
1098
1099 if (I.null) {
1100 return;
1101 }
1102 memset (&white, ' ', sizeof (white));
1103 while ((nl = strchr (ptr, '\n'))) {
1104 int len = ((int)(size_t)(nl - ptr)) + 1;
1105 int lines_needed = 0;
1106
1107 *nl = 0;
1108 alen = real_strlen (ptr, len);
1109 *nl = '\n';
1110 pptr = ptr > buffer ? ptr - 1 : ptr;
1111 plen = ptr > buffer ? len : len - 1;
1112
1113 if (break_lines) {
1114 lines_needed = alen / cols + (alen % cols == 0 ? 0 : 1);
1115 }
1116 if ((break_lines && lines < lines_needed && lines > 0)
1117 || (!break_lines && alen > cols)) {
1118 int olen = len;
1119 endptr = r_str_ansi_chrn (ptr, (break_lines ? cols * lines : cols) + 1);
1120 endptr++;
1121 len = endptr - ptr;
1122 plen = ptr > buffer ? len : len - 1;
1123 if (lines > 0) {
1124 __cons_write (pptr, plen);
1125 if (len != olen) {
1126 __cons_write (R_CONS_CLEAR_FROM_CURSOR_TO_END, -1);
1127 __cons_write (Color_RESET, strlen (Color_RESET));
1128 }
1129 }
1130 } else {
1131 if (lines > 0) {
1132 int w = cols - (alen % cols == 0 ? cols : alen % cols);
1133 __cons_write (pptr, plen);
1134 if (I.blankline && w > 0) {
1135 if (w > sizeof (white) - 1) {
1136 w = sizeof (white) - 1;
1137 }
1138 __cons_write (white, w);
1139 }
1140 }
1141 // TRICK to empty columns.. maybe buggy in w32
1142 if (r_mem_mem ((const ut8*)ptr, len, (const ut8*)"\x1b[0;0H", 6)) {
1143 lines = I.rows;
1144 __cons_write (pptr, plen);
1145 }
1146 }
1147 if (break_lines) {
1148 lines -= lines_needed;
1149 } else {
1150 lines--; // do not use last line
1151 }
1152 ptr = nl + 1;
1153 }
1154 /* fill the rest of screen */
1155 if (lines > 0) {
1156 if (cols > sizeof (white)) {
1157 cols = sizeof (white);
1158 }
1159 while (--lines >= 0) {
1160 __cons_write (white, cols);
1161 }
1162 }
1163 }
1164
r_cons_printf_list(const char * format,va_list ap)1165 R_API void r_cons_printf_list(const char *format, va_list ap) {
1166 size_t size, written;
1167 va_list ap2, ap3;
1168
1169 va_copy (ap2, ap);
1170 va_copy (ap3, ap);
1171 if (I.null || !format) {
1172 va_end (ap2);
1173 va_end (ap3);
1174 return;
1175 }
1176 if (strchr (format, '%')) {
1177 if (palloc (MOAR + strlen (format) * 20)) {
1178 club:
1179 size = I.context->buffer_sz - I.context->buffer_len - 1; /* remaining space in I.context->buffer */
1180 written = vsnprintf (I.context->buffer + I.context->buffer_len, size, format, ap3);
1181 if (written >= size) { /* not all bytes were written */
1182 if (palloc (written)) {
1183 va_end (ap3);
1184 va_copy (ap3, ap2);
1185 goto club;
1186 }
1187 }
1188 I.context->buffer_len += written;
1189 I.context->buffer[I.context->buffer_len] = 0;
1190 }
1191 } else {
1192 r_cons_strcat (format);
1193 }
1194 va_end (ap2);
1195 va_end (ap3);
1196 }
1197
r_cons_printf(const char * format,...)1198 R_API int r_cons_printf(const char *format, ...) {
1199 va_list ap;
1200 if (!format || !*format) {
1201 return -1;
1202 }
1203 va_start (ap, format);
1204 r_cons_printf_list (format, ap);
1205 va_end (ap);
1206
1207 return 0;
1208 }
1209
r_cons_errmode(int mode)1210 R_API void r_cons_errmode(int mode) {
1211 I.context->errmode = mode;
1212 }
1213
r_cons_errmodes(const char * mode)1214 R_API void r_cons_errmodes(const char *mode) {
1215 int m = -1;
1216 if (!strcmp (mode, "echo")) {
1217 m = R_CONS_ERRMODE_ECHO;
1218 } else if (!strcmp (mode, "null")) {
1219 m = R_CONS_ERRMODE_NULL;
1220 } else if (!strcmp (mode, "buffer")) {
1221 m = R_CONS_ERRMODE_BUFFER;
1222 } else if (!strcmp (mode, "quiet")) {
1223 m = R_CONS_ERRMODE_QUIET;
1224 } else if (!strcmp (mode, "flush")) {
1225 m = R_CONS_ERRMODE_FLUSH;
1226 }
1227 I.context->errmode = m;
1228 }
1229
r_cons_errstr(void)1230 R_API char *r_cons_errstr(void) {
1231 char *s = r_strbuf_drain (I.context->error);
1232 I.context->error = NULL;
1233 return s;
1234 }
1235
r_cons_eprintf(const char * format,...)1236 R_API int r_cons_eprintf(const char *format, ...) {
1237 va_list ap;
1238 r_return_val_if_fail (!R_STR_ISEMPTY (format), -1);
1239 va_start (ap, format);
1240 switch (I.context->errmode) {
1241 case R_CONS_ERRMODE_NULL:
1242 break;
1243 case R_CONS_ERRMODE_ECHO:
1244 vfprintf (stderr, format, ap);
1245 break;
1246 case R_CONS_ERRMODE_QUIET:
1247 case R_CONS_ERRMODE_BUFFER:
1248 case R_CONS_ERRMODE_FLUSH:
1249 if (!I.context->error) {
1250 I.context->error = r_strbuf_new ("");
1251 }
1252 r_strbuf_vappendf (I.context->error, format, ap);
1253 break;
1254 }
1255 va_end (ap);
1256
1257 return r_strbuf_length (I.context->error);
1258 }
1259
r_cons_get_column(void)1260 R_API int r_cons_get_column(void) {
1261 char *line = strrchr (I.context->buffer, '\n');
1262 if (!line) {
1263 line = I.context->buffer;
1264 }
1265 I.context->buffer[I.context->buffer_len] = 0;
1266 return r_str_ansi_len (line);
1267 }
1268
1269 /* final entrypoint for adding stuff in the buffer screen */
r_cons_memcat(const char * str,int len)1270 R_API int r_cons_memcat(const char *str, int len) {
1271 if (R_STR_ISEMPTY (str) || len < 0) {
1272 return -1;
1273 }
1274 if (I.echo) {
1275 // Here to silent pedantic meson flags ...
1276 int rlen;
1277 if ((rlen = write (2, str, len)) != len) {
1278 return rlen;
1279 }
1280 }
1281 if (str && len > 0 && !I.null) {
1282 if (palloc (len + 1)) {
1283 memcpy (I.context->buffer + I.context->buffer_len, str, len);
1284 I.context->buffer_len += len;
1285 I.context->buffer[I.context->buffer_len] = 0;
1286 }
1287 }
1288 if (I.flush) {
1289 r_cons_flush ();
1290 }
1291 if (I.break_word && str && len > 0) {
1292 if (r_mem_mem ((const ut8*)str, len, (const ut8*)I.break_word, I.break_word_len)) {
1293 I.context->breaked = true;
1294 }
1295 }
1296 return len;
1297 }
1298
r_cons_memset(char ch,int len)1299 R_API void r_cons_memset(char ch, int len) {
1300 if (!I.null && len > 0) {
1301 if (palloc (len + 1)) {
1302 memset (I.context->buffer + I.context->buffer_len, ch, len);
1303 I.context->buffer_len += len;
1304 I.context->buffer[I.context->buffer_len] = 0;
1305 }
1306 }
1307 }
1308
r_cons_strcat(const char * str)1309 R_API void r_cons_strcat(const char *str) {
1310 int len;
1311 if (!str || I.null) {
1312 return;
1313 }
1314 len = strlen (str);
1315 if (len > 0) {
1316 r_cons_memcat (str, len);
1317 }
1318 }
1319
r_cons_newline(void)1320 R_API void r_cons_newline(void) {
1321 if (!I.null) {
1322 r_cons_strcat ("\n");
1323 }
1324 #if 0
1325 This place is wrong to manage the color reset, can interfire with r2pipe output sending resetchars
1326 and break json output appending extra chars.
1327 this code now is managed into output.c:118 at function r_cons_w32_print
1328 now the console color is reset with each \n (same stuff do it here but in correct place ... i think)
1329
1330 #if __WINDOWS__
1331 r_cons_reset_colors();
1332 #else
1333 r_cons_strcat (Color_RESET_ALL"\n");
1334 #endif
1335 if (I.is_html) r_cons_strcat ("<br />\n");
1336 #endif
1337 }
1338
1339 /* return the aproximated x,y of cursor before flushing */
1340 // XXX this function is a huge bottleneck
r_cons_get_cursor(int * rows)1341 R_API int r_cons_get_cursor(int *rows) {
1342 int i, col = 0;
1343 int row = 0;
1344 // TODO: we need to handle GOTOXY and CLRSCR ansi escape code too
1345 for (i = 0; i < I.context->buffer_len; i++) {
1346 // ignore ansi chars, copypasta from r_str_ansi_len
1347 if (I.context->buffer[i] == 0x1b) {
1348 char ch2 = I.context->buffer[i + 1];
1349 char *str = I.context->buffer;
1350 if (ch2 == '\\') {
1351 i++;
1352 } else if (ch2 == ']') {
1353 if (!strncmp (str + 2 + 5, "rgb:", 4)) {
1354 i += 18;
1355 }
1356 } else if (ch2 == '[') {
1357 for (++i; str[i] && str[i] != 'J' && str[i] != 'm' && str[i] != 'H'; i++) {
1358 ;
1359 }
1360 }
1361 } else if (I.context->buffer[i] == '\n') {
1362 row++;
1363 col = 0;
1364 } else {
1365 col++;
1366 }
1367 }
1368 if (rows) {
1369 *rows = row;
1370 }
1371 return col;
1372 }
1373
r_cons_isatty(void)1374 R_API bool r_cons_isatty(void) {
1375 #if __UNIX__
1376 struct winsize win = { 0 };
1377 const char *tty;
1378 struct stat sb;
1379
1380 if (!isatty (1)) {
1381 return false;
1382 }
1383 if (ioctl (1, TIOCGWINSZ, &win)) {
1384 return false;
1385 }
1386 if (!win.ws_col || !win.ws_row) {
1387 return false;
1388 }
1389 tty = ttyname (1);
1390 if (!tty) {
1391 return false;
1392 }
1393 if (stat (tty, &sb) || !S_ISCHR (sb.st_mode)) {
1394 return false;
1395 }
1396 return true;
1397 #endif
1398 /* non-UNIX do not have ttys */
1399 return false;
1400 }
1401
1402 #if __WINDOWS__
__xterm_get_cur_pos(int * xpos)1403 static int __xterm_get_cur_pos(int *xpos) {
1404 int ypos = 0;
1405 const char *get_pos = R_CONS_GET_CURSOR_POSITION;
1406 if (write (I.fdout, get_pos, sizeof (get_pos)) < 1) {
1407 return 0;
1408 }
1409 int ch;
1410 char pos[16];
1411 size_t i;
1412 bool is_reply;
1413 do {
1414 is_reply = true;
1415 ch = r_cons_readchar ();
1416 if (ch != 0x1b) {
1417 while ((ch = r_cons_readchar_timeout (25))) {
1418 if (ch < 1) {
1419 return 0;
1420 }
1421 if (ch == 0x1b) {
1422 break;
1423 }
1424 }
1425 }
1426 (void)r_cons_readchar ();
1427 for (i = 0; i < R_ARRAY_SIZE (pos) - 1; i++) {
1428 ch = r_cons_readchar ();
1429 if ((!i && !IS_DIGIT (ch)) || // dumps arrow keys etc.
1430 (i == 1 && ch == '~')) { // dumps PgUp, PgDn etc.
1431 is_reply = false;
1432 break;
1433 }
1434 if (ch == ';') {
1435 pos[i] = 0;
1436 break;
1437 }
1438 pos[i] = ch;
1439 }
1440 } while (!is_reply);
1441 pos[R_ARRAY_SIZE (pos) - 1] = 0;
1442 ypos = atoi (pos);
1443 for (i = 0; i < R_ARRAY_SIZE (pos) - 1; i++) {
1444 if ((ch = r_cons_readchar ()) == 'R') {
1445 pos[i] = 0;
1446 break;
1447 }
1448 pos[i] = ch;
1449 }
1450 pos[R_ARRAY_SIZE (pos) - 1] = 0;
1451 *xpos = atoi (pos);
1452
1453 return ypos;
1454 }
1455
__xterm_get_size(void)1456 static bool __xterm_get_size(void) {
1457 if (write (I.fdout, R_CONS_CURSOR_SAVE, sizeof (R_CONS_CURSOR_SAVE)) < 1) {
1458 return false;
1459 }
1460 int rows, columns;
1461 (void)write (I.fdout, "\x1b[999;999H", sizeof ("\x1b[999;999H"));
1462 rows = __xterm_get_cur_pos (&columns);
1463 if (rows) {
1464 I.rows = rows;
1465 I.columns = columns;
1466 } // otherwise reuse previous values
1467 (void)write (I.fdout, R_CONS_CURSOR_RESTORE, sizeof (R_CONS_CURSOR_RESTORE));
1468 return true;
1469 }
1470
1471 #endif
1472
1473 // XXX: if this function returns <0 in rows or cols expect MAYHEM
r_cons_get_size(int * rows)1474 R_API int r_cons_get_size(int *rows) {
1475 #if __WINDOWS__
1476 CONSOLE_SCREEN_BUFFER_INFO csbi;
1477 bool ret = GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &csbi);
1478 if (ret) {
1479 I.columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
1480 I.rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
1481 } else {
1482 if (I.term_xterm) {
1483 ret = __xterm_get_size ();
1484 }
1485 if (!ret || (I.columns == -1 && I.rows == 0)) {
1486 // Stdout is probably redirected so we set default values
1487 I.columns = 80;
1488 I.rows = 23;
1489 }
1490 }
1491 #elif EMSCRIPTEN
1492 I.columns = 80;
1493 I.rows = 23;
1494 #elif __UNIX__
1495 struct winsize win = { 0 };
1496 if (isatty (0) && !ioctl (0, TIOCGWINSZ, &win)) {
1497 if ((!win.ws_col) || (!win.ws_row)) {
1498 const char *tty = isatty (1)? ttyname (1): NULL;
1499 int fd = open (r_str_get_fail (tty, "/dev/tty"), O_RDONLY);
1500 if (fd != -1) {
1501 int ret = ioctl (fd, TIOCGWINSZ, &win);
1502 if (ret || !win.ws_col || !win.ws_row) {
1503 win.ws_col = 80;
1504 win.ws_row = 23;
1505 }
1506 close (fd);
1507 }
1508 }
1509 I.columns = win.ws_col;
1510 I.rows = win.ws_row;
1511 } else {
1512 I.columns = 80;
1513 I.rows = 23;
1514 }
1515 #else
1516 char *str = r_sys_getenv ("COLUMNS");
1517 if (str) {
1518 I.columns = atoi (str);
1519 I.rows = 23; // XXX. windows must get console size
1520 free (str);
1521 } else {
1522 I.columns = 80;
1523 I.rows = 23;
1524 }
1525 #endif
1526 #if SIMULATE_ADB_SHELL
1527 I.rows = 0;
1528 I.columns = 0;
1529 #endif
1530 #if SIMULATE_MAYHEM
1531 // expect tons of crashes
1532 I.rows = -1;
1533 I.columns = -1;
1534 #endif
1535 if (I.rows < 0) {
1536 I.rows = 0;
1537 }
1538 if (I.columns < 0) {
1539 I.columns = 0;
1540 }
1541 if (I.force_columns) {
1542 I.columns = I.force_columns;
1543 }
1544 if (I.force_rows) {
1545 I.rows = I.force_rows;
1546 }
1547 if (I.fix_columns) {
1548 I.columns += I.fix_columns;
1549 }
1550 if (I.fix_rows) {
1551 I.rows += I.fix_rows;
1552 }
1553 if (rows) {
1554 *rows = I.rows;
1555 }
1556 I.rows = R_MAX (0, I.rows);
1557 return R_MAX (0, I.columns);
1558 }
1559
1560 #if __WINDOWS__
r_cons_is_vtcompat(void)1561 R_API int r_cons_is_vtcompat(void) {
1562 DWORD major;
1563 DWORD minor;
1564 DWORD release = 0;
1565 char *wt_session = r_sys_getenv ("WT_SESSION");
1566 if (wt_session) {
1567 free (wt_session);
1568 return 2;
1569 }
1570 char *alacritty = r_sys_getenv ("ALACRITTY_LOG");
1571 if (alacritty) {
1572 free (alacritty);
1573 return 1;
1574 }
1575 char *term = r_sys_getenv ("TERM");
1576 if (term) {
1577 if (strstr (term, "xterm")) {
1578 I.term_xterm = 1;
1579 free (term);
1580 return 2;
1581 }
1582 I.term_xterm = 0;
1583 free (term);
1584 }
1585 char *ansicon = r_sys_getenv ("ANSICON");
1586 if (ansicon) {
1587 free (ansicon);
1588 return 1;
1589 }
1590 bool win_support = 0;
1591 RSysInfo *info = r_sys_info ();
1592 if (info && info->version) {
1593 char *dot = strtok (info->version, ".");
1594 major = atoi (dot);
1595 dot = strtok (NULL, ".");
1596 minor = atoi (dot);
1597 if (info->release) {
1598 release = atoi (info->release);
1599 }
1600 if (major > 10
1601 || (major == 10 && minor > 0)
1602 || (major == 10 && minor == 0 && release >= 1703)) {
1603 win_support = 1;
1604 }
1605 }
1606 r_sys_info_free (info);
1607 return win_support;
1608 }
1609 #endif
1610
r_cons_show_cursor(int cursor)1611 R_API void r_cons_show_cursor(int cursor) {
1612 #if __WINDOWS__
1613 if (I.vtmode) {
1614 #endif
1615 (void) write (1, cursor ? "\x1b[?25h" : "\x1b[?25l", 6);
1616 #if __WINDOWS__
1617 } else {
1618 static HANDLE hStdout = NULL;
1619 static DWORD size = -1;
1620 CONSOLE_CURSOR_INFO cursor_info;
1621 if (!hStdout) {
1622 hStdout = GetStdHandle (STD_OUTPUT_HANDLE);
1623 }
1624 if (size == -1) {
1625 GetConsoleCursorInfo (hStdout, &cursor_info);
1626 size = cursor_info.dwSize;
1627 }
1628 cursor_info.dwSize = size;
1629 cursor_info.bVisible = cursor ? TRUE : FALSE;
1630 SetConsoleCursorInfo (hStdout, &cursor_info);
1631 }
1632 #endif
1633 }
1634
1635 /**
1636 * void r_cons_set_raw( [0,1] )
1637 *
1638 * Change canonicality of the terminal
1639 *
1640 * For optimization reasons, there's no initialization flag, so you need to
1641 * ensure that the make the first call to r_cons_set_raw() with '1' and
1642 * the next calls ^=1, so: 1, 0, 1, 0, 1, ...
1643 *
1644 * If you doesn't use this order you'll probably loss your terminal properties.
1645 *
1646 */
r_cons_set_raw(bool is_raw)1647 R_API void r_cons_set_raw(bool is_raw) {
1648 static int oldraw = -1;
1649 if (oldraw != -1) {
1650 if (is_raw == oldraw) {
1651 return;
1652 }
1653 }
1654 #if EMSCRIPTEN
1655 /* do nothing here */
1656 #elif __UNIX__
1657 // enforce echo off
1658 if (is_raw) {
1659 I.term_raw.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
1660 tcsetattr (0, TCSANOW, &I.term_raw);
1661 } else {
1662 tcsetattr (0, TCSANOW, &I.term_buf);
1663 }
1664 #elif __WINDOWS__
1665 if (is_raw) {
1666 if (I.term_xterm) {
1667 r_sandbox_system ("stty raw -echo", 1);
1668 } else {
1669 SetConsoleMode (h, I.term_raw);
1670 }
1671 } else {
1672 if (I.term_xterm) {
1673 r_sandbox_system ("stty -raw echo", 1);
1674 } else {
1675 SetConsoleMode (h, I.term_buf);
1676 }
1677 }
1678 #else
1679 #warning No raw console supported for this platform
1680 #endif
1681 fflush (stdout);
1682 oldraw = is_raw;
1683 }
1684
r_cons_set_utf8(bool b)1685 R_API void r_cons_set_utf8(bool b) {
1686 I.use_utf8 = b;
1687 #if __WINDOWS__
1688 if (b) {
1689 if (IsValidCodePage (CP_UTF8)) {
1690 if (!SetConsoleOutputCP (CP_UTF8)) {
1691 r_sys_perror ("r_cons_set_utf8");
1692 }
1693 #if UNICODE
1694 UINT inCP = CP_UTF8;
1695 #else
1696 UINT inCP = GetACP ();
1697 #endif
1698 if (!SetConsoleCP (inCP)) {
1699 r_sys_perror ("r_cons_set_utf8");
1700 }
1701 } else {
1702 R_LOG_WARN ("UTF-8 Codepage not installed.\n");
1703 }
1704 } else {
1705 UINT acp = GetACP ();
1706 if (!SetConsoleCP (acp) || !SetConsoleOutputCP (acp)) {
1707 r_sys_perror ("r_cons_set_utf8");
1708 }
1709 }
1710 #endif
1711 }
1712
r_cons_invert(int set,int color)1713 R_API void r_cons_invert(int set, int color) {
1714 r_cons_strcat (R_CONS_INVERT (set, color));
1715 }
1716
1717 /*
1718 Enable/Disable scrolling in terminal:
1719 FMI: cd libr/cons/t ; make ti ; ./ti
1720 smcup: disable terminal scrolling (fullscreen mode)
1721 rmcup: enable terminal scrolling (normal mode)
1722 */
r_cons_set_cup(bool enable)1723 R_API bool r_cons_set_cup(bool enable) {
1724 #if __UNIX__
1725 const char *code = enable
1726 ? "\x1b[?1049h" "\x1b" "7\x1b[?47h"
1727 : "\x1b[?1049l" "\x1b[?47l" "\x1b" "8";
1728 const size_t code_len = strlen (code);
1729 if (write (2, code, code_len) != code_len) {
1730 return false;
1731 }
1732 fflush (stdout);
1733 #elif __WINDOWS__
1734 if (I.vtmode) {
1735 if (enable) {
1736 const char *code = enable // xterm + xterm-color
1737 ? "\x1b[?1049h\x1b" "7\x1b[?47h"
1738 : "\x1b[?1049l\x1b[?47l""\x1b""8";
1739 const size_t code_len = strlen (code);
1740 if (write (2, code, code_len) != code_len) {
1741 return false;
1742 }
1743 }
1744 fflush (stdout);
1745 }
1746 #endif
1747 return true;
1748 }
1749
r_cons_column(int c)1750 R_API void r_cons_column(int c) {
1751 char *b = malloc (I.context->buffer_len + 1);
1752 if (!b) {
1753 return;
1754 }
1755 memcpy (b, I.context->buffer, I.context->buffer_len);
1756 b[I.context->buffer_len] = 0;
1757 r_cons_reset ();
1758 // align current buffer N chars right
1759 r_cons_strcat_justify (b, c, 0);
1760 r_cons_gotoxy (0, 0);
1761 free (b);
1762 }
1763
1764 // XXX deprecate must be push/pop context state
1765 static bool lasti = false; /* last interactive mode */
1766
r_cons_set_interactive(bool x)1767 R_API void r_cons_set_interactive(bool x) {
1768 lasti = r_cons_singleton ()->context->is_interactive;
1769 r_cons_singleton ()->context->is_interactive = x;
1770 }
1771
r_cons_set_last_interactive(void)1772 R_API void r_cons_set_last_interactive(void) {
1773 r_cons_singleton ()->context->is_interactive = lasti;
1774 }
1775
r_cons_set_title(const char * str)1776 R_API void r_cons_set_title(const char *str) {
1777 #if __WINDOWS__
1778 # if defined(_UNICODE)
1779 wchar_t* wstr = r_utf8_to_utf16_l (str, strlen (str));
1780 if (wstr) {
1781 SetConsoleTitleW (wstr);
1782 R_FREE (wstr);
1783 }
1784 # else // defined(_UNICODE)
1785 SetConsoleTitle (str);
1786 # endif // defined(_UNICODE)
1787 #else
1788 r_cons_printf ("\x1b]0;%s\007", str);
1789 #endif
1790 }
1791
r_cons_zero(void)1792 R_API void r_cons_zero(void) {
1793 if (I.line) {
1794 I.line->zerosep = true;
1795 }
1796 (void)write (1, "", 1);
1797 }
1798
r_cons_highlight(const char * word)1799 R_API void r_cons_highlight(const char *word) {
1800 int l, *cpos = NULL;
1801 char *rword = NULL, *res, *clean = NULL;
1802 char *inv[2] = {
1803 R_CONS_INVERT (true, true),
1804 R_CONS_INVERT (false, true)
1805 };
1806 int linv[2] = {
1807 strlen (inv[0]),
1808 strlen (inv[1])
1809 };
1810
1811 if (!I.enable_highlight) {
1812 r_cons_enable_highlight (true);
1813 return;
1814 }
1815 if (word && *word && I.context->buffer) {
1816 int word_len = strlen (word);
1817 char *orig;
1818 clean = r_str_ndup (I.context->buffer, I.context->buffer_len);
1819 l = r_str_ansi_filter (clean, &orig, &cpos, -1);
1820 free (I.context->buffer);
1821 I.context->buffer = orig;
1822 if (I.highlight) {
1823 if (strcmp (word, I.highlight)) {
1824 free (I.highlight);
1825 I.highlight = strdup (word);
1826 }
1827 } else {
1828 I.highlight = strdup (word);
1829 }
1830 rword = malloc (word_len + linv[0] + linv[1] + 1);
1831 if (!rword) {
1832 free (cpos);
1833 free (clean);
1834 return;
1835 }
1836 strcpy (rword, inv[0]);
1837 strcpy (rword + linv[0], word);
1838 strcpy (rword + linv[0] + word_len, inv[1]);
1839 res = r_str_replace_thunked (I.context->buffer, clean, cpos,
1840 l, word, rword, 1);
1841 if (res) {
1842 I.context->buffer = res;
1843 I.context->buffer_len = I.context->buffer_sz = strlen (res);
1844 }
1845 free (rword);
1846 free (clean);
1847 free (cpos);
1848 /* don't free orig - it's assigned
1849 * to I.context->buffer and possibly realloc'd */
1850 } else {
1851 R_FREE (I.highlight);
1852 }
1853 }
1854
r_cons_lastline(int * len)1855 R_API char *r_cons_lastline(int *len) {
1856 char *b = I.context->buffer + I.context->buffer_len;
1857 while (b > I.context->buffer) {
1858 if (*b == '\n') {
1859 b++;
1860 break;
1861 }
1862 b--;
1863 }
1864 if (len) {
1865 int delta = b - I.context->buffer;
1866 *len = I.context->buffer_len - delta;
1867 }
1868 return b;
1869 }
1870
1871 // same as r_cons_lastline(), but len will be the number of
1872 // utf-8 characters excluding ansi escape sequences as opposed to just bytes
r_cons_lastline_utf8_ansi_len(int * len)1873 R_API char *r_cons_lastline_utf8_ansi_len(int *len) {
1874 if (!len) {
1875 return r_cons_lastline (0);
1876 }
1877
1878 char *b = I.context->buffer + I.context->buffer_len;
1879 int l = 0;
1880 int last_possible_ansi_end = 0;
1881 char ch = '\0';
1882 char ch2;
1883 while (b > I.context->buffer) {
1884 ch2 = ch;
1885 ch = *b;
1886
1887 if (ch == '\n') {
1888 b++;
1889 l--;
1890 break;
1891 }
1892
1893 // utf-8
1894 if ((ch & 0xc0) != 0x80) {
1895 l++;
1896 }
1897
1898 // ansi
1899 if (ch == 'J' || ch == 'm' || ch == 'H') {
1900 last_possible_ansi_end = l - 1;
1901 } else if (ch == '\x1b' && ch2 == '[') {
1902 l = last_possible_ansi_end;
1903 }
1904
1905 b--;
1906 }
1907
1908 *len = l;
1909 return b;
1910 }
1911
1912 /* swap color from foreground to background, returned value must be freed */
r_cons_swap_ground(const char * col)1913 R_API char *r_cons_swap_ground(const char *col) {
1914 if (!col) {
1915 return NULL;
1916 }
1917 if (!strncmp (col, "\x1b[48;5;", 7)) {
1918 /* rgb background */
1919 return r_str_newf ("\x1b[38;5;%s", col+7);
1920 } else if (!strncmp (col, "\x1b[38;5;", 7)) {
1921 /* rgb foreground */
1922 return r_str_newf ("\x1b[48;5;%s", col+7);
1923 } else if (!strncmp (col, "\x1b[4", 3)) {
1924 /* is background */
1925 return r_str_newf ("\x1b[3%s", col+3);
1926 } else if (!strncmp (col, "\x1b[3", 3)) {
1927 /* is foreground */
1928 return r_str_newf ("\x1b[4%s", col+3);
1929 }
1930 return strdup (col);
1931 }
1932
r_cons_drop(int n)1933 R_API bool r_cons_drop(int n) {
1934 if (n > I.context->buffer_len) {
1935 I.context->buffer_len = 0;
1936 return false;
1937 }
1938 I.context->buffer_len -= n;
1939 return true;
1940 }
1941
r_cons_chop(void)1942 R_API void r_cons_chop(void) {
1943 while (I.context->buffer_len > 0) {
1944 char ch = I.context->buffer[I.context->buffer_len - 1];
1945 if (ch != '\n' && !IS_WHITESPACE (ch)) {
1946 break;
1947 }
1948 I.context->buffer_len--;
1949 }
1950 }
1951
r_cons_bind(RConsBind * bind)1952 R_API void r_cons_bind(RConsBind *bind) {
1953 if (!bind) {
1954 return;
1955 }
1956 bind->get_size = r_cons_get_size;
1957 bind->get_cursor = r_cons_get_cursor;
1958 bind->cb_printf = r_cons_printf;
1959 bind->cb_flush = r_cons_flush;
1960 bind->cb_grep = r_cons_grep;
1961 bind->is_breaked = r_cons_is_breaked;
1962 }
1963
r_cons_get_rune(const ut8 ch)1964 R_API const char* r_cons_get_rune(const ut8 ch) {
1965 switch (ch) {
1966 case RUNECODE_LINE_HORIZ: return RUNE_LINE_HORIZ;
1967 case RUNECODE_LINE_VERT: return RUNE_LINE_VERT;
1968 case RUNECODE_LINE_CROSS: return RUNE_LINE_CROSS;
1969 case RUNECODE_CORNER_TL: return RUNE_CORNER_TL;
1970 case RUNECODE_CORNER_TR: return RUNE_CORNER_TR;
1971 case RUNECODE_CORNER_BR: return RUNE_CORNER_BR;
1972 case RUNECODE_CORNER_BL: return RUNE_CORNER_BL;
1973 case RUNECODE_CURVE_CORNER_TL: return RUNE_CURVE_CORNER_TL;
1974 case RUNECODE_CURVE_CORNER_TR: return RUNE_CURVE_CORNER_TR;
1975 case RUNECODE_CURVE_CORNER_BR: return RUNE_CURVE_CORNER_BR;
1976 case RUNECODE_CURVE_CORNER_BL: return RUNE_CURVE_CORNER_BL;
1977 }
1978 return NULL;
1979 }
1980
r_cons_breakword(R_NULLABLE const char * s)1981 R_API void r_cons_breakword(R_NULLABLE const char *s) {
1982 free (I.break_word);
1983 if (s) {
1984 I.break_word = strdup (s);
1985 I.break_word_len = strlen (s);
1986 } else {
1987 I.break_word = NULL;
1988 I.break_word_len = 0;
1989 }
1990 }
1991
1992 /* Print a coloured help message.
1993 * Help should be an array of NULL-terminated triples of the following form:
1994 *
1995 * {"command", "args", "description",
1996 * "command2", "args2", "description",
1997 * ...,
1998 * NULL};
1999 *
2000 * First line typically is a "Usage:" header.
2001 * Section headers are the triples with empty args and description.
2002 * Unlike normal body lines, headers are not indented.
2003 */
r_cons_cmd_help(const char * help[],bool use_color)2004 R_API void r_cons_cmd_help(const char *help[], bool use_color) {
2005 RCons *cons = r_cons_singleton ();
2006 const char
2007 *pal_input_color = use_color ? cons->context->pal.input : "",
2008 *pal_args_color = use_color ? cons->context->pal.args : "",
2009 *pal_help_color = use_color ? cons->context->pal.help : "",
2010 *pal_reset = use_color ? cons->context->pal.reset : "";
2011 int i, max_length = 0, padding = 0;
2012 const char *usage_str = "Usage:";
2013 const char *help_cmd = NULL, *help_args = NULL, *help_desc = NULL;
2014
2015 // calculate padding for description text in advance
2016 for (i = 0; help[i]; i += 3) {
2017 help_cmd = help[i + 0];
2018 help_args = help[i + 1];
2019
2020 int len_cmd = strlen (help_cmd);
2021 int len_args = strlen (help_args);
2022 if (i) {
2023 max_length = R_MAX (max_length, len_cmd + len_args);
2024 }
2025 }
2026
2027 for (i = 0; help[i]; i += 3) {
2028 help_cmd = help[i + 0];
2029 help_args = help[i + 1];
2030 help_desc = help[i + 2];
2031
2032 if (!strncmp (help_cmd, usage_str, strlen (usage_str))) {
2033 /* Usage header */
2034 r_cons_printf ("%s%s",pal_args_color, help_cmd);
2035 if (help_args[0]) {
2036 r_cons_printf (" %s", help_args);
2037 }
2038 if (help_desc[0]) {
2039 r_cons_printf (" %s", help_desc);
2040 }
2041 r_cons_printf ("%s\n", pal_reset);
2042 } else if (!help_args[0] && !help_desc[0]) {
2043 /* Section header, no need to indent it */
2044 r_cons_printf ("%s%s%s\n", pal_help_color, help_cmd, pal_reset);
2045 } else {
2046 /* Body of help text, indented */
2047 int str_length = strlen (help_cmd) + strlen (help_args);
2048 padding = R_MAX ((max_length - str_length), 0);
2049 r_cons_printf ("| %s%s%s%s%*s %s%s%s\n",
2050 pal_input_color, help_cmd,
2051 pal_args_color, help_args,
2052 padding, "",
2053 pal_help_color, help_desc, pal_reset);
2054 }
2055 }
2056 }
2057
r_cons_clear_buffer(void)2058 R_API void r_cons_clear_buffer(void) {
2059 if (I.vtmode) {
2060 (void)write (1, "\x1b" "c\x1b[3J", 6);
2061 }
2062 }
2063