Dummy commit. Get CVS off the branch.
[deliverable/binutils-gdb.git] / readline / examples / rlfe.c
1 /* A front-end using readline to "cook" input lines for Kawa.
2 *
3 * Copyright (C) 1999 Per Bothner
4 *
5 * This front-end program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as published
7 * by the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * Some code from Johnson & Troan: "Linux Application Development"
11 * (Addison-Wesley, 1998) was used directly or for inspiration.
12 */
13
14 /* PROBLEMS/TODO:
15 *
16 * Only tested under Linux; needs to be ported.
17 *
18 * When running mc -c under the Linux console, mc does not recognize
19 * mouse clicks, which mc does when not running under fep.
20 *
21 * Pasting selected text containing tabs is like hitting the tab character,
22 * which invokes readline completion. We don't want this. I don't know
23 * if this is fixable without integrating fep into a terminal emulator.
24 *
25 * Echo suppression is a kludge, but can only be avoided with better kernel
26 * support: We need a tty mode to disable "real" echoing, while still
27 * letting the inferior think its tty driver to doing echoing.
28 * Stevens's book claims SCR$ and BSD4.3+ have TIOCREMOTE.
29 *
30 * The latest readline may have some hooks we can use to avoid having
31 * to back up the prompt.
32 *
33 * Desirable readline feature: When in cooked no-echo mode (e.g. password),
34 * echo characters are they are types with '*', but remove them when done.
35 *
36 * A synchronous output while we're editing an input line should be
37 * inserted in the output view *before* the input line, so that the
38 * lines being edited (with the prompt) float at the end of the input.
39 *
40 * A "page mode" option to emulate more/less behavior: At each page of
41 * output, pause for a user command. This required parsing the output
42 * to keep track of line lengths. It also requires remembering the
43 * output, if we want an option to scroll back, which suggests that
44 * this should be integrated with a terminal emulator like xterm.
45 */
46
47 #ifdef HAVE_CONFIG_H
48 # include <config.h>
49 #endif
50
51 #include <stdio.h>
52 #include <fcntl.h>
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <signal.h>
58 #include <netdb.h>
59 #include <stdlib.h>
60 #include <errno.h>
61 #include <grp.h>
62 #include <string.h>
63 #include <sys/stat.h>
64 #include <unistd.h>
65 #include <sys/ioctl.h>
66 #include <termios.h>
67
68 #ifdef READLINE_LIBRARY
69 # include "readline.h"
70 # include "history.h"
71 #else
72 # include <readline/readline.h>
73 # include <readline/history.h>
74 #endif
75
76 #ifndef COMMAND
77 #define COMMAND "/bin/sh"
78 #endif
79 #ifndef COMMAND_ARGS
80 #define COMMAND_ARGS COMMAND
81 #endif
82
83 #ifndef HAVE_MEMMOVE
84 # if __GNUC__ > 1
85 # define memmove(d, s, n) __builtin_memcpy(d, s, n)
86 # else
87 # define memmove(d, s, n) memcpy(d, s, n)
88 # endif
89 #else
90 # define memmove(d, s, n) memcpy(d, s, n)
91 #endif
92
93 #define APPLICATION_NAME "Fep"
94
95 static int in_from_inferior_fd;
96 static int out_to_inferior_fd;
97
98 /* Unfortunately, we cannot safely display echo from the inferior process.
99 The reason is that the echo bit in the pty is "owned" by the inferior,
100 and if we try to turn it off, we could confuse the inferior.
101 Thus, when echoing, we get echo twice: First readline echoes while
102 we're actually editing. Then we send the line to the inferior, and the
103 terminal driver send back an extra echo.
104 The work-around is to remember the input lines, and when we see that
105 line come back, we supress the output.
106 A better solution (supposedly available on SVR4) would be a smarter
107 terminal driver, with more flags ... */
108 #define ECHO_SUPPRESS_MAX 1024
109 char echo_suppress_buffer[ECHO_SUPPRESS_MAX];
110 int echo_suppress_start = 0;
111 int echo_suppress_limit = 0;
112
113 #define DEBUG
114
115 #ifdef DEBUG
116 FILE *logfile = NULL;
117 #define DPRINT0(FMT) (fprintf(logfile, FMT), fflush(logfile))
118 #define DPRINT1(FMT, V1) (fprintf(logfile, FMT, V1), fflush(logfile))
119 #define DPRINT2(FMT, V1, V2) (fprintf(logfile, FMT, V1, V2), fflush(logfile))
120 #else
121 #define DPRINT0(FMT) /* Do nothing */
122 #define DPRINT1(FMT, V1) /* Do nothing */
123 #define DPRINT2(FMT, V1, V2) /* Do nothing */
124 #endif
125
126 struct termios orig_term;
127
128 /* Pid of child process. */
129 static pid_t child = -1;
130
131 static void
132 sig_child (int signo)
133 {
134 int status;
135 wait (&status);
136 DPRINT0 ("(Child process died.)\n");
137 tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
138 exit (0);
139 }
140
141 volatile int propagate_sigwinch = 0;
142
143 /* sigwinch_handler
144 * propagate window size changes from input file descriptor to
145 * master side of pty.
146 */
147 void sigwinch_handler(int signal) {
148 propagate_sigwinch = 1;
149 }
150
151 /* get_master_pty() takes a double-indirect character pointer in which
152 * to put a slave name, and returns an integer file descriptor.
153 * If it returns < 0, an error has occurred.
154 * Otherwise, it has returned the master pty file descriptor, and fills
155 * in *name with the name of the corresponding slave pty.
156 * Once the slave pty has been opened, you are responsible to free *name.
157 */
158
159 int get_master_pty(char **name) {
160 int i, j;
161 /* default to returning error */
162 int master = -1;
163
164 /* create a dummy name to fill in */
165 *name = strdup("/dev/ptyXX");
166
167 /* search for an unused pty */
168 for (i=0; i<16 && master <= 0; i++) {
169 for (j=0; j<16 && master <= 0; j++) {
170 (*name)[5] = 'p';
171 (*name)[8] = "pqrstuvwxyzPQRST"[i];
172 (*name)[9] = "0123456789abcdef"[j];
173 /* open the master pty */
174 if ((master = open(*name, O_RDWR)) < 0) {
175 if (errno == ENOENT) {
176 /* we are out of pty devices */
177 free (*name);
178 return (master);
179 }
180 }
181 else {
182 /* By substituting a letter, we change the master pty
183 * name into the slave pty name.
184 */
185 (*name)[5] = 't';
186 if (access(*name, R_OK|W_OK) != 0)
187 {
188 close(master);
189 master = -1;
190 }
191 }
192 }
193 }
194 if ((master < 0) && (i == 16) && (j == 16)) {
195 /* must have tried every pty unsuccessfully */
196 free (*name);
197 return (master);
198 }
199
200 (*name)[5] = 't';
201
202 return (master);
203 }
204
205 /* get_slave_pty() returns an integer file descriptor.
206 * If it returns < 0, an error has occurred.
207 * Otherwise, it has returned the slave file descriptor.
208 */
209
210 int get_slave_pty(char *name) {
211 struct group *gptr;
212 gid_t gid;
213 int slave = -1;
214
215 /* chown/chmod the corresponding pty, if possible.
216 * This will only work if the process has root permissions.
217 * Alternatively, write and exec a small setuid program that
218 * does just this.
219 */
220 if ((gptr = getgrnam("tty")) != 0) {
221 gid = gptr->gr_gid;
222 } else {
223 /* if the tty group does not exist, don't change the
224 * group on the slave pty, only the owner
225 */
226 gid = -1;
227 }
228
229 /* Note that we do not check for errors here. If this is code
230 * where these actions are critical, check for errors!
231 */
232 chown(name, getuid(), gid);
233 /* This code only makes the slave read/writeable for the user.
234 * If this is for an interactive shell that will want to
235 * receive "write" and "wall" messages, OR S_IWGRP into the
236 * second argument below.
237 */
238 chmod(name, S_IRUSR|S_IWUSR);
239
240 /* open the corresponding slave pty */
241 slave = open(name, O_RDWR);
242 return (slave);
243 }
244
245 /* Certain special characters, such as ctrl/C, we want to pass directly
246 to the inferior, rather than letting readline handle them. */
247
248 static char special_chars[20];
249 static int special_chars_count;
250
251 static void
252 add_special_char(int ch)
253 {
254 if (ch != 0)
255 special_chars[special_chars_count++] = ch;
256 }
257
258 static int eof_char;
259
260 static int
261 is_special_char(int ch)
262 {
263 int i;
264 #if 0
265 if (ch == eof_char && rl_point == rl_end)
266 return 1;
267 #endif
268 for (i = special_chars_count; --i >= 0; )
269 if (special_chars[i] == ch)
270 return 1;
271 return 0;
272 }
273
274 static char buf[1024];
275 /* buf[0 .. buf_count-1] is the what has been emitted on the current line.
276 It is used as the readline prompt. */
277 static int buf_count = 0;
278
279 int num_keys = 0;
280
281 static void
282 null_prep_terminal (int meta)
283 {
284 }
285
286 static void
287 null_deprep_terminal ()
288 {
289 }
290
291 char pending_special_char;
292
293 static void
294 line_handler (char *line)
295 {
296 if (line == NULL)
297 {
298 char buf[1];
299 DPRINT0("saw eof!\n");
300 buf[0] = '\004'; /* ctrl/d */
301 write (out_to_inferior_fd, buf, 1);
302 }
303 else
304 {
305 static char enter[] = "\r";
306 /* Send line to inferior: */
307 int length = strlen (line);
308 if (length > ECHO_SUPPRESS_MAX-2)
309 {
310 echo_suppress_start = 0;
311 echo_suppress_limit = 0;
312 }
313 else
314 {
315 if (echo_suppress_limit + length > ECHO_SUPPRESS_MAX - 2)
316 {
317 if (echo_suppress_limit - echo_suppress_start + length
318 <= ECHO_SUPPRESS_MAX - 2)
319 {
320 memmove (echo_suppress_buffer,
321 echo_suppress_buffer + echo_suppress_start,
322 echo_suppress_limit - echo_suppress_start);
323 echo_suppress_limit -= echo_suppress_start;
324 echo_suppress_start = 0;
325 }
326 else
327 {
328 echo_suppress_limit = 0;
329 }
330 echo_suppress_start = 0;
331 }
332 memcpy (echo_suppress_buffer + echo_suppress_limit,
333 line, length);
334 echo_suppress_limit += length;
335 echo_suppress_buffer[echo_suppress_limit++] = '\r';
336 echo_suppress_buffer[echo_suppress_limit++] = '\n';
337 }
338 write (out_to_inferior_fd, line, length);
339 if (pending_special_char == 0)
340 {
341 write (out_to_inferior_fd, enter, sizeof(enter)-1);
342 if (*line)
343 add_history (line);
344 }
345 free (line);
346 }
347 rl_callback_handler_remove ();
348 buf_count = 0;
349 num_keys = 0;
350 if (pending_special_char != 0)
351 {
352 write (out_to_inferior_fd, &pending_special_char, 1);
353 pending_special_char = 0;
354 }
355 }
356
357 /* Value of rl_getc_function.
358 Use this because readline should read from stdin, not rl_instream,
359 points to the pty (so readline has monitor its terminal modes). */
360
361 int
362 my_rl_getc (FILE *dummy)
363 {
364 int ch = rl_getc (stdin);
365 if (is_special_char (ch))
366 {
367 pending_special_char = ch;
368 return '\r';
369 }
370 return ch;
371 }
372
373 int
374 main(int argc, char** argv)
375 {
376 char *path;
377 int i;
378 int master;
379 char *name;
380 int in_from_tty_fd;
381 struct sigaction act;
382 struct winsize ws;
383 struct termios t;
384 int maxfd;
385 fd_set in_set;
386 static char empty_string[1] = "";
387 char *prompt = empty_string;
388 int ioctl_err = 0;
389
390 #ifdef DEBUG
391 logfile = fopen("LOG", "w");
392 #endif
393
394 rl_readline_name = APPLICATION_NAME;
395
396 if ((master = get_master_pty(&name)) < 0)
397 {
398 perror("ptypair: could not open master pty");
399 exit(1);
400 }
401
402 DPRINT1("pty name: '%s'\n", name);
403
404 /* set up SIGWINCH handler */
405 act.sa_handler = sigwinch_handler;
406 sigemptyset(&(act.sa_mask));
407 act.sa_flags = 0;
408 if (sigaction(SIGWINCH, &act, NULL) < 0)
409 {
410 perror("ptypair: could not handle SIGWINCH ");
411 exit(1);
412 }
413
414 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
415 {
416 perror("ptypair: could not get window size");
417 exit(1);
418 }
419
420 if ((child = fork()) < 0)
421 {
422 perror("cannot fork");
423 exit(1);
424 }
425
426 if (child == 0)
427 {
428 int slave; /* file descriptor for slave pty */
429
430 /* We are in the child process */
431 close(master);
432
433 #ifdef TIOCSCTTY
434 if ((slave = get_slave_pty(name)) < 0)
435 {
436 perror("ptypair: could not open slave pty");
437 exit(1);
438 }
439 free(name);
440 #endif
441
442 /* We need to make this process a session group leader, because
443 * it is on a new PTY, and things like job control simply will
444 * not work correctly unless there is a session group leader
445 * and process group leader (which a session group leader
446 * automatically is). This also disassociates us from our old
447 * controlling tty.
448 */
449 if (setsid() < 0)
450 {
451 perror("could not set session leader");
452 }
453
454 /* Tie us to our new controlling tty. */
455 #ifdef TIOCSCTTY
456 if (ioctl(slave, TIOCSCTTY, NULL))
457 {
458 perror("could not set new controlling tty");
459 }
460 #else
461 if ((slave = get_slave_pty(name)) < 0)
462 {
463 perror("ptypair: could not open slave pty");
464 exit(1);
465 }
466 free(name);
467 #endif
468
469 /* make slave pty be standard in, out, and error */
470 dup2(slave, STDIN_FILENO);
471 dup2(slave, STDOUT_FILENO);
472 dup2(slave, STDERR_FILENO);
473
474 /* at this point the slave pty should be standard input */
475 if (slave > 2)
476 {
477 close(slave);
478 }
479
480 /* Try to restore window size; failure isn't critical */
481 if (ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws) < 0)
482 {
483 perror("could not restore window size");
484 }
485
486 /* now start the shell */
487 {
488 static char* command_args[] = { COMMAND_ARGS, NULL };
489 if (argc <= 1)
490 execvp(COMMAND, command_args);
491 else
492 execvp(argv[1], &argv[1]);
493 }
494
495 /* should never be reached */
496 exit(1);
497 }
498
499 /* parent */
500 signal (SIGCHLD, sig_child);
501 free(name);
502
503 /* Note that we only set termios settings for standard input;
504 * the master side of a pty is NOT a tty.
505 */
506 tcgetattr(STDIN_FILENO, &orig_term);
507
508 t = orig_term;
509 eof_char = t.c_cc[VEOF];
510 /* add_special_char(t.c_cc[VEOF]);*/
511 add_special_char(t.c_cc[VINTR]);
512 add_special_char(t.c_cc[VQUIT]);
513 add_special_char(t.c_cc[VSUSP]);
514 #if defined (VDISCARD)
515 add_special_char(t.c_cc[VDISCARD]);
516 #endif
517
518 #if 0
519 t.c_lflag |= (ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \
520 ECHOK | ECHOKE | ECHONL | ECHOPRT );
521 #else
522 t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \
523 ECHOK | ECHOKE | ECHONL | ECHOPRT );
524 #endif
525 t.c_iflag |= IGNBRK;
526 t.c_cc[VMIN] = 1;
527 t.c_cc[VTIME] = 0;
528 tcsetattr(STDIN_FILENO, TCSANOW, &t);
529 in_from_inferior_fd = master;
530 out_to_inferior_fd = master;
531 rl_instream = fdopen (master, "r");
532 rl_getc_function = my_rl_getc;
533
534 rl_prep_term_function = null_prep_terminal;
535 rl_deprep_term_function = null_deprep_terminal;
536 rl_callback_handler_install (prompt, line_handler);
537
538 in_from_tty_fd = STDIN_FILENO;
539 FD_ZERO (&in_set);
540 maxfd = in_from_inferior_fd > in_from_tty_fd ? in_from_inferior_fd
541 : in_from_tty_fd;
542 for (;;)
543 {
544 int num;
545 FD_SET (in_from_inferior_fd, &in_set);
546 FD_SET (in_from_tty_fd, &in_set);
547
548 num = select(maxfd+1, &in_set, NULL, NULL, NULL);
549
550 if (propagate_sigwinch)
551 {
552 struct winsize ws;
553 if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
554 {
555 ioctl (master, TIOCSWINSZ, &ws);
556 }
557 propagate_sigwinch = 0;
558 continue;
559 }
560
561 if (num <= 0)
562 {
563 perror ("select");
564 exit (-1);
565 }
566 if (FD_ISSET (in_from_tty_fd, &in_set))
567 {
568 extern int readline_echoing_p;
569 struct termios term_master;
570 int do_canon = 1;
571 int ioctl_ret;
572
573 DPRINT1("[tty avail num_keys:%d]\n", num_keys);
574
575 /* If we can't get tty modes for the master side of the pty, we
576 can't handle non-canonical-mode programs. Always assume the
577 master is in canonical echo mode if we can't tell. */
578 ioctl_ret = tcgetattr(master, &term_master);
579
580 if (ioctl_ret >= 0)
581 {
582 DPRINT2 ("echo:%d, canon:%d\n",
583 (term_master.c_lflag & ECHO) != 0,
584 (term_master.c_lflag & ICANON) != 0);
585 do_canon = (term_master.c_lflag & ICANON) != 0;
586 readline_echoing_p = (term_master.c_lflag & ECHO) != 0;
587 }
588 else
589 {
590 if (ioctl_err == 0)
591 DPRINT1("tcgetattr on master fd failed: errno = %d\n", errno);
592 ioctl_err = 1;
593 }
594
595 if (do_canon == 0 && num_keys == 0)
596 {
597 char ch[10];
598 int count = read (STDIN_FILENO, ch, sizeof(ch));
599 write (out_to_inferior_fd, ch, count);
600 }
601 else
602 {
603 if (num_keys == 0)
604 {
605 int i;
606 /* Re-install callback handler for new prompt. */
607 if (prompt != empty_string)
608 free (prompt);
609 prompt = malloc (buf_count + 1);
610 if (prompt == NULL)
611 prompt = empty_string;
612 else
613 {
614 memcpy (prompt, buf, buf_count);
615 prompt[buf_count] = '\0';
616 DPRINT1("New prompt '%s'\n", prompt);
617 #if 0 /* ifdef HAVE_RL_ALREADY_PROMPTED -- doesn't work */
618 rl_already_prompted = buf_count > 0;
619 #else
620 if (buf_count > 0)
621 write (1, "\r", 1);
622 #endif
623 }
624 rl_callback_handler_install (prompt, line_handler);
625 }
626 num_keys++;
627 rl_callback_read_char ();
628 }
629 }
630 else /* input from inferior. */
631 {
632 int i;
633 int count;
634 int old_count;
635 if (buf_count > (sizeof(buf) >> 2))
636 buf_count = 0;
637 count = read (in_from_inferior_fd, buf+buf_count,
638 sizeof(buf) - buf_count);
639 if (count <= 0)
640 {
641 DPRINT0 ("(Connection closed by foreign host.)\n");
642 tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
643 exit (0);
644 }
645 old_count = buf_count;
646
647 /* Look for any pending echo that we need to suppress. */
648 while (echo_suppress_start < echo_suppress_limit
649 && count > 0
650 && buf[buf_count] == echo_suppress_buffer[echo_suppress_start])
651 {
652 count--;
653 buf_count++;
654 echo_suppress_start++;
655 }
656
657 /* Write to the terminal anything that was not suppressed. */
658 if (count > 0)
659 write (1, buf + buf_count, count);
660
661 /* Finally, look for a prompt candidate.
662 * When we get around to going input (from the keyboard),
663 * we will consider the prompt to be anything since the last
664 * line terminator. So we need to save that text in the
665 * initial part of buf. However, anything before the
666 * most recent end-of-line is not interesting. */
667 buf_count += count;
668 #if 1
669 for (i = buf_count; --i >= old_count; )
670 #else
671 for (i = buf_count - 1; i-- >= buf_count - count; )
672 #endif
673 {
674 if (buf[i] == '\n' || buf[i] == '\r')
675 {
676 i++;
677 memmove (buf, buf+i, buf_count - i);
678 buf_count -= i;
679 break;
680 }
681 }
682 DPRINT2("-> i: %d, buf_count: %d\n", i, buf_count);
683 }
684 }
685 }
This page took 0.044536 seconds and 4 git commands to generate.