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