1157184Sache/* A front-end using readline to "cook" input lines. 2157184Sache * 3157184Sache * Copyright (C) 2004, 1999 Per Bothner 4157184Sache * 5157184Sache * This front-end program is free software; you can redistribute it and/or 6157184Sache * modify it under the terms of the GNU General Public License as published 7157184Sache * by the Free Software Foundation; either version 2, or (at your option) 8157184Sache * any later version. 9157184Sache * 10157184Sache * Some code from Johnson & Troan: "Linux Application Development" 11157184Sache * (Addison-Wesley, 1998) was used directly or for inspiration. 12157184Sache * 13157184Sache * 2003-11-07 Wolfgang Taeuber <wolfgang_taeuber@agilent.com> 14157184Sache * Specify a history file and the size of the history file with command 15157184Sache * line options; use EDITOR/VISUAL to set vi/emacs preference. 16157184Sache */ 17157184Sache 18157184Sache/* PROBLEMS/TODO: 19157184Sache * 20157184Sache * Only tested under GNU/Linux and Mac OS 10.x; needs to be ported. 21157184Sache * 22157184Sache * Switching between line-editing-mode vs raw-char-mode depending on 23157184Sache * what tcgetattr returns is inherently not robust, plus it doesn't 24157184Sache * work when ssh/telnetting in. A better solution is possible if the 25157184Sache * tty system can send in-line escape sequences indicating the current 26157184Sache * mode, echo'd input, etc. That would also allow a user preference 27157184Sache * to set different colors for prompt, input, stdout, and stderr. 28157184Sache * 29157184Sache * When running mc -c under the Linux console, mc does not recognize 30157184Sache * mouse clicks, which mc does when not running under rlfe. 31157184Sache * 32157184Sache * Pasting selected text containing tabs is like hitting the tab character, 33157184Sache * which invokes readline completion. We don't want this. I don't know 34157184Sache * if this is fixable without integrating rlfe into a terminal emulator. 35157184Sache * 36157184Sache * Echo suppression is a kludge, but can only be avoided with better kernel 37157184Sache * support: We need a tty mode to disable "real" echoing, while still 38157184Sache * letting the inferior think its tty driver to doing echoing. 39157184Sache * Stevens's book claims SCR$ and BSD4.3+ have TIOCREMOTE. 40157184Sache * 41157184Sache * The latest readline may have some hooks we can use to avoid having 42157184Sache * to back up the prompt. (See HAVE_ALREADY_PROMPTED.) 43157184Sache * 44157184Sache * Desirable readline feature: When in cooked no-echo mode (e.g. password), 45157184Sache * echo characters are they are types with '*', but remove them when done. 46157184Sache * 47157184Sache * Asynchronous output while we're editing an input line should be 48157184Sache * inserted in the output view *before* the input line, so that the 49157184Sache * lines being edited (with the prompt) float at the end of the input. 50157184Sache * 51157184Sache * A "page mode" option to emulate more/less behavior: At each page of 52157184Sache * output, pause for a user command. This required parsing the output 53157184Sache * to keep track of line lengths. It also requires remembering the 54157184Sache * output, if we want an option to scroll back, which suggests that 55157184Sache * this should be integrated with a terminal emulator like xterm. 56157184Sache */ 57157184Sache 58157184Sache#include <stdio.h> 59157184Sache#include <fcntl.h> 60157184Sache#include <sys/types.h> 61157184Sache#include <sys/socket.h> 62157184Sache#include <netinet/in.h> 63157184Sache#include <arpa/inet.h> 64157184Sache#include <signal.h> 65157184Sache#include <netdb.h> 66157184Sache#include <stdlib.h> 67157184Sache#include <errno.h> 68157184Sache#include <grp.h> 69157184Sache#include <string.h> 70157184Sache#include <sys/stat.h> 71157184Sache#include <unistd.h> 72157184Sache#include <sys/ioctl.h> 73157184Sache#include <termios.h> 74157184Sache 75157184Sache#include "config.h" 76157184Sache 77157184Sache#ifdef READLINE_LIBRARY 78157184Sache# include "readline.h" 79157184Sache# include "history.h" 80157184Sache#else 81157184Sache# include <readline/readline.h> 82157184Sache# include <readline/history.h> 83157184Sache#endif 84157184Sache 85157184Sache#ifndef COMMAND 86157184Sache#define COMMAND "/bin/bash" 87157184Sache#endif 88157184Sache#ifndef COMMAND_ARGS 89157184Sache#define COMMAND_ARGS COMMAND 90157184Sache#endif 91157184Sache 92157184Sache#ifndef ALT_COMMAND 93157184Sache#define ALT_COMMAND "/bin/sh" 94157184Sache#endif 95157184Sache#ifndef ALT_COMMAND_ARGS 96157184Sache#define ALT_COMMAND_ARGS ALT_COMMAND 97157184Sache#endif 98157184Sache 99157184Sache#ifndef HAVE_MEMMOVE 100157184Sache# if __GNUC__ > 1 101157184Sache# define memmove(d, s, n) __builtin_memcpy(d, s, n) 102157184Sache# else 103157184Sache# define memmove(d, s, n) memcpy(d, s, n) 104157184Sache# endif 105157184Sache#else 106157184Sache# define memmove(d, s, n) memcpy(d, s, n) 107157184Sache#endif 108157184Sache 109157184Sache#define APPLICATION_NAME "rlfe" 110157184Sache 111157184Sachestatic int in_from_inferior_fd; 112157184Sachestatic int out_to_inferior_fd; 113157184Sachestatic void set_edit_mode (); 114157184Sachestatic void usage_exit (); 115157184Sachestatic char *hist_file = 0; 116157184Sachestatic int hist_size = 0; 117157184Sache 118157184Sache/* Unfortunately, we cannot safely display echo from the inferior process. 119157184Sache The reason is that the echo bit in the pty is "owned" by the inferior, 120157184Sache and if we try to turn it off, we could confuse the inferior. 121157184Sache Thus, when echoing, we get echo twice: First readline echoes while 122157184Sache we're actually editing. Then we send the line to the inferior, and the 123157184Sache terminal driver send back an extra echo. 124157184Sache The work-around is to remember the input lines, and when we see that 125157184Sache line come back, we supress the output. 126157184Sache A better solution (supposedly available on SVR4) would be a smarter 127157184Sache terminal driver, with more flags ... */ 128157184Sache#define ECHO_SUPPRESS_MAX 1024 129157184Sachechar echo_suppress_buffer[ECHO_SUPPRESS_MAX]; 130157184Sacheint echo_suppress_start = 0; 131157184Sacheint echo_suppress_limit = 0; 132157184Sache 133157184Sache/*#define DEBUG*/ 134157184Sache 135157184Sache#ifdef DEBUG 136157184SacheFILE *logfile = NULL; 137157184Sache#define DPRINT0(FMT) (fprintf(logfile, FMT), fflush(logfile)) 138157184Sache#define DPRINT1(FMT, V1) (fprintf(logfile, FMT, V1), fflush(logfile)) 139157184Sache#define DPRINT2(FMT, V1, V2) (fprintf(logfile, FMT, V1, V2), fflush(logfile)) 140157184Sache#else 141157184Sache#define DPRINT0(FMT) ((void) 0) /* Do nothing */ 142157184Sache#define DPRINT1(FMT, V1) ((void) 0) /* Do nothing */ 143157184Sache#define DPRINT2(FMT, V1, V2) ((void) 0) /* Do nothing */ 144157184Sache#endif 145157184Sache 146157184Sachestruct termios orig_term; 147157184Sache 148157184Sache/* Pid of child process. */ 149157184Sachestatic pid_t child = -1; 150157184Sache 151157184Sachestatic void 152157184Sachesig_child (int signo) 153157184Sache{ 154157184Sache int status; 155157184Sache wait (&status); 156157184Sache if (hist_file != 0) 157157184Sache { 158157184Sache write_history (hist_file); 159157184Sache if (hist_size) 160157184Sache history_truncate_file (hist_file, hist_size); 161157184Sache } 162157184Sache DPRINT0 ("(Child process died.)\n"); 163157184Sache tcsetattr(STDIN_FILENO, TCSANOW, &orig_term); 164157184Sache exit (0); 165157184Sache} 166157184Sache 167157184Sachevolatile int propagate_sigwinch = 0; 168157184Sache 169157184Sache/* sigwinch_handler 170157184Sache * propagate window size changes from input file descriptor to 171157184Sache * master side of pty. 172157184Sache */ 173157184Sachevoid sigwinch_handler(int signal) { 174157184Sache propagate_sigwinch = 1; 175157184Sache} 176157184Sache 177157184Sache 178157184Sache/* get_slave_pty() returns an integer file descriptor. 179157184Sache * If it returns < 0, an error has occurred. 180157184Sache * Otherwise, it has returned the slave file descriptor. 181157184Sache */ 182157184Sache 183157184Sacheint get_slave_pty(char *name) { 184157184Sache struct group *gptr; 185157184Sache gid_t gid; 186157184Sache int slave = -1; 187157184Sache 188157184Sache /* chown/chmod the corresponding pty, if possible. 189157184Sache * This will only work if the process has root permissions. 190157184Sache * Alternatively, write and exec a small setuid program that 191157184Sache * does just this. 192157184Sache */ 193157184Sache if ((gptr = getgrnam("tty")) != 0) { 194157184Sache gid = gptr->gr_gid; 195157184Sache } else { 196157184Sache /* if the tty group does not exist, don't change the 197157184Sache * group on the slave pty, only the owner 198157184Sache */ 199157184Sache gid = -1; 200157184Sache } 201157184Sache 202157184Sache /* Note that we do not check for errors here. If this is code 203157184Sache * where these actions are critical, check for errors! 204157184Sache */ 205157184Sache chown(name, getuid(), gid); 206157184Sache /* This code only makes the slave read/writeable for the user. 207157184Sache * If this is for an interactive shell that will want to 208157184Sache * receive "write" and "wall" messages, OR S_IWGRP into the 209157184Sache * second argument below. 210157184Sache */ 211157184Sache chmod(name, S_IRUSR|S_IWUSR); 212157184Sache 213157184Sache /* open the corresponding slave pty */ 214157184Sache slave = open(name, O_RDWR); 215157184Sache return (slave); 216157184Sache} 217157184Sache 218157184Sache/* Certain special characters, such as ctrl/C, we want to pass directly 219157184Sache to the inferior, rather than letting readline handle them. */ 220157184Sache 221157184Sachestatic char special_chars[20]; 222157184Sachestatic int special_chars_count; 223157184Sache 224157184Sachestatic void 225157184Sacheadd_special_char(int ch) 226157184Sache{ 227157184Sache if (ch != 0) 228157184Sache special_chars[special_chars_count++] = ch; 229157184Sache} 230157184Sache 231157184Sachestatic int eof_char; 232157184Sache 233157184Sachestatic int 234157184Sacheis_special_char(int ch) 235157184Sache{ 236157184Sache int i; 237157184Sache#if 0 238157184Sache if (ch == eof_char && rl_point == rl_end) 239157184Sache return 1; 240157184Sache#endif 241157184Sache for (i = special_chars_count; --i >= 0; ) 242157184Sache if (special_chars[i] == ch) 243157184Sache return 1; 244157184Sache return 0; 245157184Sache} 246157184Sache 247157184Sachestatic char buf[1024]; 248157184Sache/* buf[0 .. buf_count-1] is the what has been emitted on the current line. 249157184Sache It is used as the readline prompt. */ 250157184Sachestatic int buf_count = 0; 251157184Sache 252157184Sacheint do_emphasize_input = 1; 253157184Sacheint current_emphasize_input; 254157184Sache 255157184Sachechar *start_input_mode = "\033[1m"; 256157184Sachechar *end_input_mode = "\033[0m"; 257157184Sache 258157184Sacheint num_keys = 0; 259157184Sache 260157184Sachestatic void maybe_emphasize_input (int on) 261157184Sache{ 262157184Sache if (on == current_emphasize_input 263157184Sache || (on && ! do_emphasize_input)) 264157184Sache return; 265157184Sache fprintf (rl_outstream, on ? start_input_mode : end_input_mode); 266157184Sache fflush (rl_outstream); 267157184Sache current_emphasize_input = on; 268157184Sache} 269157184Sache 270157184Sachestatic void 271157184Sachenull_prep_terminal (int meta) 272157184Sache{ 273157184Sache} 274157184Sache 275157184Sachestatic void 276157184Sachenull_deprep_terminal () 277157184Sache{ 278157184Sache maybe_emphasize_input (0); 279157184Sache} 280157184Sache 281157184Sachestatic int 282157184Sachepre_input_change_mode (void) 283157184Sache{ 284157184Sache return 0; 285157184Sache} 286157184Sache 287157184Sachechar pending_special_char; 288157184Sache 289157184Sachestatic void 290157184Sacheline_handler (char *line) 291157184Sache{ 292157184Sache if (line == NULL) 293157184Sache { 294157184Sache char buf[1]; 295157184Sache DPRINT0("saw eof!\n"); 296157184Sache buf[0] = '\004'; /* ctrl/d */ 297157184Sache write (out_to_inferior_fd, buf, 1); 298157184Sache } 299157184Sache else 300157184Sache { 301157184Sache static char enter[] = "\r"; 302157184Sache /* Send line to inferior: */ 303157184Sache int length = strlen (line); 304157184Sache if (length > ECHO_SUPPRESS_MAX-2) 305157184Sache { 306157184Sache echo_suppress_start = 0; 307157184Sache echo_suppress_limit = 0; 308157184Sache } 309157184Sache else 310157184Sache { 311157184Sache if (echo_suppress_limit + length > ECHO_SUPPRESS_MAX - 2) 312157184Sache { 313157184Sache if (echo_suppress_limit - echo_suppress_start + length 314157184Sache <= ECHO_SUPPRESS_MAX - 2) 315157184Sache { 316157184Sache memmove (echo_suppress_buffer, 317157184Sache echo_suppress_buffer + echo_suppress_start, 318157184Sache echo_suppress_limit - echo_suppress_start); 319157184Sache echo_suppress_limit -= echo_suppress_start; 320157184Sache echo_suppress_start = 0; 321157184Sache } 322157184Sache else 323157184Sache { 324157184Sache echo_suppress_limit = 0; 325157184Sache } 326157184Sache echo_suppress_start = 0; 327157184Sache } 328157184Sache memcpy (echo_suppress_buffer + echo_suppress_limit, 329157184Sache line, length); 330157184Sache echo_suppress_limit += length; 331157184Sache echo_suppress_buffer[echo_suppress_limit++] = '\r'; 332157184Sache echo_suppress_buffer[echo_suppress_limit++] = '\n'; 333157184Sache } 334157184Sache write (out_to_inferior_fd, line, length); 335157184Sache if (pending_special_char == 0) 336157184Sache { 337157184Sache write (out_to_inferior_fd, enter, sizeof(enter)-1); 338157184Sache if (*line) 339157184Sache add_history (line); 340157184Sache } 341157184Sache free (line); 342157184Sache } 343157184Sache rl_callback_handler_remove (); 344157184Sache buf_count = 0; 345157184Sache num_keys = 0; 346157184Sache if (pending_special_char != 0) 347157184Sache { 348157184Sache write (out_to_inferior_fd, &pending_special_char, 1); 349157184Sache pending_special_char = 0; 350157184Sache } 351157184Sache} 352157184Sache 353157184Sache/* Value of rl_getc_function. 354157184Sache Use this because readline should read from stdin, not rl_instream, 355157184Sache points to the pty (so readline has monitor its terminal modes). */ 356157184Sache 357157184Sacheint 358157184Sachemy_rl_getc (FILE *dummy) 359157184Sache{ 360157184Sache int ch = rl_getc (stdin); 361157184Sache if (is_special_char (ch)) 362157184Sache { 363157184Sache pending_special_char = ch; 364157184Sache return '\r'; 365157184Sache } 366157184Sache return ch; 367157184Sache} 368157184Sache 369157184Sacheint 370157184Sachemain(int argc, char** argv) 371157184Sache{ 372157184Sache char *path; 373157184Sache int i; 374157184Sache int master; 375157184Sache char *name; 376157184Sache int in_from_tty_fd; 377157184Sache struct sigaction act; 378157184Sache struct winsize ws; 379157184Sache struct termios t; 380157184Sache int maxfd; 381157184Sache fd_set in_set; 382157184Sache static char empty_string[1] = ""; 383157184Sache char *prompt = empty_string; 384157184Sache int ioctl_err = 0; 385157184Sache int arg_base = 1; 386157184Sache 387157184Sache#ifdef DEBUG 388157184Sache logfile = fopen("/tmp/rlfe.log", "w"); 389157184Sache#endif 390157184Sache 391157184Sache while (arg_base<argc) 392157184Sache { 393157184Sache if (argv[arg_base][0] != '-') 394157184Sache break; 395157184Sache if (arg_base+1 >= argc ) 396157184Sache usage_exit(); 397157184Sache switch(argv[arg_base][1]) 398157184Sache { 399157184Sache case 'h': 400157184Sache arg_base++; 401157184Sache hist_file = argv[arg_base]; 402157184Sache break; 403157184Sache case 's': 404157184Sache arg_base++; 405157184Sache hist_size = atoi(argv[arg_base]); 406157184Sache if (hist_size<0) 407157184Sache usage_exit(); 408157184Sache break; 409157184Sache default: 410157184Sache usage_exit(); 411157184Sache } 412157184Sache arg_base++; 413157184Sache } 414157184Sache if (hist_file) 415157184Sache read_history (hist_file); 416157184Sache 417157184Sache set_edit_mode (); 418157184Sache 419157184Sache rl_readline_name = APPLICATION_NAME; 420157184Sache 421157184Sache if ((master = OpenPTY (&name)) < 0) 422157184Sache { 423157184Sache perror("ptypair: could not open master pty"); 424157184Sache exit(1); 425157184Sache } 426157184Sache 427157184Sache DPRINT1("pty name: '%s'\n", name); 428157184Sache 429157184Sache /* set up SIGWINCH handler */ 430157184Sache act.sa_handler = sigwinch_handler; 431157184Sache sigemptyset(&(act.sa_mask)); 432157184Sache act.sa_flags = 0; 433157184Sache if (sigaction(SIGWINCH, &act, NULL) < 0) 434157184Sache { 435157184Sache perror("ptypair: could not handle SIGWINCH "); 436157184Sache exit(1); 437157184Sache } 438157184Sache 439157184Sache if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) 440157184Sache { 441157184Sache perror("ptypair: could not get window size"); 442157184Sache exit(1); 443157184Sache } 444157184Sache 445157184Sache if ((child = fork()) < 0) 446157184Sache { 447157184Sache perror("cannot fork"); 448157184Sache exit(1); 449157184Sache } 450157184Sache 451157184Sache if (child == 0) 452157184Sache { 453157184Sache int slave; /* file descriptor for slave pty */ 454157184Sache 455157184Sache /* We are in the child process */ 456157184Sache close(master); 457157184Sache 458157184Sache#ifdef TIOCSCTTY 459157184Sache if ((slave = get_slave_pty(name)) < 0) 460157184Sache { 461157184Sache perror("ptypair: could not open slave pty"); 462157184Sache exit(1); 463157184Sache } 464157184Sache#endif 465157184Sache 466157184Sache /* We need to make this process a session group leader, because 467157184Sache * it is on a new PTY, and things like job control simply will 468157184Sache * not work correctly unless there is a session group leader 469157184Sache * and process group leader (which a session group leader 470157184Sache * automatically is). This also disassociates us from our old 471157184Sache * controlling tty. 472157184Sache */ 473157184Sache if (setsid() < 0) 474157184Sache { 475157184Sache perror("could not set session leader"); 476157184Sache } 477157184Sache 478157184Sache /* Tie us to our new controlling tty. */ 479157184Sache#ifdef TIOCSCTTY 480157184Sache if (ioctl(slave, TIOCSCTTY, NULL)) 481157184Sache { 482157184Sache perror("could not set new controlling tty"); 483157184Sache } 484157184Sache#else 485157184Sache if ((slave = get_slave_pty(name)) < 0) 486157184Sache { 487157184Sache perror("ptypair: could not open slave pty"); 488157184Sache exit(1); 489157184Sache } 490157184Sache#endif 491157184Sache 492157184Sache /* make slave pty be standard in, out, and error */ 493157184Sache dup2(slave, STDIN_FILENO); 494157184Sache dup2(slave, STDOUT_FILENO); 495157184Sache dup2(slave, STDERR_FILENO); 496157184Sache 497157184Sache /* at this point the slave pty should be standard input */ 498157184Sache if (slave > 2) 499157184Sache { 500157184Sache close(slave); 501157184Sache } 502157184Sache 503157184Sache /* Try to restore window size; failure isn't critical */ 504157184Sache if (ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws) < 0) 505157184Sache { 506157184Sache perror("could not restore window size"); 507157184Sache } 508157184Sache 509157184Sache /* now start the shell */ 510157184Sache { 511157184Sache static char* command_args[] = { COMMAND_ARGS, NULL }; 512157184Sache static char* alt_command_args[] = { ALT_COMMAND_ARGS, NULL }; 513157184Sache if (argc <= 1) 514157184Sache { 515157184Sache execvp (COMMAND, command_args); 516157184Sache execvp (ALT_COMMAND, alt_command_args); 517157184Sache } 518157184Sache else 519157184Sache execvp (argv[arg_base], &argv[arg_base]); 520157184Sache } 521157184Sache 522157184Sache /* should never be reached */ 523157184Sache exit(1); 524157184Sache } 525157184Sache 526157184Sache /* parent */ 527157184Sache signal (SIGCHLD, sig_child); 528157184Sache 529157184Sache /* Note that we only set termios settings for standard input; 530157184Sache * the master side of a pty is NOT a tty. 531157184Sache */ 532157184Sache tcgetattr(STDIN_FILENO, &orig_term); 533157184Sache 534157184Sache t = orig_term; 535157184Sache eof_char = t.c_cc[VEOF]; 536157184Sache /* add_special_char(t.c_cc[VEOF]);*/ 537157184Sache add_special_char(t.c_cc[VINTR]); 538157184Sache add_special_char(t.c_cc[VQUIT]); 539157184Sache add_special_char(t.c_cc[VSUSP]); 540157184Sache#if defined (VDISCARD) 541157184Sache add_special_char(t.c_cc[VDISCARD]); 542157184Sache#endif 543157184Sache 544157184Sache t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \ 545157184Sache ECHOK | ECHOKE | ECHONL | ECHOPRT ); 546157184Sache t.c_iflag &= ~ICRNL; 547157184Sache t.c_iflag |= IGNBRK; 548157184Sache t.c_cc[VMIN] = 1; 549157184Sache t.c_cc[VTIME] = 0; 550157184Sache tcsetattr(STDIN_FILENO, TCSANOW, &t); 551157184Sache in_from_inferior_fd = master; 552157184Sache out_to_inferior_fd = master; 553157184Sache rl_instream = fdopen (master, "r"); 554157184Sache rl_getc_function = my_rl_getc; 555157184Sache 556157184Sache rl_prep_term_function = null_prep_terminal; 557157184Sache rl_deprep_term_function = null_deprep_terminal; 558157184Sache rl_pre_input_hook = pre_input_change_mode; 559157184Sache rl_callback_handler_install (prompt, line_handler); 560157184Sache 561157184Sache in_from_tty_fd = STDIN_FILENO; 562157184Sache FD_ZERO (&in_set); 563157184Sache maxfd = in_from_inferior_fd > in_from_tty_fd ? in_from_inferior_fd 564157184Sache : in_from_tty_fd; 565157184Sache for (;;) 566157184Sache { 567157184Sache int num; 568157184Sache FD_SET (in_from_inferior_fd, &in_set); 569157184Sache FD_SET (in_from_tty_fd, &in_set); 570157184Sache 571157184Sache num = select(maxfd+1, &in_set, NULL, NULL, NULL); 572157184Sache 573157184Sache if (propagate_sigwinch) 574157184Sache { 575157184Sache struct winsize ws; 576157184Sache if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) >= 0) 577157184Sache { 578157184Sache ioctl (master, TIOCSWINSZ, &ws); 579157184Sache } 580157184Sache propagate_sigwinch = 0; 581157184Sache continue; 582157184Sache } 583157184Sache 584157184Sache if (num <= 0) 585157184Sache { 586157184Sache perror ("select"); 587157184Sache exit (-1); 588157184Sache } 589157184Sache if (FD_ISSET (in_from_tty_fd, &in_set)) 590157184Sache { 591157184Sache extern int readline_echoing_p; 592157184Sache struct termios term_master; 593157184Sache int do_canon = 1; 594157184Sache int do_icrnl = 1; 595157184Sache int ioctl_ret; 596157184Sache 597157184Sache DPRINT1("[tty avail num_keys:%d]\n", num_keys); 598157184Sache 599157184Sache /* If we can't get tty modes for the master side of the pty, we 600157184Sache can't handle non-canonical-mode programs. Always assume the 601157184Sache master is in canonical echo mode if we can't tell. */ 602157184Sache ioctl_ret = tcgetattr(master, &term_master); 603157184Sache 604157184Sache if (ioctl_ret >= 0) 605157184Sache { 606157184Sache do_canon = (term_master.c_lflag & ICANON) != 0; 607157184Sache do_icrnl = (term_master.c_lflag & ICRNL) != 0; 608157184Sache readline_echoing_p = (term_master.c_lflag & ECHO) != 0; 609157184Sache DPRINT1 ("echo,canon,crnl:%03d\n", 610157184Sache 100 * readline_echoing_p 611157184Sache + 10 * do_canon 612157184Sache + 1 * do_icrnl); 613157184Sache } 614157184Sache else 615157184Sache { 616157184Sache if (ioctl_err == 0) 617157184Sache DPRINT1("tcgetattr on master fd failed: errno = %d\n", errno); 618157184Sache ioctl_err = 1; 619157184Sache } 620157184Sache 621157184Sache if (do_canon == 0 && num_keys == 0) 622157184Sache { 623157184Sache char ch[10]; 624157184Sache int count = read (STDIN_FILENO, ch, sizeof(ch)); 625157184Sache DPRINT1("[read %d chars from stdin: ", count); 626157184Sache DPRINT2(" \"%.*s\"]\n", count, ch); 627157184Sache if (do_icrnl) 628157184Sache { 629157184Sache int i = count; 630157184Sache while (--i >= 0) 631157184Sache { 632157184Sache if (ch[i] == '\r') 633157184Sache ch[i] = '\n'; 634157184Sache } 635157184Sache } 636157184Sache maybe_emphasize_input (1); 637157184Sache write (out_to_inferior_fd, ch, count); 638157184Sache } 639157184Sache else 640157184Sache { 641157184Sache if (num_keys == 0) 642157184Sache { 643157184Sache int i; 644157184Sache /* Re-install callback handler for new prompt. */ 645157184Sache if (prompt != empty_string) 646157184Sache free (prompt); 647157184Sache if (prompt == NULL) 648157184Sache { 649157184Sache DPRINT0("New empty prompt\n"); 650157184Sache prompt = empty_string; 651157184Sache } 652157184Sache else 653157184Sache { 654157184Sache if (do_emphasize_input && buf_count > 0) 655157184Sache { 656157184Sache prompt = malloc (buf_count + strlen (end_input_mode) 657157184Sache + strlen (start_input_mode) + 5); 658157184Sache sprintf (prompt, "\001%s\002%.*s\001%s\002", 659157184Sache end_input_mode, 660157184Sache buf_count, buf, 661157184Sache start_input_mode); 662157184Sache } 663157184Sache else 664157184Sache { 665157184Sache prompt = malloc (buf_count + 1); 666157184Sache memcpy (prompt, buf, buf_count); 667157184Sache prompt[buf_count] = '\0'; 668157184Sache } 669157184Sache DPRINT1("New prompt '%s'\n", prompt); 670157184Sache#if 0 /* ifdef HAVE_RL_ALREADY_PROMPTED */ 671157184Sache /* Doesn't quite work when do_emphasize_input is 1. */ 672157184Sache rl_already_prompted = buf_count > 0; 673157184Sache#else 674157184Sache if (buf_count > 0) 675157184Sache write (1, "\r", 1); 676157184Sache#endif 677157184Sache } 678157184Sache 679157184Sache rl_callback_handler_install (prompt, line_handler); 680157184Sache } 681157184Sache num_keys++; 682157184Sache maybe_emphasize_input (1); 683157184Sache rl_callback_read_char (); 684157184Sache } 685157184Sache } 686157184Sache else /* output from inferior. */ 687157184Sache { 688157184Sache int i; 689157184Sache int count; 690157184Sache int old_count; 691157184Sache if (buf_count > (sizeof(buf) >> 2)) 692157184Sache buf_count = 0; 693157184Sache count = read (in_from_inferior_fd, buf+buf_count, 694157184Sache sizeof(buf) - buf_count); 695157184Sache DPRINT2("read %d from inferior, buf_count=%d", count, buf_count); 696157184Sache DPRINT2(": \"%.*s\"", count, buf+buf_count); 697157184Sache maybe_emphasize_input (0); 698157184Sache if (count <= 0) 699157184Sache { 700157184Sache DPRINT0 ("(Connection closed by foreign host.)\n"); 701157184Sache tcsetattr(STDIN_FILENO, TCSANOW, &orig_term); 702157184Sache exit (0); 703157184Sache } 704157184Sache old_count = buf_count; 705157184Sache 706157184Sache /* Look for any pending echo that we need to suppress. */ 707157184Sache while (echo_suppress_start < echo_suppress_limit 708157184Sache && count > 0 709157184Sache && buf[buf_count] == echo_suppress_buffer[echo_suppress_start]) 710157184Sache { 711157184Sache count--; 712157184Sache buf_count++; 713157184Sache echo_suppress_start++; 714157184Sache } 715157184Sache DPRINT1("suppressed %d characters of echo.\n", buf_count-old_count); 716157184Sache 717157184Sache /* Write to the terminal anything that was not suppressed. */ 718157184Sache if (count > 0) 719157184Sache write (1, buf + buf_count, count); 720157184Sache 721157184Sache /* Finally, look for a prompt candidate. 722157184Sache * When we get around to going input (from the keyboard), 723157184Sache * we will consider the prompt to be anything since the last 724157184Sache * line terminator. So we need to save that text in the 725157184Sache * initial part of buf. However, anything before the 726157184Sache * most recent end-of-line is not interesting. */ 727157184Sache buf_count += count; 728157184Sache#if 1 729157184Sache for (i = buf_count; --i >= old_count; ) 730157184Sache#else 731157184Sache for (i = buf_count - 1; i-- >= buf_count - count; ) 732157184Sache#endif 733157184Sache { 734157184Sache if (buf[i] == '\n' || buf[i] == '\r') 735157184Sache { 736157184Sache i++; 737157184Sache memmove (buf, buf+i, buf_count - i); 738157184Sache buf_count -= i; 739157184Sache break; 740157184Sache } 741157184Sache } 742157184Sache DPRINT2("-> i: %d, buf_count: %d\n", i, buf_count); 743157184Sache } 744157184Sache } 745157184Sache} 746157184Sache 747157184Sachestatic void set_edit_mode () 748157184Sache{ 749157184Sache int vi = 0; 750157184Sache char *shellopts; 751157184Sache 752157184Sache shellopts = getenv ("SHELLOPTS"); 753157184Sache while (shellopts != 0) 754157184Sache { 755157184Sache if (strncmp ("vi", shellopts, 2) == 0) 756157184Sache { 757157184Sache vi = 1; 758157184Sache break; 759157184Sache } 760157184Sache shellopts = index (shellopts + 1, ':'); 761157184Sache } 762157184Sache 763157184Sache if (!vi) 764157184Sache { 765157184Sache if (getenv ("EDITOR") != 0) 766157184Sache vi |= strcmp (getenv ("EDITOR"), "vi") == 0; 767157184Sache } 768157184Sache 769157184Sache if (vi) 770157184Sache rl_variable_bind ("editing-mode", "vi"); 771157184Sache else 772157184Sache rl_variable_bind ("editing-mode", "emacs"); 773157184Sache} 774157184Sache 775157184Sache 776157184Sachestatic void usage_exit () 777157184Sache{ 778157184Sache fprintf (stderr, "Usage: rlfe [-h histfile] [-s size] cmd [arg1] [arg2] ...\n\n"); 779157184Sache exit (1); 780157184Sache} 781