1 /* GNUPLOT - readline.c */
2 
3 /*[
4  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
5  *
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31 ]*/
32 
33 
34 /*
35  * AUTHORS
36  *
37  *   Original Software:
38  *     Tom Tkacik
39  *
40  *   Msdos port and some enhancements:
41  *     Gershon Elber and many others.
42  *
43  *   Adapted to work with UTF-8 encoding.
44  *     Ethan A Merritt  April 2011
45  */
46 
47 #include <signal.h>
48 
49 #include "stdfn.h"
50 #include "readline.h"
51 
52 #include "alloc.h"
53 #include "gp_hist.h"
54 #include "plot.h"
55 #include "util.h"
56 #include "encoding.h"
57 #include "term_api.h"
58 #ifdef HAVE_WCHAR_H
59 #include <wchar.h>
60 #endif
61 #ifdef WGP_CONSOLE
62 #include "win/winmain.h"
63 #endif
64 
65 /*
66  * adaptor routine for gnu libreadline
67  * to allow multiplexing terminal and mouse input
68  */
69 #if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)
70 int
getc_wrapper(FILE * fp)71 getc_wrapper(FILE* fp)
72 {
73     int c;
74 
75     while (1) {
76 	errno = 0;
77 #ifdef USE_MOUSE
78 	/* EAM June 2020:
79 	 * For 20 years this was conditional on interactive.
80 	 *    if (term && term->waitforinput && interactive)
81 	 * Now I am suspicious that this was the cause of dropped
82 	 * characters when mixing piped input with mousing,
83 	 * e.g. Bugs 2134, 2279
84 	 */
85 	if (term && term->waitforinput) {
86 	    c = term->waitforinput(0);
87 	}
88 	else
89 #endif
90 #if defined(WGP_CONSOLE)
91 	    c = ConsoleGetch();
92 #else
93 	if (fp)
94 	    c = getc(fp);
95 	else
96 	    c = getchar(); /* HAVE_LIBEDITLINE */
97 	if (c == EOF && errno == EINTR)
98 	    continue;
99 #endif
100 	return c;
101     }
102 }
103 #endif /* HAVE_LIBREADLINE || HAVE_LIBEDITLINE */
104 
105 #if defined(HAVE_LIBREADLINE) && defined(HAVE_READLINE_SIGNAL_HANDLER)
106 /*
107  * The signal handler of libreadline sets a flag when SIGTSTP is received
108  * but does not suspend until this flag is checked by other library
109  * routines.  Since gnuplot's term->waitforinput() + getc_wrapper()
110  * replace these other routines, we must do the test and suspend ourselves.
111  */
112 void
wrap_readline_signal_handler()113 wrap_readline_signal_handler()
114 {
115     int sig;
116 
117     /* FIXME:	At the moment, there is no portable way to invoke the
118 	    signal handler. */
119     extern void _rl_signal_handler(int);
120 
121 # ifdef HAVE_READLINE_PENDING_SIGNAL
122     sig = rl_pending_signal();
123 # else
124     /* XXX: We assume all versions of readline have this... */
125     extern int volatile _rl_caught_signal;
126     sig = _rl_caught_signal;
127 # endif
128 
129     if (sig) _rl_signal_handler(sig);
130 }
131 
132 #endif /* defined(HAVE_LIBREADLINE) && defined(HAVE_READLINE_SIGNAL_HANDLER) */
133 
134 
135 #ifdef READLINE
136 
137 /* This is a small portable version of GNU's readline that does not require
138  * any terminal capabilities except backspace and space overwrites a character.
139  * It is not the BASH or GNU EMACS version of READLINE due to Copyleft
140  * restrictions.
141  * Configuration option:   ./configure --with-readline=builtin
142  */
143 
144 /* NANO-EMACS line editing facility
145  * printable characters print as themselves (insert not overwrite)
146  * ^A moves to the beginning of the line
147  * ^B moves back a single character
148  * ^E moves to the end of the line
149  * ^F moves forward a single character
150  * ^K kills from current position to the end of line
151  * ^P moves back through history
152  * ^N moves forward through history
153  * ^H deletes the previous character
154  * ^D deletes the current character, or EOF if line is empty
155  * ^L redraw line in case it gets trashed
156  * ^U kills the entire line
157  * ^W deletes previous full or partial word
158  * ^V disables interpretation of the following key
159  * LF and CR return the entire line regardless of the cursor position
160  * DEL deletes previous or current character (configuration dependent)
161  * TAB will perform filename completion
162  * ^R start a backward-search of the history
163  * EOF with an empty line returns (char *)NULL
164  *
165  * all other characters are ignored
166  */
167 
168 #ifdef HAVE_SYS_IOCTL_H
169 /* For ioctl() prototype under Linux (and BeOS?) */
170 # include <sys/ioctl.h>
171 #endif
172 
173 /* replaces the previous kludge in configure */
174 #if defined(HAVE_TERMIOS_H) && defined(HAVE_TCGETATTR)
175 # define TERMIOS
176 #else /* not HAVE_TERMIOS_H && HAVE_TCGETATTR */
177 # ifdef HAVE_SGTTY_H
178 #  define SGTTY
179 # endif
180 #endif /* not HAVE_TERMIOS_H && HAVE_TCGETATTR */
181 
182 #if !defined(MSDOS) && !defined(_WIN32)
183 
184 /*
185  * Set up structures using the proper include file
186  */
187 # if defined(_IBMR2) || defined(alliant)
188 #  define SGTTY
189 # endif
190 
191 /*  submitted by Francois.Dagorn@cicb.fr */
192 # ifdef SGTTY
193 #  include <sgtty.h>
194 static struct sgttyb orig_termio, rl_termio;
195 /* define terminal control characters */
196 static struct tchars s_tchars;
197 #  ifndef VERASE
198 #   define VERASE    0
199 #  endif			/* not VERASE */
200 #  ifndef VEOF
201 #   define VEOF      1
202 #  endif			/* not VEOF */
203 #  ifndef VKILL
204 #   define VKILL     2
205 #  endif			/* not VKILL */
206 #  ifdef TIOCGLTC		/* available only with the 'new' line discipline */
207 static struct ltchars s_ltchars;
208 #   ifndef VWERASE
209 #    define VWERASE   3
210 #   endif			/* not VWERASE */
211 #   ifndef VREPRINT
212 #    define VREPRINT  4
213 #   endif			/* not VREPRINT */
214 #   ifndef VSUSP
215 #    define VSUSP     5
216 #   endif			/* not VSUP */
217 #  endif			/* TIOCGLTC */
218 #  ifndef NCCS
219 #   define NCCS      6
220 #  endif			/* not NCCS */
221 
222 # else				/* not SGTTY */
223 
224 /* SIGTSTP defines job control
225  * if there is job control then we need termios.h instead of termio.h
226  * (Are there any systems with job control that use termio.h?  I hope not.)
227  */
228 #  if defined(SIGTSTP) || defined(TERMIOS)
229 #   ifndef TERMIOS
230 #    define TERMIOS
231 #   endif			/* not TERMIOS */
232 #   include <termios.h>
233 /* Added by Robert Eckardt, RobertE@beta.TP2.Ruhr-Uni-Bochum.de */
234 #   ifdef ISC22
235 #    ifndef ONOCR		/* taken from sys/termio.h */
236 #     define ONOCR 0000020	/* true at least for ISC 2.2 */
237 #    endif			/* not ONOCR */
238 #    ifndef IUCLC
239 #     define IUCLC 0001000
240 #    endif			/* not IUCLC */
241 #   endif			/* ISC22 */
242 #   if !defined(IUCLC)
243      /* translate upper to lower case not supported */
244 #    define IUCLC 0
245 #   endif			/* not IUCLC */
246 
247 static struct termios orig_termio, rl_termio;
248 #  else				/* not SIGSTP || TERMIOS */
249 #   include <termio.h>
250 static struct termio orig_termio, rl_termio;
251 /* termio defines NCC instead of NCCS */
252 #   define NCCS    NCC
253 #  endif			/* not SIGTSTP || TERMIOS */
254 # endif				/* SGTTY */
255 
256 /* ULTRIX defines VRPRNT instead of VREPRINT */
257 # if defined(VRPRNT) && !defined(VREPRINT)
258 #  define VREPRINT VRPRNT
259 # endif				/* VRPRNT */
260 
261 /* define characters to use with our input character handler */
262 static char term_chars[NCCS];
263 
264 static int term_set = 0;	/* =1 if rl_termio set */
265 
266 #define special_getc() ansi_getc()
267 static int ansi_getc(void);
268 #define DEL_ERASES_CURRENT_CHAR
269 
270 #else /* MSDOS or _WIN32 */
271 
272 # ifdef _WIN32
273 #  include <windows.h>
274 #  include "win/winmain.h"
275 #  include "win/wcommon.h"
276 #  define TEXTUSER 0xf1
277 #  define TEXTGNUPLOT 0xf0
278 #  ifdef WGP_CONSOLE
279 #   define special_getc() win_getch()
280 static int win_getch(void);
281 #  else
282     /* The wgnuplot text window will suppress intermediate
283        screen updates in 'suspend' mode and only redraw the
284        input line after 'resume'. */
285 #   define SUSPENDOUTPUT TextSuspend(&textwin)
286 #   define RESUMEOUTPUT TextResume(&textwin)
287 #   define special_getc() msdos_getch()
288 static int msdos_getch(void);
289 #  endif /* WGP_CONSOLE */
290 #  define DEL_ERASES_CURRENT_CHAR
291 # endif				/* _WIN32 */
292 
293 # if defined(MSDOS)
294 /* MSDOS specific stuff */
295 #  ifdef DJGPP
296 #   include <pc.h>
297 #  endif			/* DJGPP */
298 #  if defined(__EMX__) || defined (__WATCOMC__)
299 #   include <conio.h>
300 #  endif			/* __EMX__ */
301 #  define special_getc() msdos_getch()
302 static int msdos_getch();
303 #  define DEL_ERASES_CURRENT_CHAR
304 # endif				/* MSDOS */
305 
306 #endif /* MSDOS or _WIN32 */
307 
308 #ifdef OS2
309 # if defined( special_getc )
310 #  undef special_getc()
311 # endif				/* special_getc */
312 # define special_getc() os2_getch()
313 static int msdos_getch(void);
314 static int os2_getch(void);
315 #  define DEL_ERASES_CURRENT_CHAR
316 #endif /* OS2 */
317 
318 
319 /* initial size and increment of input line length */
320 #define MAXBUF	1024
321 #define BACKSPACE '\b'   /* ^H */
322 #define SPACE	' '
323 #define NEWLINE	'\n'
324 
325 #define MAX_COMPLETIONS 50
326 
327 #ifndef SUSPENDOUTPUT
328 #define SUSPENDOUTPUT
329 #define RESUMEOUTPUT
330 #endif
331 
332 static char *cur_line;		/* current contents of the line */
333 static size_t line_len = 0;
334 static size_t cur_pos = 0;	/* current position of the cursor */
335 static size_t max_pos = 0;	/* maximum character position */
336 
337 static TBOOLEAN search_mode = FALSE;
338 static const char search_prompt[] = "search '";
339 static const char search_prompt2[] = "': ";
340 static struct hist * search_result = NULL;
341 static int search_result_width = 0;	/* on-screen width of the search result */
342 
343 static void fix_line(void);
344 static void redraw_line(const char *prompt);
345 static void clear_line(const char *prompt);
346 static void clear_eoline(const char *prompt);
347 static void delete_previous_word(void);
348 static void copy_line(char *line);
349 static void set_termio(void);
350 static void reset_termio(void);
351 static int user_putc(int ch);
352 static int user_puts(char *str);
353 static int backspace(void);
354 static void extend_cur_line(void);
355 static void step_forward(void);
356 static void delete_forward(void);
357 static void delete_backward(void);
358 static int char_seqlen(void);
359 #if defined(HAVE_DIRENT)
360 static char *fn_completion(size_t anchor_pos, int direction);
361 static void tab_completion(TBOOLEAN forward);
362 #endif
363 static void switch_prompt(const char * old_prompt, const char * new_prompt);
364 static int do_search(int dir);
365 static void print_search_result(const struct hist * result);
366 
367 #ifndef _WIN32
368 static int mbwidth(const char *c);
369 #endif
370 static int strwidth(const char * str);
371 
372 /* user_putc and user_puts should be used in the place of
373  * fputc(ch,stderr) and fputs(str,stderr) for all output
374  * of user typed characters.  This allows MS-Windows to
375  * display user input in a different color.
376  */
377 static int
user_putc(int ch)378 user_putc(int ch)
379 {
380     int rv;
381 #if defined(_WIN32) && !defined(WGP_CONSOLE)
382     TextAttr(&textwin, TEXTUSER);
383 #endif
384     rv = fputc(ch, stderr);
385 #if defined(_WIN32) && !defined(WGP_CONSOLE)
386     TextAttr(&textwin, TEXTGNUPLOT);
387 #endif
388     return rv;
389 }
390 
391 static int
user_puts(char * str)392 user_puts(char *str)
393 {
394     int rv;
395 #if defined(_WIN32) && !defined(WGP_CONSOLE)
396     TextAttr(&textwin, TEXTUSER);
397 #endif
398     rv = fputs(str, stderr);
399 #if defined(_WIN32) && !defined(WGP_CONSOLE)
400     TextAttr(&textwin, TEXTGNUPLOT);
401 #endif
402     return rv;
403 }
404 
405 
406 #if !defined(_WIN32)
407 /* EAM FIXME
408  * This test is intended to determine if the current character, of which
409  * we have only seen the first byte so far, will require twice the width
410  * of an ascii character.  The test catches glyphs above unicode 0x3000,
411  * which is roughly the set of CJK characters.
412  * It should be replaced with a more accurate test.
413  */
414 static int
mbwidth(const char * c)415 mbwidth(const char *c)
416 {
417     switch (encoding) {
418 
419     case S_ENC_UTF8:
420 	return ((unsigned char)(*c) >= 0xe3 ? 2 : 1);
421 
422     case S_ENC_SJIS:
423 	/* Assume all double-byte characters have double-width. */
424 	return is_sjis_lead_byte(*c) ? 2 : 1;
425 
426     default:
427 	return 1;
428     }
429 }
430 #endif
431 
432 static int
strwidth(const char * str)433 strwidth(const char * str)
434 {
435 #if !defined(_WIN32)
436     int width = 0;
437     int i = 0;
438 
439     switch (encoding) {
440     case S_ENC_UTF8:
441 	while (str[i]) {
442 	    const char *ch = &str[i++];
443 	    if ((*ch & 0xE0) == 0xC0) {
444 		i += 1;
445 	    } else if ((*ch & 0xF0) == 0xE0) {
446 		i += 2;
447 	    } else if ((*ch & 0xF8) == 0xF0) {
448 		i += 3;
449 	    }
450 	    width += mbwidth(ch);
451 	}
452 	break;
453     case S_ENC_SJIS:
454 	/* Assume all double-byte characters have double-width. */
455 	width = gp_strlen(str);
456 	break;
457     default:
458 	width = strlen(str);
459     }
460     return width;
461 #else
462     /* double width characters are handled in the backend */
463     return gp_strlen(str);
464 #endif
465 }
466 
467 static int
isdoublewidth(size_t pos)468 isdoublewidth(size_t pos)
469 {
470 #if defined(_WIN32)
471     /* double width characters are handled in the backend */
472     return FALSE;
473 #else
474     return mbwidth(cur_line + pos) > 1;
475 #endif
476 }
477 
478 /*
479  * Determine length of multi-byte sequence starting at current position
480  */
481 static int
char_seqlen()482 char_seqlen()
483 {
484     switch (encoding) {
485 
486     case S_ENC_UTF8: {
487 	int i = cur_pos;
488 	do {i++;}
489 	while (((cur_line[i] & 0xc0) != 0xc0)
490 	       && ((cur_line[i] & 0x80) != 0)
491 	       && (i < max_pos));
492 	return (i - cur_pos);
493     }
494 
495     case S_ENC_SJIS:
496 	return is_sjis_lead_byte(cur_line[cur_pos]) ? 2 : 1;
497 
498     default:
499 	return 1;
500     }
501 }
502 
503 /*
504  * Back up over one multi-byte character sequence immediately preceding
505  * the current position.  Non-destructive.  Affects both cur_pos and screen cursor.
506  */
507 static int
backspace()508 backspace()
509 {
510     switch (encoding) {
511 
512     case S_ENC_UTF8: {
513 	int seqlen = 0;
514 	do {
515 	    cur_pos--;
516 	    seqlen++;
517 	} while (((cur_line[cur_pos] & 0xc0) != 0xc0)
518 	         && ((cur_line[cur_pos] & 0x80) != 0)
519 		 && (cur_pos > 0)
520 	        );
521 
522 	if (   ((cur_line[cur_pos] & 0xc0) == 0xc0)
523 	    || isprint((unsigned char)cur_line[cur_pos])
524 	   )
525 	    user_putc(BACKSPACE);
526 	if (isdoublewidth(cur_pos))
527 	    user_putc(BACKSPACE);
528 	return seqlen;
529     }
530 
531     case S_ENC_SJIS: {
532 	/* With S-JIS you cannot always determine if a byte is a single byte or part
533 	   of a double-byte sequence by looking of an arbitrary byte in a string.
534 	   Always test from the start of the string instead.
535 	*/
536 	int i;
537         int seqlen = 1;
538 
539 	for (i = 0; i < cur_pos; i += seqlen) {
540 	    seqlen = is_sjis_lead_byte(cur_line[i]) ? 2 : 1;
541 	}
542 	cur_pos -= seqlen;
543 	user_putc(BACKSPACE);
544 	if (isdoublewidth(cur_pos))
545 	    user_putc(BACKSPACE);
546 	return seqlen;
547     }
548 
549     default:
550 	cur_pos--;
551 	user_putc(BACKSPACE);
552 	return 1;
553     }
554 }
555 
556 /*
557  * Step forward over one multi-byte character sequence.
558  * We don't assume a non-destructive forward space, so we have
559  * to redraw the character as we go.
560  */
561 static void
step_forward()562 step_forward()
563 {
564     int i, seqlen;
565 
566     switch (encoding) {
567 
568     case S_ENC_UTF8:
569     case S_ENC_SJIS:
570 	seqlen = char_seqlen();
571 	for (i = 0; i < seqlen; i++)
572 	    user_putc(cur_line[cur_pos++]);
573 	break;
574 
575     default:
576 	user_putc(cur_line[cur_pos++]);
577 	break;
578     }
579 }
580 
581 /*
582  * Delete the character we are on and collapse all subsequent characters back one
583  */
584 static void
delete_forward()585 delete_forward()
586 {
587     if (cur_pos < max_pos) {
588 	size_t i;
589 	int seqlen = char_seqlen();
590 	max_pos -= seqlen;
591 	for (i = cur_pos; i < max_pos; i++)
592 	    cur_line[i] = cur_line[i + seqlen];
593 	cur_line[max_pos] = '\0';
594 	fix_line();
595     }
596 }
597 
598 /*
599  * Delete the previous character and collapse all subsequent characters back one
600  */
601 static void
delete_backward()602 delete_backward()
603 {
604     if (cur_pos > 0) {
605 	size_t i;
606 	int seqlen = backspace();
607 	max_pos -= seqlen;
608 	for (i = cur_pos; i < max_pos; i++)
609 	    cur_line[i] = cur_line[i + seqlen];
610 	cur_line[max_pos] = '\0';
611 	fix_line();
612     }
613 }
614 
615 static void
extend_cur_line()616 extend_cur_line()
617 {
618     char *new_line;
619 
620     /* extend input line length */
621     new_line = (char *) gp_realloc(cur_line, line_len + MAXBUF, NULL);
622     if (!new_line) {
623 	reset_termio();
624 	int_error(NO_CARET, "Can't extend readline length");
625     }
626     cur_line = new_line;
627     line_len += MAXBUF;
628     FPRINTF((stderr, "\nextending readline length to %d chars\n", line_len));
629 }
630 
631 
632 #if defined(HAVE_DIRENT)
633 static char *
fn_completion(size_t anchor_pos,int direction)634 fn_completion(size_t anchor_pos, int direction)
635 {
636     static char * completions[MAX_COMPLETIONS];
637     static int n_completions = 0;
638     static int completion_idx = 0;
639 
640     if (direction == 0) {
641 	/* new completion */
642 	DIR * dir;
643 	char * start, * path;
644 	char * t, * search;
645 	char * name = NULL;
646 	size_t nlen;
647 
648 	if (n_completions != 0) {
649 	    /* new completion, cleanup first */
650 	    int i;
651 	    for (i = 0; i < n_completions; i++)
652 		free(completions[i]);
653 	    memset(completions, 0, sizeof(completions));
654 	    n_completions = 0;
655 	    completion_idx = 0;
656 	}
657 
658 	/* extract path to complete */
659 	start = cur_line + anchor_pos;
660 	if (anchor_pos > 0) {
661 	    /* first, look for a quote to start the string */
662 	    for ( ; start >= cur_line; start--) {
663 	        if ((*start == '"') || (*start == '\'')) {
664 		    start++;
665 		    /* handle pipe commands */
666 		    if ((*start == '<') || (*start == '|'))
667 			start++;
668 		    break;
669 		}
670 	    }
671 	    /* if not found, search for a space or a system command '!' instead */
672 	    if (start <= cur_line) {
673 		for (start = cur_line + anchor_pos; start >= cur_line; start--) {
674 		    if ((*start == ' ') || (*start == '!')) {
675 			start++;
676 			break;
677 		    }
678 		}
679 	    }
680 	    if (start < cur_line)
681 		start = cur_line;
682 
683 	    path = strndup(start, cur_line - start + anchor_pos);
684 	    gp_expand_tilde(&path);
685 	} else {
686 	    path = gp_strdup("");
687 	}
688 
689 	/* separate directory and (partial) file directory name */
690 	t = strrchr(path, DIRSEP1);
691 #if DIRSEP2 != NUL
692 	if (t == NULL) t = strrchr(path, DIRSEP2);
693 #endif
694 	if (t == NULL) {
695 	    /* name... */
696 	    search = gp_strdup(".");
697 	    name = strdup(path);
698 	} else if (t == path) {
699 	    /* root dir: /name... */
700 	    search = strndup(path, 1);
701 	    nlen = cur_pos - (t - path) - 1;
702 	    name = strndup(t + 1, nlen);
703 	} else {
704 	    /* normal case: dir/dir/name... */
705 	    search = strndup(path, t - path);
706 	    nlen = cur_pos - (t - path) - 1;
707 	    name = strndup(t + 1, nlen);
708 	}
709 	nlen = strlen(name);
710 	free(path);
711 
712 	n_completions = 0;
713 	if ((dir = opendir(search))) {
714 	    struct dirent * entry;
715 	    while ((entry = readdir(dir)) != NULL) {
716 		/* ignore files and directories starting with a dot */
717 		if (entry->d_name[0] == '.')
718 		    continue;
719 
720 		/* skip entries which don't match */
721 		if (nlen > 0)
722 		    if (strncmp(entry->d_name, name, nlen) != 0) continue;
723 
724 		completions[n_completions] = gp_strdup(entry->d_name + nlen);
725 		n_completions++;
726 
727 		/* limit number of completions */
728 		if (n_completions == MAX_COMPLETIONS) break;
729 	    }
730 	    closedir(dir);
731 	    free(search);
732 	    if (name) free(name);
733 	    if (n_completions > 0)
734 		return completions[0];
735 	    else
736 		return NULL;
737 	}
738 	free(search);
739 	if (name) free(name);
740     } else {
741 	/* cycle trough previous results */
742 	if (n_completions > 0) {
743 	    if (direction > 0)
744 		completion_idx = (completion_idx + 1) % n_completions;
745 	    else
746 		completion_idx = (completion_idx + n_completions - 1) % n_completions;
747 	    return completions[completion_idx];
748 	} else
749 	    return NULL;
750     }
751     return NULL;
752 }
753 
754 
755 static void
tab_completion(TBOOLEAN forward)756 tab_completion(TBOOLEAN forward)
757 {
758     size_t i;
759     char * completion;
760     size_t completion_len;
761     static size_t last_tab_pos = -1;
762     static size_t last_completion_len = 0;
763     int direction;
764 
765     /* detect tab cycling */
766     if ((last_tab_pos + last_completion_len) != cur_pos) {
767 	last_completion_len = 0;
768 	last_tab_pos = cur_pos;
769 	direction = 0; /* new completion */
770     } else {
771 	direction = (forward ? 1 : -1);
772     }
773 
774     /* find completion */
775     completion = fn_completion(last_tab_pos, direction);
776     if (!completion) return;
777 
778     /* make room for new completion */
779     completion_len = strlen(completion);
780     if (completion_len > last_completion_len)
781 	while (max_pos + completion_len - last_completion_len + 1 > line_len)
782 	    extend_cur_line();
783 
784     SUSPENDOUTPUT;
785     /* erase from last_tab_pos to eol */
786     while (cur_pos > last_tab_pos)
787 	backspace();
788     while (cur_pos < max_pos) {
789 	user_putc(SPACE);
790 	if (isdoublewidth(cur_pos))
791 	    user_putc(SPACE);
792 	cur_pos += char_seqlen();
793     }
794 
795     /* rewind to last_tab_pos */
796     while (cur_pos > last_tab_pos)
797 	backspace();
798 
799     /* insert completion string */
800     if (max_pos > (last_tab_pos - last_completion_len))
801 	memmove(cur_line + last_tab_pos + completion_len,
802 		cur_line + last_tab_pos + last_completion_len,
803 		max_pos  - last_tab_pos - last_completion_len);
804     memcpy(cur_line + last_tab_pos, completion, completion_len);
805     max_pos += completion_len - last_completion_len;
806     cur_line[max_pos] = NUL;
807 
808     /* draw new completion */
809     for (i = 0; i < completion_len; i++)
810 	user_putc(cur_line[last_tab_pos+i]);
811     cur_pos += completion_len;
812     fix_line();
813     RESUMEOUTPUT;
814 
815     /* remember this completion */
816     last_tab_pos  = cur_pos - completion_len;
817     last_completion_len = completion_len;
818 }
819 
820 #endif /* HAVE_DIRENT */
821 
822 
823 char *
readline(const char * prompt)824 readline(const char *prompt)
825 {
826     int cur_char;
827     char *new_line;
828     TBOOLEAN next_verbatim = FALSE;
829     char *prev_line;
830 
831     /* start with a string of MAXBUF chars */
832     if (line_len != 0) {
833 	free(cur_line);
834 	line_len = 0;
835     }
836     cur_line = (char *) gp_alloc(MAXBUF, "readline");
837     line_len = MAXBUF;
838 
839     /* set the termio so we can do our own input processing */
840     set_termio();
841 
842     /* print the prompt */
843     fputs(prompt, stderr);
844     cur_line[0] = '\0';
845     cur_pos = 0;
846     max_pos = 0;
847 
848     /* move to end of history */
849     while (next_history());
850 
851     /* init global variables */
852     search_mode = FALSE;
853 
854     /* get characters */
855     for (;;) {
856 
857 	cur_char = special_getc();
858 
859 	/* Accumulate ascii (7bit) printable characters
860 	 * and all leading 8bit characters.
861 	 */
862 	if (((isprint(cur_char)
863 	      || (((cur_char & 0x80) != 0) && (cur_char != EOF))
864 	     ) && (cur_char != '\t')) /* TAB is a printable character in some locales */
865 	    || next_verbatim
866 	    ) {
867 	    size_t i;
868 
869 	    if (max_pos + 1 >= line_len) {
870 		extend_cur_line();
871 	    }
872 	    for (i = max_pos; i > cur_pos; i--) {
873 		cur_line[i] = cur_line[i - 1];
874 	    }
875 	    user_putc(cur_char);
876 
877 	    cur_line[cur_pos] = cur_char;
878 	    cur_pos += 1;
879 	    max_pos += 1;
880 	    cur_line[max_pos] = '\0';
881 
882 	    if (cur_pos < max_pos) {
883 		switch (encoding) {
884 		case S_ENC_UTF8:
885 		    if ((cur_char & 0xc0) == 0) {
886 			next_verbatim = FALSE;
887 			fix_line(); /* Normal ascii character */
888 		    } else if ((cur_char & 0xc0) == 0xc0) {
889 			; /* start of a multibyte sequence. */
890 		    } else if (((cur_char & 0xc0) == 0x80) &&
891 			 ((unsigned char)(cur_line[cur_pos-2]) >= 0xe0)) {
892 			; /* second byte of a >2 byte sequence */
893 		    } else {
894 			/* Last char of multi-byte sequence */
895 			next_verbatim = FALSE;
896 			fix_line();
897 		    }
898 		    break;
899 
900 		case S_ENC_SJIS: {
901 		    /* S-JIS requires a state variable */
902 		    static int mbwait = 0;
903 
904 		    if (mbwait == 0) {
905 		        if (!is_sjis_lead_byte(cur_char)) {
906 			    /* single-byte character */
907 			    next_verbatim = FALSE;
908 			    fix_line();
909 			} else {
910 			    /* first byte of a double-byte sequence */
911 			    ;
912 			}
913 		    } else {
914 			/* second byte of a double-byte sequence */
915 			mbwait = 0;
916 			next_verbatim = FALSE;
917 			fix_line();
918 		    }
919 		}
920 
921 		default:
922 		    next_verbatim = FALSE;
923 		    fix_line();
924 		    break;
925 		}
926 	    } else {
927 		static int mbwait = 0;
928 
929 		next_verbatim = FALSE;
930 		if (search_mode) {
931 		    /* Only update the search at the end of a multi-byte sequence. */
932 		    if (mbwait == 0) {
933 			if (encoding == S_ENC_SJIS)
934 			    mbwait = is_sjis_lead_byte(cur_char) ? 1 : 0;
935 			if (encoding == S_ENC_UTF8) {
936 			    char ch = cur_char;
937 			    if (ch & 0x80)
938 				while ((ch = (ch << 1)) & 0x80)
939 				    mbwait++;
940 			}
941 		    } else {
942 			mbwait--;
943 		    }
944 		    if (!mbwait)
945 			do_search(-1);
946 		}
947 	    }
948 
949 	/* ignore special characters in search_mode */
950 	} else if (!search_mode) {
951 
952 	if (0) {
953 	    ;
954 
955 	/* else interpret unix terminal driver characters */
956 #ifdef VERASE
957 	} else if (cur_char == term_chars[VERASE]) {	/* ^H */
958 	    delete_backward();
959 #endif /* VERASE */
960 #ifdef VEOF
961 	} else if (cur_char == term_chars[VEOF]) {	/* ^D? */
962 	    if (max_pos == 0) {
963 		reset_termio();
964 		return ((char *) NULL);
965 	    }
966 	    delete_forward();
967 #endif /* VEOF */
968 #ifdef VKILL
969 	} else if (cur_char == term_chars[VKILL]) {	/* ^U? */
970 	    clear_line(prompt);
971 #endif /* VKILL */
972 #ifdef VWERASE
973 	} else if (cur_char == term_chars[VWERASE]) {	/* ^W? */
974 	    delete_previous_word();
975 #endif /* VWERASE */
976 #ifdef VREPRINT
977 #if 0 /* conflict with reverse-search */
978 	} else if (cur_char == term_chars[VREPRINT]) {	/* ^R? */
979 	    putc(NEWLINE, stderr);	/* go to a fresh line */
980 	    redraw_line(prompt);
981 #endif
982 #endif /* VREPRINT */
983 #ifdef VSUSP
984 	} else if (cur_char == term_chars[VSUSP]) {
985 	    reset_termio();
986 	    kill(0, SIGTSTP);
987 
988 	    /* process stops here */
989 
990 	    set_termio();
991 	    /* print the prompt */
992 	    redraw_line(prompt);
993 #endif /* VSUSP */
994 	} else {
995 	    /* do normal editing commands */
996 	    /* some of these are also done above */
997 	    switch (cur_char) {
998 	    case EOF:
999 		reset_termio();
1000 		return ((char *) NULL);
1001 	    case 001:		/* ^A */
1002 		while (cur_pos > 0)
1003 		    backspace();
1004 		break;
1005 	    case 002:		/* ^B */
1006 		if (cur_pos > 0)
1007 		    backspace();
1008 		break;
1009 	    case 005:		/* ^E */
1010 		while (cur_pos < max_pos) {
1011 		    user_putc(cur_line[cur_pos]);
1012 		    cur_pos += 1;
1013 		}
1014 		break;
1015 	    case 006:		/* ^F */
1016 		if (cur_pos < max_pos) {
1017 		    step_forward();
1018 		}
1019 		break;
1020 #if defined(HAVE_DIRENT)
1021 	    case 011:		/* ^I / TAB */
1022 		tab_completion(TRUE); /* next tab completion */
1023 		break;
1024 	    case 034:		/* remapped by wtext.c or ansi_getc from Shift-Tab */
1025 		tab_completion(FALSE); /* previous tab completion */
1026 		break;
1027 #endif
1028 	    case 013:		/* ^K */
1029 		clear_eoline(prompt);
1030 		max_pos = cur_pos;
1031 		break;
1032 	    case 020:		/* ^P */
1033 		if (previous_history() != NULL) {
1034 		    clear_line(prompt);
1035 		    copy_line(current_history()->line);
1036 		}
1037 		break;
1038 	    case 016:		/* ^N */
1039 		clear_line(prompt);
1040 		if (next_history() != NULL) {
1041 		    copy_line(current_history()->line);
1042 		} else {
1043 		    cur_pos = max_pos = 0;
1044 		}
1045 		break;
1046 	    case 022:		/* ^R */
1047 		prev_line = strdup(cur_line);
1048 		switch_prompt(prompt, search_prompt);
1049 		while (next_history()); /* seek to end of history */
1050 		search_result = NULL;
1051 		search_result_width = 0;
1052 		search_mode = TRUE;
1053 		print_search_result(NULL);
1054 		break;
1055 	    case 014:		/* ^L */
1056 		putc(NEWLINE, stderr);	/* go to a fresh line */
1057 		redraw_line(prompt);
1058 		break;
1059 #ifndef DEL_ERASES_CURRENT_CHAR
1060 	    case 0177:		/* DEL */
1061 	    case 023:		/* Re-mapped from CSI~3 in ansi_getc() */
1062 #endif
1063 	    case 010:		/* ^H */
1064 		delete_backward();
1065 		break;
1066 	    case 004:		/* ^D */
1067 		/* Also catch asynchronous termination signal on Windows */
1068 		if (max_pos == 0 || terminate_flag) {
1069 		    reset_termio();
1070 		    return NULL;
1071 		}
1072 		/* intentionally omitting break */
1073 #ifdef DEL_ERASES_CURRENT_CHAR
1074 	    case 0177:		/* DEL */
1075 	    case 023:		/* Re-mapped from CSI~3 in ansi_getc() */
1076 #endif
1077 		delete_forward();
1078 		break;
1079 	    case 025:		/* ^U */
1080 		clear_line(prompt);
1081 		break;
1082 	    case 026:		/* ^V */
1083 		next_verbatim = TRUE;
1084 		break;
1085 	    case 027:		/* ^W */
1086 		delete_previous_word();
1087 		break;
1088 	    case '\n':		/* ^J */
1089 	    case '\r':		/* ^M */
1090 		cur_line[max_pos + 1] = '\0';
1091 #ifdef OS2
1092 		while (cur_pos < max_pos) {
1093 		    user_putc(cur_line[cur_pos]);
1094 		    cur_pos += 1;
1095 		}
1096 #endif
1097 		putc(NEWLINE, stderr);
1098 
1099 		/* Shrink the block down to fit the string ?
1100 		 * if the alloc fails, we still own block at cur_line,
1101 		 * but this shouldn't really fail.
1102 		 */
1103 		new_line = (char *) gp_realloc(cur_line, strlen(cur_line) + 1,
1104 					       "line resize");
1105 		if (new_line)
1106 		    cur_line = new_line;
1107 		/* else we just hang on to what we had - it's not a problem */
1108 
1109 		line_len = 0;
1110 		FPRINTF((stderr, "Resizing input line to %d chars\n", strlen(cur_line)));
1111 		reset_termio();
1112 		return (cur_line);
1113 	    default:
1114 		break;
1115 	    }
1116 	}
1117 
1118 	} else {  /* search-mode */
1119 #ifdef VERASE
1120 	    if (cur_char == term_chars[VERASE]) {	/* ^H */
1121 		delete_backward();
1122 		do_search(-1);
1123 	    } else
1124 #endif /* VERASE */
1125 	    {
1126 	    switch (cur_char) {
1127 	    case 022:		/* ^R */
1128 		/* search next */
1129 		previous_history();
1130 		if (do_search(-1) == -1)
1131 		    next_history();
1132 		break;
1133 	    case 023:		/* ^S */
1134 		/* search previous */
1135 		next_history();
1136 		if (do_search(1) == -1)
1137 		    previous_history();
1138 		break;
1139 		break;
1140 	    case '\n':		/* ^J */
1141 	    case '\r':		/* ^M */
1142 		/* accept */
1143 		switch_prompt(search_prompt, prompt);
1144 		if (search_result != NULL)
1145 		    copy_line(search_result->line);
1146 		free(prev_line);
1147 		search_result_width = 0;
1148 		search_mode = FALSE;
1149 		break;
1150 #ifndef DEL_ERASES_CURRENT_CHAR
1151 	    case 0177:		/* DEL */
1152 	    /* FIXME: conflict! */
1153 	    //case 023:		/* Re-mapped from CSI~3 in ansi_getc() */
1154 #endif
1155 	    case 010:		/* ^H */
1156 		delete_backward();
1157 		do_search(1);
1158 		break;
1159 	    default:
1160 		/* abort, restore previous input line */
1161 		switch_prompt(search_prompt, prompt);
1162 		copy_line(prev_line);
1163 		free(prev_line);
1164 		search_result_width = 0;
1165 		search_mode = FALSE;
1166 		break;
1167 	    }
1168 	    }
1169 	}
1170     }
1171 }
1172 
1173 
1174 static int
do_search(int dir)1175 do_search(int dir)
1176 {
1177     int ret = -1;
1178 
1179     if ((ret = history_search(cur_line, dir)) != -1)
1180 	search_result = current_history();
1181     print_search_result(search_result);
1182     return ret;
1183 }
1184 
1185 
1186 void
print_search_result(const struct hist * result)1187 print_search_result(const struct hist * result)
1188 {
1189     int i, width = 0;
1190 
1191     SUSPENDOUTPUT;
1192     fputs(search_prompt2, stderr);
1193     if (result != NULL && result->line != NULL) {
1194 	fputs(result->line, stderr);
1195 	width = strwidth(result->line);
1196     }
1197 
1198     /* overwrite previous search result, and the line might
1199        just have gotten 1 double-width character shorter */
1200     for (i = 0; i < search_result_width - width + 2; i++)
1201 	putc(SPACE, stderr);
1202     for (i = 0; i < search_result_width - width + 2; i++)
1203 	putc(BACKSPACE, stderr);
1204     search_result_width = width;
1205 
1206     /* restore cursor position */
1207     for (i = 0; i < width; i++)
1208 	putc(BACKSPACE, stderr);
1209     for (i = 0; i < strlen(search_prompt2); i++)
1210 	putc(BACKSPACE, stderr);
1211     RESUMEOUTPUT;
1212 }
1213 
1214 
1215 static void
switch_prompt(const char * old_prompt,const char * new_prompt)1216 switch_prompt(const char * old_prompt, const char * new_prompt)
1217 {
1218     int i, len;
1219 
1220     SUSPENDOUTPUT;
1221 
1222     /* clear search results (if any) */
1223     if (search_mode) {
1224 	for (i = 0; i < search_result_width + strlen(search_prompt2); i++)
1225 	    user_putc(SPACE);
1226 	for (i = 0; i < search_result_width + strlen(search_prompt2); i++)
1227 	    user_putc(BACKSPACE);
1228     }
1229 
1230     /* clear current line */
1231     clear_line(old_prompt);
1232     putc('\r', stderr);
1233     fputs(new_prompt, stderr);
1234     cur_pos = 0;
1235 
1236     /* erase remainder of previous prompt */
1237     len = GPMAX((int)strlen(old_prompt) - (int)strlen(new_prompt), 0);
1238     for (i = 0; i < len; i++)
1239 	user_putc(SPACE);
1240     for (i = 0; i < len; i++)
1241 	user_putc(BACKSPACE);
1242     RESUMEOUTPUT;
1243 }
1244 
1245 
1246 /* Fix up the line from cur_pos to max_pos.
1247  * Does not need any terminal capabilities except backspace,
1248  * and space overwrites a character
1249  */
1250 static void
fix_line()1251 fix_line()
1252 {
1253     size_t i;
1254 
1255     SUSPENDOUTPUT;
1256     /* write tail of string */
1257     for (i = cur_pos; i < max_pos; i++)
1258 	user_putc(cur_line[i]);
1259 
1260     /* We may have just shortened the line by deleting a character.
1261      * Write a space at the end to over-print the former last character.
1262      * It needs 2 spaces in case the former character was double width.
1263      */
1264     user_putc(SPACE);
1265     user_putc(SPACE);
1266     if (search_mode) {
1267 	for (i = 0; i < search_result_width; i++)
1268 	    user_putc(SPACE);
1269 	for (i = 0; i < search_result_width; i++)
1270 	    user_putc(BACKSPACE);
1271     }
1272     user_putc(BACKSPACE);
1273     user_putc(BACKSPACE);
1274 
1275     /* Back up to original position */
1276     i = cur_pos;
1277     for (cur_pos = max_pos; cur_pos > i; )
1278 	backspace();
1279     RESUMEOUTPUT;
1280 }
1281 
1282 /* redraw the entire line, putting the cursor where it belongs */
1283 static void
redraw_line(const char * prompt)1284 redraw_line(const char *prompt)
1285 {
1286     size_t i;
1287 
1288     SUSPENDOUTPUT;
1289     fputs(prompt, stderr);
1290     user_puts(cur_line);
1291 
1292     /* put the cursor where it belongs */
1293     i = cur_pos;
1294     for (cur_pos = max_pos; cur_pos > i; )
1295 	backspace();
1296     RESUMEOUTPUT;
1297 }
1298 
1299 /* clear cur_line and the screen line */
1300 static void
clear_line(const char * prompt)1301 clear_line(const char *prompt)
1302 {
1303     SUSPENDOUTPUT;
1304     putc('\r', stderr);
1305     fputs(prompt, stderr);
1306     cur_pos = 0;
1307 
1308     while (cur_pos < max_pos) {
1309 	user_putc(SPACE);
1310 	if (isdoublewidth(cur_pos))
1311 	    user_putc(SPACE);
1312 	cur_pos += char_seqlen();
1313     }
1314     while (max_pos > 0)
1315 	cur_line[--max_pos] = '\0';
1316 
1317     putc('\r', stderr);
1318     fputs(prompt, stderr);
1319     cur_pos = 0;
1320     RESUMEOUTPUT;
1321 }
1322 
1323 /* clear to end of line and the screen end of line */
1324 static void
clear_eoline(const char * prompt)1325 clear_eoline(const char *prompt)
1326 {
1327     size_t save_pos = cur_pos;
1328 
1329     SUSPENDOUTPUT;
1330     while (cur_pos < max_pos) {
1331 	user_putc(SPACE);
1332 	if (isdoublewidth(cur_line[cur_pos]))
1333 	    user_putc(SPACE);
1334 	cur_pos += char_seqlen();
1335     }
1336     cur_pos = save_pos;
1337     while (max_pos > cur_pos)
1338 	cur_line[--max_pos] = '\0';
1339 
1340     putc('\r', stderr);
1341     fputs(prompt, stderr);
1342     user_puts(cur_line);
1343     RESUMEOUTPUT;
1344 }
1345 
1346 /* delete the full or partial word immediately before cursor position */
1347 static void
delete_previous_word()1348 delete_previous_word()
1349 {
1350     size_t save_pos = cur_pos;
1351 
1352     SUSPENDOUTPUT;
1353     /* skip whitespace */
1354     while ((cur_pos > 0) &&
1355 	   (cur_line[cur_pos - 1] == SPACE)) {
1356 	backspace();
1357     }
1358     /* find start of previous word */
1359     while ((cur_pos > 0) &&
1360 	   (cur_line[cur_pos - 1] != SPACE)) {
1361 	backspace();
1362     }
1363     if (cur_pos != save_pos) {
1364 	size_t new_cur_pos = cur_pos;
1365 	size_t m = max_pos - save_pos;
1366 
1367 	/* erase to eol */
1368 	while (cur_pos < max_pos) {
1369 	    user_putc(SPACE);
1370 	    if (isdoublewidth(cur_pos))
1371 		user_putc(SPACE);
1372 	    cur_pos += char_seqlen();
1373 	}
1374 	while (cur_pos > new_cur_pos)
1375 	    backspace();
1376 
1377 	/* overwrite previous word with trailing characters */
1378 	memmove(cur_line + cur_pos, cur_line + save_pos, m);
1379 	/* overwrite characters at end of string with NULs */
1380 	memset(cur_line + cur_pos + m, NUL, save_pos - cur_pos);
1381 
1382 	/* update display and line length */
1383 	max_pos = cur_pos + m;
1384 	fix_line();
1385     }
1386     RESUMEOUTPUT;
1387 }
1388 
1389 /* copy line to cur_line, draw it and set cur_pos and max_pos */
1390 static void
copy_line(char * line)1391 copy_line(char *line)
1392 {
1393     while (strlen(line) + 1 > line_len) {
1394 	extend_cur_line();
1395     }
1396     strcpy(cur_line, line);
1397     user_puts(cur_line);
1398     cur_pos = max_pos = strlen(cur_line);
1399 }
1400 
1401 #if !defined(MSDOS) && !defined(_WIN32)
1402 /* Convert ANSI arrow keys to control characters */
1403 static int
ansi_getc()1404 ansi_getc()
1405 {
1406     int c;
1407 
1408 #ifdef USE_MOUSE
1409     /* EAM June 2020 why only interactive?
1410      * if (term && term->waitforinput && interactive)
1411      */
1412     if (term && term->waitforinput)
1413 	c = term->waitforinput(0);
1414     else
1415 #endif
1416     c = getc(stdin);
1417 
1418     if (c == 033) {
1419 	c = getc(stdin);	/* check for CSI */
1420 	if (c == '[') {
1421 	    c = getc(stdin);	/* get command character */
1422 	    switch (c) {
1423 	    case 'D':		/* left arrow key */
1424 		c = 002;
1425 		break;
1426 	    case 'C':		/* right arrow key */
1427 		c = 006;
1428 		break;
1429 	    case 'A':		/* up arrow key */
1430 		c = 020;
1431 		break;
1432 	    case 'B':		/* down arrow key */
1433 		c = 016;
1434 		break;
1435 	    case 'F':		/* end key */
1436 		c = 005;
1437 		break;
1438 	    case 'H':		/* home key */
1439 		c = 001;
1440 		break;
1441 	    case 'Z':		/* shift-tab key */
1442 		c = 034;	/* FS: non-standard! */
1443 		break;
1444 	    case '3':		/* DEL can be <esc>[3~ */
1445 		getc(stdin);	/* eat the ~ */
1446 		c = 023;	/* DC3 ^S NB: non-standard!! */
1447 	    }
1448 	}
1449     }
1450     return c;
1451 }
1452 #endif
1453 
1454 #if defined(MSDOS) || defined(_WIN32) || defined(OS2)
1455 
1456 #ifdef WGP_CONSOLE
1457 static int
win_getch()1458 win_getch()
1459 {
1460     if (term && term->waitforinput)
1461 	return term->waitforinput(0);
1462     else
1463 	return ConsoleGetch();
1464 }
1465 #else
1466 
1467 /* Convert Arrow keystrokes to Control characters: */
1468 static int
msdos_getch()1469 msdos_getch()
1470 {
1471     int c;
1472 
1473 #ifdef DJGPP
1474     /* no need to handle mouse input here: it's done in term->text() */
1475     int ch = getkey();
1476     c = (ch & 0xff00) ? 0 : ch & 0xff;
1477 #elif defined (OS2)
1478     c = getc(stdin);
1479 #else /* not OS2, not DJGPP*/
1480 # if defined (USE_MOUSE)
1481     if (term && term->waitforinput && interactive)
1482 	c = term->waitforinput(0);
1483     else
1484 # endif /* not USE_MOUSE */
1485     c = getch();
1486 #endif /* not DJGPP, not OS2 */
1487 
1488     if (c == 0) {
1489 #ifdef DJGPP
1490 	c = ch & 0xff;
1491 #elif defined(OS2)
1492 	c = getc(stdin);
1493 #else /* not OS2, not DJGPP */
1494 # if defined (USE_MOUSE)
1495     if (term && term->waitforinput && interactive)
1496 	c = term->waitforinput(0);
1497     else
1498 # endif /* not USE_MOUSE */
1499 	c = getch();		/* Get the extended code. */
1500 #endif /* not DJGPP, not OS2 */
1501 
1502 	switch (c) {
1503 	case 75:		/* Left Arrow. */
1504 	    c = 002;
1505 	    break;
1506 	case 77:		/* Right Arrow. */
1507 	    c = 006;
1508 	    break;
1509 	case 72:		/* Up Arrow. */
1510 	    c = 020;
1511 	    break;
1512 	case 80:		/* Down Arrow. */
1513 	    c = 016;
1514 	    break;
1515 	case 115:		/* Ctl Left Arrow. */
1516 	case 71:		/* Home */
1517 	    c = 001;
1518 	    break;
1519 	case 116:		/* Ctl Right Arrow. */
1520 	case 79:		/* End */
1521 	    c = 005;
1522 	    break;
1523 	case 83:		/* Delete */
1524 	    c = 0177;
1525 	    break;
1526 	case 15:		/* BackTab / Shift-Tab */
1527 	    c = 034;	/* FS: remap to non-standard code for tab-completion */
1528 	    break;
1529 	default:
1530 	    c = 0;
1531 	    break;
1532 	}
1533     } else if (c == 033) {	/* ESC */
1534 	c = 025;
1535     }
1536     return c;
1537 }
1538 #endif /* WGP_CONSOLE */
1539 #endif /* MSDOS || _WIN32 || OS2 */
1540 
1541 #ifdef OS2
1542 /* We need to call different procedures, dependent on the
1543    session type: VIO/window or an (XFree86) xterm */
1544 static int
os2_getch()1545 os2_getch() {
1546   static int IsXterm = 0;
1547   static int init = 0;
1548 
1549   if (!init) {
1550      if (getenv("WINDOWID")) {
1551         IsXterm = 1;
1552      }
1553      init = 1;
1554   }
1555   if (IsXterm) {
1556      return ansi_getc();
1557   } else {
1558      return msdos_getch();
1559   }
1560 }
1561 #endif /* OS2 */
1562 
1563 
1564   /* set termio so we can do our own input processing */
1565 static void
set_termio()1566 set_termio()
1567 {
1568 #if !defined(MSDOS) && !defined(_WIN32)
1569 /* set termio so we can do our own input processing */
1570 /* and save the old terminal modes so we can reset them later */
1571     if (term_set == 0) {
1572 	/*
1573 	 * Get terminal modes.
1574 	 */
1575 #  ifdef SGTTY
1576 	ioctl(0, TIOCGETP, &orig_termio);
1577 #  else				/* not SGTTY */
1578 #   ifdef TERMIOS
1579 #    ifdef TCGETS
1580 	ioctl(0, TCGETS, &orig_termio);
1581 #    else			/* not TCGETS */
1582 	tcgetattr(0, &orig_termio);
1583 #    endif			/* not TCGETS */
1584 #   else			/* not TERMIOS */
1585 	ioctl(0, TCGETA, &orig_termio);
1586 #   endif			/* TERMIOS */
1587 #  endif			/* not SGTTY */
1588 
1589 	/*
1590 	 * Save terminal modes
1591 	 */
1592 	rl_termio = orig_termio;
1593 
1594 	/*
1595 	 * Set the modes to the way we want them
1596 	 *  and save our input special characters
1597 	 */
1598 #  ifdef SGTTY
1599 	rl_termio.sg_flags |= CBREAK;
1600 	rl_termio.sg_flags &= ~(ECHO | XTABS);
1601 	ioctl(0, TIOCSETN, &rl_termio);
1602 
1603 	ioctl(0, TIOCGETC, &s_tchars);
1604 	term_chars[VERASE] = orig_termio.sg_erase;
1605 	term_chars[VEOF] = s_tchars.t_eofc;
1606 	term_chars[VKILL] = orig_termio.sg_kill;
1607 #   ifdef TIOCGLTC
1608 	ioctl(0, TIOCGLTC, &s_ltchars);
1609 	term_chars[VWERASE] = s_ltchars.t_werasc;
1610 	term_chars[VREPRINT] = s_ltchars.t_rprntc;
1611 	term_chars[VSUSP] = s_ltchars.t_suspc;
1612 
1613 	/* disable suspending process on ^Z */
1614 	s_ltchars.t_suspc = 0;
1615 	ioctl(0, TIOCSLTC, &s_ltchars);
1616 #   endif			/* TIOCGLTC */
1617 #  else				/* not SGTTY */
1618 	rl_termio.c_iflag &= ~(BRKINT | PARMRK | INPCK | IUCLC | IXON | IXOFF);
1619 	rl_termio.c_iflag |= (IGNBRK | IGNPAR);
1620 
1621 	/* rl_termio.c_oflag &= ~(ONOCR); Costas Sphocleous Irvine,CA */
1622 
1623 	rl_termio.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | NOFLSH);
1624 #   ifdef OS2
1625 	/* for emx: remove default terminal processing */
1626 	rl_termio.c_lflag &= ~(IDEFAULT);
1627 #   endif			/* OS2 */
1628 	rl_termio.c_lflag |= (ISIG);
1629 	rl_termio.c_cc[VMIN] = 1;
1630 	rl_termio.c_cc[VTIME] = 0;
1631 
1632 #   ifndef VWERASE
1633 #    define VWERASE 3
1634 #   endif			/* VWERASE */
1635 	term_chars[VERASE] = orig_termio.c_cc[VERASE];
1636 	term_chars[VEOF] = orig_termio.c_cc[VEOF];
1637 	term_chars[VKILL] = orig_termio.c_cc[VKILL];
1638 #   ifdef TERMIOS
1639 	term_chars[VWERASE] = orig_termio.c_cc[VWERASE];
1640 #    ifdef VREPRINT
1641 	term_chars[VREPRINT] = orig_termio.c_cc[VREPRINT];
1642 #    else			/* not VREPRINT */
1643 #     ifdef VRPRNT
1644 	term_chars[VRPRNT] = orig_termio.c_cc[VRPRNT];
1645 #     endif			/* VRPRNT */
1646 #    endif			/* not VREPRINT */
1647 	term_chars[VSUSP] = orig_termio.c_cc[VSUSP];
1648 
1649 	/* disable suspending process on ^Z */
1650 	rl_termio.c_cc[VSUSP] = 0;
1651 #   endif			/* TERMIOS */
1652 #  endif			/* not SGTTY */
1653 
1654 	/*
1655 	 * Set the new terminal modes.
1656 	 */
1657 #  ifdef SGTTY
1658 	ioctl(0, TIOCSLTC, &s_ltchars);
1659 #  else				/* not SGTTY */
1660 #   ifdef TERMIOS
1661 #    ifdef TCSETSW
1662 	ioctl(0, TCSETSW, &rl_termio);
1663 #    else			/* not TCSETSW */
1664 	tcsetattr(0, TCSADRAIN, &rl_termio);
1665 #    endif			/* not TCSETSW */
1666 #   else			/* not TERMIOS */
1667 	ioctl(0, TCSETAW, &rl_termio);
1668 #   endif			/* not TERMIOS */
1669 #  endif			/* not SGTTY */
1670 	term_set = 1;
1671     }
1672 #endif /* not MSDOS && not _WIN32 */
1673 }
1674 
1675 static void
reset_termio()1676 reset_termio()
1677 {
1678 #if !defined(MSDOS) && !defined(_WIN32)
1679 /* reset saved terminal modes */
1680     if (term_set == 1) {
1681 #  ifdef SGTTY
1682 	ioctl(0, TIOCSETN, &orig_termio);
1683 #   ifdef TIOCGLTC
1684 	/* enable suspending process on ^Z */
1685 	s_ltchars.t_suspc = term_chars[VSUSP];
1686 	ioctl(0, TIOCSLTC, &s_ltchars);
1687 #   endif			/* TIOCGLTC */
1688 #  else				/* not SGTTY */
1689 #   ifdef TERMIOS
1690 #    ifdef TCSETSW
1691 	ioctl(0, TCSETSW, &orig_termio);
1692 #    else			/* not TCSETSW */
1693 	tcsetattr(0, TCSADRAIN, &orig_termio);
1694 #    endif			/* not TCSETSW */
1695 #   else			/* not TERMIOS */
1696 	ioctl(0, TCSETAW, &orig_termio);
1697 #   endif			/* TERMIOS */
1698 #  endif			/* not SGTTY */
1699 	term_set = 0;
1700     }
1701 #endif /* not MSDOS && not _WIN32 */
1702 }
1703 
1704 #endif /* READLINE */
1705