1 /* radare - LGPL - Copyright 2009-2019 - pancake */
2
3 #include <r_cons.h>
4 #include <string.h>
5 #if __UNIX__
6 #include <errno.h>
7 #endif
8
9 #define I r_cons_singleton ()
10
11 // TODO: Support binary, use RBuffer and remove globals
12 static char *readbuffer = NULL;
13 static int readbuffer_length = 0;
14 static bool bufactive = true;
15
16 #if 0
17 //__UNIX__
18 #include <poll.h>
19 static int __is_fd_ready(int fd) {
20 fd_set rfds;
21 struct timeval tv;
22 if (fd==-1)
23 return 0;
24 FD_ZERO (&rfds);
25 FD_SET (fd, &rfds);
26 tv.tv_sec = 0;
27 tv.tv_usec = 1;
28 if (select (1, &rfds, NULL, NULL, &tv) == -1)
29 return 0;
30 return 1;
31 return !FD_ISSET (0, &rfds);
32 }
33 #endif
34
r_cons_controlz(int ch)35 R_API int r_cons_controlz(int ch) {
36 #if __UNIX__
37 if (ch == 0x1a) {
38 r_cons_show_cursor (true);
39 r_cons_enable_mouse (false);
40 r_sys_stop ();
41 return 0;
42 }
43 #endif
44 return ch;
45 }
46
47 // 96 - wheel up
48 // 97 - wheel down
49 // 95 - mouse up
50 // 92 - mouse down
__parseMouseEvent(void)51 static int __parseMouseEvent(void) {
52 char xpos[32];
53 char ypos[32];
54 (void) r_cons_readchar (); // skip first char
55 int ch2 = r_cons_readchar ();
56
57 // [32M - mousedown
58 // [35M - mouseup
59 if (ch2 == ';') {
60 size_t i;
61 // read until next ;
62 for (i = 0; i < sizeof (xpos) - 1; i++) {
63 char ch = r_cons_readchar ();
64 if (ch == ';' || ch == 'M') {
65 break;
66 }
67 xpos[i] = ch;
68 }
69 xpos[i] = 0;
70 for (i = 0; i < sizeof (ypos) - 1; i++) {
71 char ch = r_cons_readchar ();
72 if (ch == ';' || ch == 'M') {
73 break;
74 }
75 ypos[i] = ch;
76 }
77 ypos[i] = 0;
78 r_cons_set_click (atoi (xpos), atoi (ypos));
79 (void) r_cons_readchar ();
80 // ignored
81 int ch = r_cons_readchar ();
82 if (ch == 27) {
83 ch = r_cons_readchar (); // '['
84 }
85 if (ch == '[') {
86 do {
87 ch = r_cons_readchar (); // '3'
88 } while (ch != 'M');
89 }
90 }
91 return 0;
92 }
93
94 #if __WINDOWS__
95 static bool bCtrl;
96 static bool is_arrow;
97 #endif
98
r_cons_arrow_to_hjkl(int ch)99 R_API int r_cons_arrow_to_hjkl(int ch) {
100 #if __WINDOWS__
101 if (I->vtmode != 2) {
102 if (is_arrow) {
103 switch (ch) {
104 case VK_DOWN: // key down
105 ch = bCtrl ? 'J' : 'j';
106 break;
107 case VK_RIGHT: // key right
108 ch = bCtrl ? 'L' : 'l';
109 break;
110 case VK_UP: // key up
111 ch = bCtrl ? 'K' : 'k';
112 break;
113 case VK_LEFT: // key left
114 ch = bCtrl ? 'H' : 'h';
115 break;
116 case VK_PRIOR: // key home
117 ch = 'K';
118 break;
119 case VK_NEXT: // key end
120 ch = 'J';
121 break;
122 }
123 }
124 return I->mouse_event && (ut8)ch == UT8_MAX ? 0 : ch;
125 }
126 #endif
127 I->mouse_event = 0;
128 /* emacs */
129 switch ((ut8)ch) {
130 case 0xc3: r_cons_readchar (); ch='K'; break; // emacs repag (alt + v)
131 case 0x16: ch='J'; break; // emacs avpag (ctrl + v)
132 case 0x10: ch='k'; break; // emacs up (ctrl + p)
133 case 0x0e: ch='j'; break; // emacs down (ctrl + n)
134 case 0x06: ch='l'; break; // emacs right (ctrl + f)
135 case 0x02: ch='h'; break; // emacs left (ctrl + b)
136 }
137 if (ch != 0x1b) {
138 return ch;
139 }
140 ch = r_cons_readchar ();
141 if (!ch) {
142 return 0;
143 }
144 switch (ch) {
145 case 0x1b:
146 ch = 'q'; // XXX: must be 0x1b (R_CONS_KEY_ESC)
147 break;
148 case 0x4f: // function keys from f1 to f4
149 ch = r_cons_readchar ();
150 #if defined(__HAIKU__)
151 /* Haiku't don use the '[' char for function keys */
152 if (ch > 'O') {/* only in f1..f12 function keys */
153 ch = 0xf1 + (ch&0xf);
154 break;
155 }
156 case '[': // 0x5b function keys (2)
157 /* Haiku need ESC + [ for PageUp and PageDown */
158 if (ch < 'A' || ch == '[') {
159 ch = r_cons_readchar ();
160 }
161 #else
162 ch = 0xf1 + (ch & 0xf);
163 break;
164 case '[': // function keys (2)
165 ch = r_cons_readchar ();
166 #endif
167 switch (ch) {
168 case '<':
169 {
170 char pos[8] = {0};
171 int p = 0;
172 int x = 0;
173 int y = 0;
174 int sc = 0;
175
176 char vel[8] = {0};
177 int vn = 0;
178 do {
179 ch = r_cons_readchar ();
180 // just for debugging
181 //eprintf ( "%c", ch);
182 if (sc > 0) {
183 if (ch >= '0' && ch <= '9') {
184 pos[p++] = ch;
185 }
186 }
187 if (sc < 1) {
188 vel[vn++] = ch;
189 }
190 if (ch == ';') {
191 if (sc == 1) {
192 pos[p++] = 0;
193 x = atoi (pos);
194 }
195 sc++;
196 p = 0;
197 }
198 } while (ch != 'M' && ch != 'm');
199 int nvel = atoi (vel);
200 switch (nvel) {
201 case 2: // right click
202 if (ch == 'M') {
203 return INT8_MAX;
204 }
205 return -INT8_MAX;
206 case 64: // wheel up
207 return 'k';
208 case 65: // wheel down
209 return 'j';
210 }
211 pos[p++] = 0;
212 y = atoi (pos);
213 if (ch == 'm') { // mouse up only
214 r_cons_set_click (x, y);
215 }
216 }
217 return 0;
218 case '[':
219 ch = r_cons_readchar ();
220 switch (ch) {
221 case '2': ch = R_CONS_KEY_F11; break;
222 case 'A': ch = R_CONS_KEY_F1; break;
223 case 'B': ch = R_CONS_KEY_F2; break;
224 case 'C': ch = R_CONS_KEY_F3; break;
225 case 'D': ch = R_CONS_KEY_F4; break;
226 }
227 break;
228 case '9':
229 // handle mouse wheel
230 // __parseWheelEvent();
231 ch = r_cons_readchar ();
232 // 6 is up
233 // 7 is down
234 I->mouse_event = 1;
235 if (ch == '6') {
236 ch = 'k';
237 } else if (ch == '7') {
238 ch = 'j';
239 } else {
240 // unhandled case
241 ch = 0;
242 }
243 int ch2;
244 do {
245 ch2 = r_cons_readchar ();
246 } while (ch2 != 'M');
247 break;
248 case '3':
249 // handle mouse down /up events (35 vs 32)
250 __parseMouseEvent ();
251 return 0;
252 case '2':
253 ch = r_cons_readchar ();
254 switch (ch) {
255 case 0x7e:
256 ch = R_CONS_KEY_F12;
257 break;
258 default:
259 r_cons_readchar ();
260 switch (ch) {
261 case '0': ch = R_CONS_KEY_F9; break;
262 case '1': ch = R_CONS_KEY_F10; break;
263 case '3': ch = R_CONS_KEY_F11; break;
264 }
265 break;
266 }
267 break;
268 case '1':
269 ch = r_cons_readchar ();
270 switch (ch) {
271 case '1': ch = R_CONS_KEY_F1; break;
272 case '2': ch = R_CONS_KEY_F2; break;
273 case '3': ch = R_CONS_KEY_F3; break;
274 case '4': ch = R_CONS_KEY_F4; break;
275 case '5': ch = R_CONS_KEY_F5; break;
276 // case '6': ch = R_CONS_KEY_F5; break;
277 case '7': ch = R_CONS_KEY_F6; break;
278 case '8': ch = R_CONS_KEY_F7; break;
279 case '9': ch = R_CONS_KEY_F8; break;
280 #if 0
281 case '5':
282 r_cons_readchar ();
283 ch = 0xf5;
284 break;
285 case '6':
286 r_cons_readchar ();
287 ch = 0xf7;
288 break;
289 case '7':
290 r_cons_readchar ();
291 ch = 0xf6;
292 break;
293 case '8':
294 r_cons_readchar ();
295 ch = 0xf7;
296 break;
297 case '9':
298 r_cons_readchar ();
299 ch = 0xf8;
300 break;
301 #endif
302 // Support st/st-256color term and others
303 // for shift+arrows
304 case ';': // arrow+mod
305 ch = r_cons_readchar ();
306 switch (ch) {
307 case '2': // arrow+shift
308 ch = r_cons_readchar ();
309 switch (ch) {
310 case 'A': ch = 'K'; break;
311 case 'B': ch = 'J'; break;
312 case 'C': ch = 'L'; break;
313 case 'D': ch = 'H'; break;
314 }
315 break;
316 // add other modifiers
317 }
318 break;
319 case ':': // arrow+shift
320 ch = r_cons_readchar ();
321 ch = r_cons_readchar ();
322 switch (ch) {
323 case 'A': ch = 'K'; break;
324 case 'B': ch = 'J'; break;
325 case 'C': ch = 'L'; break;
326 case 'D': ch = 'H'; break;
327 }
328 break;
329 } // F9-F12 not yet supported!!
330 break;
331 case '5': ch = 'K'; r_cons_readchar (); break; // repag
332 case '6': ch = 'J'; r_cons_readchar (); break; // avpag
333 /* arrow keys */
334 case 'A': ch = 'k'; break; // up
335 case 'B': ch = 'j'; break; // down
336 case 'C': ch = 'l'; break; // right
337 case 'D': ch = 'h'; break; // left
338 // Support rxvt-unicode term for shift+arrows
339 case 'a': ch = 'K'; break; // shift+up
340 case 'b': ch = 'J'; break; // shift+down
341 case 'c': ch = 'L'; break; // shift+right
342 case 'd': ch = 'H'; break; // shift+left
343 // case 'm': ch = __parseMouseEvent (); break; // mouse down
344 case 'M': ch = __parseMouseEvent (); break; // mouse up
345 }
346 break;
347 }
348 return ch;
349 }
350
351 // XXX no control for max length here?!?!
r_cons_fgets(char * buf,int len,int argc,const char ** argv)352 R_API int r_cons_fgets(char *buf, int len, int argc, const char **argv) {
353 #define RETURN(x) { ret=x; goto beach; }
354 RCons *cons = r_cons_singleton ();
355 int ret = 0, color = cons->context->pal.input && *cons->context->pal.input;
356 if (cons->echo) {
357 r_cons_set_raw (false);
358 r_cons_show_cursor (true);
359 }
360 #if 0
361 int mouse = r_cons_enable_mouse (false);
362 r_cons_enable_mouse (false);
363 r_cons_flush ();
364 #endif
365 errno = 0;
366 if (cons->user_fgets) {
367 RETURN (cons->user_fgets (buf, len));
368 }
369 printf ("%s", cons->line->prompt);
370 fflush (stdout);
371 *buf = '\0';
372 if (color) {
373 const char *p = cons->context->pal.input;
374 if (R_STR_ISNOTEMPTY (p)) {
375 fwrite (p, strlen (p), 1, stdout);
376 fflush (stdout);
377 }
378 }
379 if (!fgets (buf, len, cons->fdin)) {
380 if (color) {
381 printf (Color_RESET);
382 fflush (stdout);
383 }
384 RETURN (-1);
385 }
386 if (feof (cons->fdin)) {
387 if (color) {
388 printf (Color_RESET);
389 }
390 RETURN (-2);
391 }
392 r_str_trim_tail (buf);
393 if (color) {
394 printf (Color_RESET);
395 }
396 ret = strlen (buf);
397 beach:
398 //r_cons_enable_mouse (mouse);
399 return ret;
400 }
401
r_cons_any_key(const char * msg)402 R_API int r_cons_any_key(const char *msg) {
403 if (msg && *msg) {
404 r_cons_printf ("\n-- %s --\n", msg);
405 } else {
406 r_cons_print ("\n--press any key--\n");
407 }
408 r_cons_flush ();
409 return r_cons_readchar ();
410 //r_cons_strcat ("\x1b[2J\x1b[0;0H"); // wtf?
411 }
412
413 extern void resizeWin(void);
414
415 #if __WINDOWS__
__cons_readchar_w32(ut32 usec)416 static int __cons_readchar_w32(ut32 usec) {
417 int ch = 0;
418 BOOL ret;
419 bCtrl = false;
420 is_arrow = false;
421 DWORD mode, out;
422 HANDLE h;
423 INPUT_RECORD irInBuf = { 0 };
424 CONSOLE_SCREEN_BUFFER_INFO info = { 0 };
425 bool mouse_enabled = I->mouse;
426 bool click_n_drag = false;
427 void *bed;
428 I->mouse_event = 0;
429 h = GetStdHandle (STD_INPUT_HANDLE);
430 GetConsoleMode (h, &mode);
431 DWORD newmode = ENABLE_WINDOW_INPUT;
432 if (I->vtmode == 2) {
433 newmode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
434 }
435 newmode |= mode;
436 SetConsoleMode (h, newmode);
437 do {
438 bed = r_cons_sleep_begin ();
439 if (usec) {
440 if (WaitForSingleObject (h, usec) == WAIT_TIMEOUT) {
441 r_cons_sleep_end (bed);
442 return -1;
443 }
444 }
445 if (I->term_xterm) {
446 ret = ReadFile (h, &ch, 1, &out, NULL);
447 if (ret) {
448 r_cons_sleep_end (bed);
449 return ch;
450 }
451 } else {
452 ret = ReadConsoleInput (h, &irInBuf, 1, &out);
453 }
454 r_cons_sleep_end (bed);
455 if (ret) {
456 if (irInBuf.EventType == MENU_EVENT || irInBuf.EventType == FOCUS_EVENT) {
457 continue;
458 }
459 if (mouse_enabled) {
460 r_cons_enable_mouse (true);
461 }
462 if (irInBuf.EventType == MOUSE_EVENT) {
463 if (irInBuf.Event.MouseEvent.dwEventFlags == MOUSE_MOVED) {
464 if (irInBuf.Event.MouseEvent.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED) {
465 click_n_drag = true;
466 }
467 continue;
468 }
469 if (irInBuf.Event.MouseEvent.dwEventFlags == MOUSE_WHEELED) {
470 if (irInBuf.Event.MouseEvent.dwButtonState & 0xFF000000) {
471 ch = bCtrl ? 'J' : 'j';
472 } else {
473 ch = bCtrl ? 'K' : 'k';
474 }
475 I->mouse_event = 1;
476 }
477 switch (irInBuf.Event.MouseEvent.dwButtonState) {
478 case FROM_LEFT_1ST_BUTTON_PRESSED:
479 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
480 int rel_y = irInBuf.Event.MouseEvent.dwMousePosition.Y - info.srWindow.Top;
481 r_cons_set_click (irInBuf.Event.MouseEvent.dwMousePosition.X + 1, rel_y + 1);
482 ch = UT8_MAX;
483 break;
484 } // TODO: Handle more buttons?
485 }
486
487 if (click_n_drag) {
488 r_cons_set_click (irInBuf.Event.MouseEvent.dwMousePosition.X + 1, irInBuf.Event.MouseEvent.dwMousePosition.Y + 1);
489 ch = UT8_MAX;
490 }
491
492 if (irInBuf.EventType == KEY_EVENT) {
493 if (irInBuf.Event.KeyEvent.bKeyDown) {
494 ch = irInBuf.Event.KeyEvent.uChar.AsciiChar;
495 bCtrl = irInBuf.Event.KeyEvent.dwControlKeyState & 8;
496 if (irInBuf.Event.KeyEvent.uChar.AsciiChar == 0) {
497 switch (irInBuf.Event.KeyEvent.wVirtualKeyCode) {
498 case VK_DOWN: // key down
499 case VK_RIGHT: // key right
500 case VK_UP: // key up
501 case VK_LEFT: // key left
502 case VK_PRIOR: // key home
503 case VK_NEXT: // key end
504 ch = irInBuf.Event.KeyEvent.wVirtualKeyCode;
505 is_arrow = true;
506 break;
507 case VK_F1:
508 ch = R_CONS_KEY_F1;
509 break;
510 case VK_F2:
511 ch = R_CONS_KEY_F2;
512 break;
513 case VK_F3:
514 ch = R_CONS_KEY_F3;
515 break;
516 case VK_F4:
517 ch = R_CONS_KEY_F4;
518 break;
519 case VK_F5:
520 ch = bCtrl ? 0xcf5 : R_CONS_KEY_F5;
521 break;
522 case VK_F6:
523 ch = R_CONS_KEY_F6;
524 break;
525 case VK_F7:
526 ch = R_CONS_KEY_F7;
527 break;
528 case VK_F8:
529 ch = R_CONS_KEY_F8;
530 break;
531 case VK_F9:
532 ch = R_CONS_KEY_F9;
533 break;
534 case VK_F10:
535 ch = R_CONS_KEY_F10;
536 break;
537 case VK_F11:
538 ch = R_CONS_KEY_F11;
539 break;
540 case VK_F12:
541 ch = R_CONS_KEY_F12;
542 case VK_SHIFT:
543 if (mouse_enabled) {
544 r_cons_enable_mouse (false);
545 }
546 break;
547 default:
548 break;
549 }
550 }
551 }
552 }
553 if (irInBuf.EventType == WINDOW_BUFFER_SIZE_EVENT) {
554 resizeWin ();
555 }
556 }
557 if (I->vtmode != 2 && !I->term_xterm) {
558 FlushConsoleInputBuffer (h);
559 }
560 } while (ch == 0);
561 SetConsoleMode (h, mode);
562 return ch;
563 }
564 #endif
565
r_cons_readchar_timeout(ut32 usec)566 R_API int r_cons_readchar_timeout(ut32 usec) {
567 #if __UNIX__
568 struct timeval tv;
569 fd_set fdset, errset;
570 FD_ZERO (&fdset);
571 FD_ZERO (&errset);
572 FD_SET (0, &fdset);
573 tv.tv_sec = 0; // usec / 1000;
574 tv.tv_usec = 1000 * usec;
575 r_cons_set_raw (1);
576 if (select (1, &fdset, NULL, &errset, &tv) == 1) {
577 return r_cons_readchar ();
578 }
579 r_cons_set_raw (0);
580 // timeout
581 return -1;
582 #else
583 return __cons_readchar_w32 (usec);
584 #endif
585 }
586
r_cons_readpush(const char * str,int len)587 R_API bool r_cons_readpush(const char *str, int len) {
588 char *res = (len + readbuffer_length > 0) ? realloc (readbuffer, len + readbuffer_length) : NULL;
589 if (res) {
590 readbuffer = res;
591 memmove (readbuffer + readbuffer_length, str, len);
592 readbuffer_length += len;
593 return true;
594 }
595 return false;
596 }
597
r_cons_readflush(void)598 R_API void r_cons_readflush(void) {
599 R_FREE (readbuffer);
600 readbuffer_length = 0;
601 }
602
r_cons_switchbuf(bool active)603 R_API void r_cons_switchbuf(bool active) {
604 bufactive = active;
605 }
606
607 #if !__WINDOWS__
608 extern volatile sig_atomic_t sigwinchFlag;
609 #endif
610
r_cons_readchar(void)611 R_API int r_cons_readchar(void) {
612 char buf[2];
613 buf[0] = -1;
614 if (readbuffer_length > 0) {
615 int ch = *readbuffer;
616 readbuffer_length--;
617 memmove (readbuffer, readbuffer + 1, readbuffer_length);
618 return ch;
619 }
620 r_cons_set_raw (1);
621 #if __WINDOWS__
622 return __cons_readchar_w32 (0);
623 #else
624 void *bed = r_cons_sleep_begin ();
625
626 // Blocks until either stdin has something to read or a signal happens.
627 // This serves to check if the terminal window was resized. It avoids the race
628 // condition that could happen if we did not use pselect or select in case SIGWINCH
629 // was handled immediately before the blocking call (select or read). The race is
630 // prevented from happening by having SIGWINCH blocked process-wide except for in
631 // pselect (that is what pselect is for).
632 fd_set readfds;
633 sigset_t sigmask;
634 FD_ZERO (&readfds);
635 FD_SET (STDIN_FILENO, &readfds);
636 r_signal_sigmask (0, NULL, &sigmask);
637 sigdelset (&sigmask, SIGWINCH);
638 while (pselect (STDIN_FILENO + 1, &readfds, NULL, NULL, NULL, &sigmask) == -1) {
639 if (errno == EBADF) {
640 eprintf ("r_cons_readchar (): EBADF\n");
641 return -1;
642 }
643 if (sigwinchFlag) {
644 sigwinchFlag = 0;
645 resizeWin ();
646 }
647 }
648
649 ssize_t ret = read (STDIN_FILENO, buf, 1);
650 r_cons_sleep_end (bed);
651 if (ret != 1) {
652 return -1;
653 }
654 if (bufactive) {
655 r_cons_set_raw (0);
656 }
657 return r_cons_controlz (buf[0]);
658 #endif
659 }
660
r_cons_yesno(int def,const char * fmt,...)661 R_API bool r_cons_yesno(int def, const char *fmt, ...) {
662 va_list ap;
663 ut8 key = (ut8)def;
664 va_start (ap, fmt);
665
666 if (!r_cons_is_interactive ()) {
667 va_end (ap);
668 return def == 'y';
669 }
670 vfprintf (stderr, fmt, ap);
671 va_end (ap);
672 fflush (stderr);
673 r_cons_set_raw (true);
674 char buf[] = " ?\n";
675 if (read (0, buf + 1, 1) == 1) {
676 key = (ut8)buf[1];
677 if (write (2, buf, 3) == 3) {
678 if (key == 'Y') {
679 key = 'y';
680 }
681 r_cons_set_raw (false);
682 if (key == '\n' || key == '\r') {
683 key = def;
684 }
685 return key == 'y';
686 }
687 }
688 return false;
689 }
690
r_cons_password(const char * msg)691 R_API char *r_cons_password(const char *msg) {
692 int i = 0;
693 char buf[256] = {0};
694 printf ("\r%s", msg);
695 fflush (stdout);
696 r_cons_set_raw (1);
697 #if __UNIX__
698 RCons *a = r_cons_singleton ();
699 a->term_raw.c_lflag &= ~(ECHO | ECHONL);
700 // // required to make therm/iterm show the key
701 // // cannot read when enabled in this way
702 // a->term_raw.c_lflag |= ICANON;
703 tcsetattr (0, TCSADRAIN, &a->term_raw);
704 r_sys_signal (SIGTSTP, SIG_IGN);
705 #endif
706 while (i < sizeof (buf) - 1) {
707 int ch = r_cons_readchar ();
708 if (ch == 127) { // backspace
709 if (i < 1) {
710 break;
711 }
712 i--;
713 continue;
714 }
715 if (ch == '\r' || ch == '\n') {
716 break;
717 }
718 buf[i++] = ch;
719 }
720 buf[i] = 0;
721 r_cons_set_raw (0);
722 printf ("\n");
723 #if __UNIX__
724 r_sys_signal (SIGTSTP, SIG_DFL);
725 #endif
726 return strdup (buf);
727 }
728
r_cons_input(const char * msg)729 R_API char *r_cons_input(const char *msg) {
730 char *oprompt = r_line_get_prompt ();
731 if (!oprompt) {
732 return NULL;
733 }
734 char buf[1024];
735 if (msg) {
736 r_line_set_prompt (msg);
737 } else {
738 r_line_set_prompt ("");
739 }
740 buf[0] = 0;
741 r_cons_fgets (buf, sizeof (buf), 0, NULL);
742 r_line_set_prompt (oprompt);
743 free (oprompt);
744 return strdup (buf);
745 }
746