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