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