xref: /illumos-gate/usr/src/cmd/enhance/enhance.c (revision 1fa2a664)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <stdio.h>
287c478bd9Sstevel@tonic-gate #include <stdlib.h>
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <errno.h>
317c478bd9Sstevel@tonic-gate #include <signal.h>
327c478bd9Sstevel@tonic-gate #include <locale.h>
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <unistd.h>
357c478bd9Sstevel@tonic-gate #include <termios.h>
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
387c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_SELECT_H
397c478bd9Sstevel@tonic-gate #include <sys/select.h>
407c478bd9Sstevel@tonic-gate #endif
417c478bd9Sstevel@tonic-gate #endif
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <fcntl.h>
447c478bd9Sstevel@tonic-gate #include <sys/time.h>
457c478bd9Sstevel@tonic-gate #include <sys/types.h>
467c478bd9Sstevel@tonic-gate #include <sys/wait.h>
477c478bd9Sstevel@tonic-gate #include <dirent.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #if HAVE_SYSV_PTY
507c478bd9Sstevel@tonic-gate #include <stropts.h>    /* System-V stream I/O */
517c478bd9Sstevel@tonic-gate char *ptsname(int fd);
527c478bd9Sstevel@tonic-gate int grantpt(int fd);
537c478bd9Sstevel@tonic-gate int unlockpt(int fd);
547c478bd9Sstevel@tonic-gate #endif
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate #include "libtecla.h"
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate /*
597c478bd9Sstevel@tonic-gate  * Pseudo-terminal devices are found in the following directory.
607c478bd9Sstevel@tonic-gate  */
617c478bd9Sstevel@tonic-gate #define PTY_DEV_DIR "/dev/"
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate /*
647c478bd9Sstevel@tonic-gate  * Pseudo-terminal controller device file names start with the following
657c478bd9Sstevel@tonic-gate  * prefix.
667c478bd9Sstevel@tonic-gate  */
677c478bd9Sstevel@tonic-gate #define PTY_CNTRL "pty"
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
70*1fa2a664SJoshua M. Clulow  * Pseudo-terminal subsidiary device file names start with the following
717c478bd9Sstevel@tonic-gate  * prefix.
727c478bd9Sstevel@tonic-gate  */
73*1fa2a664SJoshua M. Clulow #define PTY_SUBSID "tty"
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate /*
76*1fa2a664SJoshua M. Clulow  * Specify the maximum suffix length for the control and subsidiary device
777c478bd9Sstevel@tonic-gate  * names.
787c478bd9Sstevel@tonic-gate  */
797c478bd9Sstevel@tonic-gate #define PTY_MAX_SUFFIX 10
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*
82*1fa2a664SJoshua M. Clulow  * Set the maximum length of the manager and subsidiary terminal device
83*1fa2a664SJoshua M. Clulow  * filenames, including space for a terminating '\0'.
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \
86*1fa2a664SJoshua M. Clulow 		      (sizeof(PTY_SUBSID) > sizeof(PTY_CNTRL) ? \
87*1fa2a664SJoshua M. Clulow 		       sizeof(PTY_SUBSID) : sizeof(PTY_CNTRL))-1 \
887c478bd9Sstevel@tonic-gate 		      + PTY_MAX_SUFFIX + 1)
897c478bd9Sstevel@tonic-gate /*
907c478bd9Sstevel@tonic-gate  * Set the maximum length of an input line.
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate #define PTY_MAX_LINE 4096
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate /*
957c478bd9Sstevel@tonic-gate  * Set the size of the buffer used for accumulating bytes written by the
967c478bd9Sstevel@tonic-gate  * user's terminal to its stdout.
977c478bd9Sstevel@tonic-gate  */
987c478bd9Sstevel@tonic-gate #define PTY_MAX_READ 1000
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate /*
1017c478bd9Sstevel@tonic-gate  * Set the amount of memory used to record history.
1027c478bd9Sstevel@tonic-gate  */
1037c478bd9Sstevel@tonic-gate #define PTY_HIST_SIZE 10000
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * Set the timeout delay used to check for quickly arriving
1077c478bd9Sstevel@tonic-gate  * sequential output from the application.
1087c478bd9Sstevel@tonic-gate  */
1097c478bd9Sstevel@tonic-gate #define PTY_READ_TIMEOUT 100000    /* micro-seconds */
1107c478bd9Sstevel@tonic-gate 
111*1fa2a664SJoshua M. Clulow static int pty_open_manager(const char *prog, int *cntrl, char *subsid_name);
112*1fa2a664SJoshua M. Clulow static int pty_open_subsid(const char *prog, char *subsid_name);
113*1fa2a664SJoshua M. Clulow static int pty_child(const char *prog, int subsid, char *argv[]);
1147c478bd9Sstevel@tonic-gate static int pty_parent(const char *prog, int cntrl);
1157c478bd9Sstevel@tonic-gate static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff);
1167c478bd9Sstevel@tonic-gate static GL_FD_EVENT_FN(pty_read_from_program);
1177c478bd9Sstevel@tonic-gate static int pty_write_to_fd(int fd, const char *string, int n);
1187c478bd9Sstevel@tonic-gate static void pty_child_exited(int sig);
119*1fa2a664SJoshua M. Clulow static int pty_manager_readable(int fd, long usec);
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate /*.......................................................................
1227c478bd9Sstevel@tonic-gate  * Run a program with enhanced terminal editing facilities.
1237c478bd9Sstevel@tonic-gate  *
1247c478bd9Sstevel@tonic-gate  * Usage:
1257c478bd9Sstevel@tonic-gate  *  enhance program [args...]
1267c478bd9Sstevel@tonic-gate  */
main(int argc,char * argv[])1277c478bd9Sstevel@tonic-gate int main(int argc, char *argv[])
1287c478bd9Sstevel@tonic-gate {
1297c478bd9Sstevel@tonic-gate   int cntrl = -1;  /* The fd of the pseudo-terminal controller device */
130*1fa2a664SJoshua M. Clulow   int subsid = -1;  /* The fd of the pseudo-terminal subsidiary device */
1317c478bd9Sstevel@tonic-gate   pid_t pid;       /* The return value of fork() */
1327c478bd9Sstevel@tonic-gate   int status;      /* The return statuses of the parent and child functions */
133*1fa2a664SJoshua M. Clulow   char subsid_name[PTY_MAX_NAME]; /* The filename of the subsidiary end of */
134*1fa2a664SJoshua M. Clulow 				 /*  the pseudo-terminal. */
1357c478bd9Sstevel@tonic-gate   char *prog;      /* The name of the program (ie. argv[0]) */
1367c478bd9Sstevel@tonic-gate /*
1377c478bd9Sstevel@tonic-gate  * Check the arguments.
1387c478bd9Sstevel@tonic-gate  */
1397c478bd9Sstevel@tonic-gate   if(argc < 2) {
1407c478bd9Sstevel@tonic-gate     fprintf(stderr, "Usage: %s <program> [arguments...]\n", argv[0]);
1417c478bd9Sstevel@tonic-gate     return 1;
1427c478bd9Sstevel@tonic-gate   };
1437c478bd9Sstevel@tonic-gate /*
1447c478bd9Sstevel@tonic-gate  * Get the name of the program.
1457c478bd9Sstevel@tonic-gate  */
1467c478bd9Sstevel@tonic-gate   prog = argv[0];
1477c478bd9Sstevel@tonic-gate /*
1487c478bd9Sstevel@tonic-gate  * If the user has the LC_CTYPE or LC_ALL environment variables set,
1497c478bd9Sstevel@tonic-gate  * enable display of characters corresponding to the specified locale.
1507c478bd9Sstevel@tonic-gate  */
1517c478bd9Sstevel@tonic-gate   (void) setlocale(LC_CTYPE, "");
1527c478bd9Sstevel@tonic-gate /*
1537c478bd9Sstevel@tonic-gate  * If the program is taking its input from a pipe or a file, or
1547c478bd9Sstevel@tonic-gate  * sending its output to something other than a terminal, run the
1557c478bd9Sstevel@tonic-gate  * program without tecla.
1567c478bd9Sstevel@tonic-gate  */
1577c478bd9Sstevel@tonic-gate   if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
1587c478bd9Sstevel@tonic-gate     if(execvp(argv[1], argv + 1) < 0) {
1597c478bd9Sstevel@tonic-gate       fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1],
1607c478bd9Sstevel@tonic-gate 	      strerror(errno));
1617c478bd9Sstevel@tonic-gate       fflush(stderr);
1627c478bd9Sstevel@tonic-gate       _exit(1);
1637c478bd9Sstevel@tonic-gate     };
1647c478bd9Sstevel@tonic-gate   };
1657c478bd9Sstevel@tonic-gate /*
166*1fa2a664SJoshua M. Clulow  * Open the manager side of a pseudo-terminal pair, and return
1677c478bd9Sstevel@tonic-gate  * the corresponding file descriptor and the filename of the
168*1fa2a664SJoshua M. Clulow  * subsidiary end of the pseudo-terminal.
1697c478bd9Sstevel@tonic-gate  */
170*1fa2a664SJoshua M. Clulow   if(pty_open_manager(prog, &cntrl, subsid_name))
1717c478bd9Sstevel@tonic-gate     return 1;
1727c478bd9Sstevel@tonic-gate /*
1737c478bd9Sstevel@tonic-gate  * Set up a signal handler to watch for the child process exiting.
1747c478bd9Sstevel@tonic-gate  */
1757c478bd9Sstevel@tonic-gate   signal(SIGCHLD, pty_child_exited);
1767c478bd9Sstevel@tonic-gate /*
1777c478bd9Sstevel@tonic-gate  * The above signal handler sends the parent process a SIGINT signal.
1787c478bd9Sstevel@tonic-gate  * This signal is caught by gl_get_line(), which resets the terminal
1797c478bd9Sstevel@tonic-gate  * settings, and if the application signal handler for this signal
1807c478bd9Sstevel@tonic-gate  * doesn't abort the process, gl_get_line() returns NULL with errno
1817c478bd9Sstevel@tonic-gate  * set to EINTR. Arrange to ignore the signal, so that gl_get_line()
1827c478bd9Sstevel@tonic-gate  * returns and we have a chance to cleanup.
1837c478bd9Sstevel@tonic-gate  */
1847c478bd9Sstevel@tonic-gate   signal(SIGINT, SIG_IGN);
1857c478bd9Sstevel@tonic-gate /*
1867c478bd9Sstevel@tonic-gate  * We will read user input in one process, and run the user's program
1877c478bd9Sstevel@tonic-gate  * in a child process.
1887c478bd9Sstevel@tonic-gate  */
1897c478bd9Sstevel@tonic-gate   pid = fork();
1907c478bd9Sstevel@tonic-gate   if(pid < 0) {
1917c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog,
1927c478bd9Sstevel@tonic-gate 	    strerror(errno));
1937c478bd9Sstevel@tonic-gate     return 1;
1947c478bd9Sstevel@tonic-gate   };
1957c478bd9Sstevel@tonic-gate /*
1967c478bd9Sstevel@tonic-gate  * Are we the parent?
1977c478bd9Sstevel@tonic-gate  */
1987c478bd9Sstevel@tonic-gate   if(pid!=0) {
1997c478bd9Sstevel@tonic-gate     status = pty_parent(prog, cntrl);
2007c478bd9Sstevel@tonic-gate     close(cntrl);
2017c478bd9Sstevel@tonic-gate   } else {
202*1fa2a664SJoshua M. Clulow     close(cntrl); /* The child doesn't use the subsidiary device */
2037c478bd9Sstevel@tonic-gate     signal(SIGCHLD, pty_child_exited);
204*1fa2a664SJoshua M. Clulow     if((subsid = pty_open_subsid(prog, subsid_name)) >= 0) {
205*1fa2a664SJoshua M. Clulow       status = pty_child(prog, subsid, argv + 1);
206*1fa2a664SJoshua M. Clulow       close(subsid);
2077c478bd9Sstevel@tonic-gate     } else {
2087c478bd9Sstevel@tonic-gate       status = 1;
2097c478bd9Sstevel@tonic-gate     };
2107c478bd9Sstevel@tonic-gate   };
2117c478bd9Sstevel@tonic-gate   return status;
2127c478bd9Sstevel@tonic-gate }
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate /*.......................................................................
215*1fa2a664SJoshua M. Clulow  * Open the manager side of a pseudo-terminal pair, and return
2167c478bd9Sstevel@tonic-gate  * the corresponding file descriptor and the filename of the
217*1fa2a664SJoshua M. Clulow  * subsidiary end of the pseudo-terminal.
2187c478bd9Sstevel@tonic-gate  *
2197c478bd9Sstevel@tonic-gate  * Input/Output:
2207c478bd9Sstevel@tonic-gate  *  prog  const char *  The name of this program.
2217c478bd9Sstevel@tonic-gate  *  cntrl        int *  The file descriptor of the pseudo-terminal
2227c478bd9Sstevel@tonic-gate  *                      controller device will be assigned tp *cntrl.
223*1fa2a664SJoshua M. Clulow  *  subsid_name  char *  The file-name of the pseudo-terminal subsidiary device
224*1fa2a664SJoshua M. Clulow  *                      will be recorded in subsid_name[], which must have
2257c478bd9Sstevel@tonic-gate  *                      at least PTY_MAX_NAME elements.
2267c478bd9Sstevel@tonic-gate  * Output:
2277c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
2287c478bd9Sstevel@tonic-gate  *                      1 - Error.
2297c478bd9Sstevel@tonic-gate  */
pty_open_manager(const char * prog,int * cntrl,char * subsid_name)230*1fa2a664SJoshua M. Clulow static int pty_open_manager(const char *prog, int *cntrl, char *subsid_name)
2317c478bd9Sstevel@tonic-gate {
232*1fa2a664SJoshua M. Clulow   char manager_name[PTY_MAX_NAME]; /* The filename of the manager device */
2337c478bd9Sstevel@tonic-gate   DIR *dir;                       /* The directory iterator */
2347c478bd9Sstevel@tonic-gate   struct dirent *file;            /* A file in "/dev" */
2357c478bd9Sstevel@tonic-gate /*
2367c478bd9Sstevel@tonic-gate  * Mark the controller device as not opened yet.
2377c478bd9Sstevel@tonic-gate  */
2387c478bd9Sstevel@tonic-gate   *cntrl = -1;
2397c478bd9Sstevel@tonic-gate /*
2407c478bd9Sstevel@tonic-gate  * On systems with the Sys-V pseudo-terminal interface, we don't
241*1fa2a664SJoshua M. Clulow  * have to search for a free manager terminal. We just open /dev/ptmx,
242*1fa2a664SJoshua M. Clulow  * and if there is a free manager terminal device, we are given a file
2437c478bd9Sstevel@tonic-gate  * descriptor connected to it.
2447c478bd9Sstevel@tonic-gate  */
2457c478bd9Sstevel@tonic-gate #if HAVE_SYSV_PTY
2467c478bd9Sstevel@tonic-gate   *cntrl = open("/dev/ptmx", O_RDWR);
2477c478bd9Sstevel@tonic-gate   if(*cntrl >= 0) {
2487c478bd9Sstevel@tonic-gate /*
249*1fa2a664SJoshua M. Clulow  * Get the filename of the subsidiary side of the pseudo-terminal.
2507c478bd9Sstevel@tonic-gate  */
2517c478bd9Sstevel@tonic-gate     char *name = ptsname(*cntrl);
2527c478bd9Sstevel@tonic-gate     if(name) {
2537c478bd9Sstevel@tonic-gate       if(strlen(name)+1 > PTY_MAX_NAME) {
254*1fa2a664SJoshua M. Clulow 	fprintf(stderr, "%s: Subsidiary pty filename too long.\n", prog);
2557c478bd9Sstevel@tonic-gate 	return 1;
2567c478bd9Sstevel@tonic-gate       };
257*1fa2a664SJoshua M. Clulow       strlcpy(subsid_name, name, PTY_MAX_NAME);
2587c478bd9Sstevel@tonic-gate /*
259*1fa2a664SJoshua M. Clulow  * If unable to get the subsidiary name, discard the controller file
260*1fa2a664SJoshua M. Clulow  * descriptor, ready to try a search instead.
2617c478bd9Sstevel@tonic-gate  */
2627c478bd9Sstevel@tonic-gate     } else {
2637c478bd9Sstevel@tonic-gate       close(*cntrl);
2647c478bd9Sstevel@tonic-gate       *cntrl = -1;
2657c478bd9Sstevel@tonic-gate     };
2667c478bd9Sstevel@tonic-gate   } else {
2677c478bd9Sstevel@tonic-gate #endif
2687c478bd9Sstevel@tonic-gate /*
2697c478bd9Sstevel@tonic-gate  * On systems without /dev/ptmx, or if opening /dev/ptmx failed,
270*1fa2a664SJoshua M. Clulow  * we open one manager terminal after another, until one that isn't
2717c478bd9Sstevel@tonic-gate  * in use by another program is found.
2727c478bd9Sstevel@tonic-gate  *
2737c478bd9Sstevel@tonic-gate  * Open the devices directory.
2747c478bd9Sstevel@tonic-gate  */
2757c478bd9Sstevel@tonic-gate     dir = opendir(PTY_DEV_DIR);
2767c478bd9Sstevel@tonic-gate     if(!dir) {
2777c478bd9Sstevel@tonic-gate       fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR,
2787c478bd9Sstevel@tonic-gate 	      strerror(errno));
2797c478bd9Sstevel@tonic-gate       return 1;
2807c478bd9Sstevel@tonic-gate     };
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  * Look for pseudo-terminal controller device files in the devices
2837c478bd9Sstevel@tonic-gate  * directory.
2847c478bd9Sstevel@tonic-gate  */
2857c478bd9Sstevel@tonic-gate     while(*cntrl < 0 && (file = readdir(dir))) {
2867c478bd9Sstevel@tonic-gate       if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) {
2877c478bd9Sstevel@tonic-gate /*
288*1fa2a664SJoshua M. Clulow  * Get the common extension of the control and subsidiary filenames.
2897c478bd9Sstevel@tonic-gate  */
2907c478bd9Sstevel@tonic-gate 	const char *ext = file->d_name + sizeof(PTY_CNTRL)-1;
2917c478bd9Sstevel@tonic-gate 	if(strlen(ext) > PTY_MAX_SUFFIX)
2927c478bd9Sstevel@tonic-gate 	  continue;
2937c478bd9Sstevel@tonic-gate /*
2947c478bd9Sstevel@tonic-gate  * Attempt to open the control file.
2957c478bd9Sstevel@tonic-gate  */
296*1fa2a664SJoshua M. Clulow 	strlcpy(manager_name, PTY_DEV_DIR, sizeof(manager_name));
297*1fa2a664SJoshua M. Clulow 	strlcat(manager_name, PTY_CNTRL, sizeof(manager_name));
298*1fa2a664SJoshua M. Clulow 	strlcat(manager_name, ext, sizeof(manager_name));
299*1fa2a664SJoshua M. Clulow 	*cntrl = open(manager_name, O_RDWR);
3007c478bd9Sstevel@tonic-gate 	if(*cntrl < 0)
3017c478bd9Sstevel@tonic-gate 	  continue;
3027c478bd9Sstevel@tonic-gate /*
303*1fa2a664SJoshua M. Clulow  * Attempt to open the matching subsidiary file.
3047c478bd9Sstevel@tonic-gate  */
305*1fa2a664SJoshua M. Clulow 	strlcpy(subsid_name, PTY_DEV_DIR, PTY_MAX_NAME);
306*1fa2a664SJoshua M. Clulow 	strlcat(subsid_name, PTY_SUBSID, PTY_MAX_NAME);
307*1fa2a664SJoshua M. Clulow 	strlcat(subsid_name, ext, PTY_MAX_NAME);
3087c478bd9Sstevel@tonic-gate       };
3097c478bd9Sstevel@tonic-gate     };
3107c478bd9Sstevel@tonic-gate     closedir(dir);
3117c478bd9Sstevel@tonic-gate #if HAVE_SYSV_PTY
3127c478bd9Sstevel@tonic-gate   };
3137c478bd9Sstevel@tonic-gate #endif
3147c478bd9Sstevel@tonic-gate /*
3157c478bd9Sstevel@tonic-gate  * Did we fail to find a pseudo-terminal pair that we could open?
3167c478bd9Sstevel@tonic-gate  */
3177c478bd9Sstevel@tonic-gate   if(*cntrl < 0) {
3187c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog);
3197c478bd9Sstevel@tonic-gate     return 1;
3207c478bd9Sstevel@tonic-gate   };
3217c478bd9Sstevel@tonic-gate /*
322*1fa2a664SJoshua M. Clulow  * System V systems require the program that opens the manager to
323*1fa2a664SJoshua M. Clulow  * grant access to the subsidiary side of the pseudo-terminal.
3247c478bd9Sstevel@tonic-gate  */
3257c478bd9Sstevel@tonic-gate #ifdef HAVE_SYSV_PTY
3267c478bd9Sstevel@tonic-gate   if(grantpt(*cntrl) < 0 ||
3277c478bd9Sstevel@tonic-gate      unlockpt(*cntrl) < 0) {
3287c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog,
3297c478bd9Sstevel@tonic-gate 	    strerror(errno));
3307c478bd9Sstevel@tonic-gate     return 1;
3317c478bd9Sstevel@tonic-gate   };
3327c478bd9Sstevel@tonic-gate #endif
3337c478bd9Sstevel@tonic-gate /*
3347c478bd9Sstevel@tonic-gate  * Success.
3357c478bd9Sstevel@tonic-gate  */
3367c478bd9Sstevel@tonic-gate   return 0;
3377c478bd9Sstevel@tonic-gate }
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate /*.......................................................................
340*1fa2a664SJoshua M. Clulow  * Open the subsidiary end of a pseudo-terminal.
3417c478bd9Sstevel@tonic-gate  *
3427c478bd9Sstevel@tonic-gate  * Input:
3437c478bd9Sstevel@tonic-gate  *  prog   const char *  The name of this program.
344*1fa2a664SJoshua M. Clulow  *  subsid_name   char *  The filename of the subsidiary device.
3457c478bd9Sstevel@tonic-gate  * Output:
3467c478bd9Sstevel@tonic-gate  *  return        int    The file descriptor of the successfully opened
347*1fa2a664SJoshua M. Clulow  *                       subsidiary device, or < 0 on error.
3487c478bd9Sstevel@tonic-gate  */
pty_open_subsid(const char * prog,char * subsid_name)349*1fa2a664SJoshua M. Clulow static int pty_open_subsid(const char *prog, char *subsid_name)
3507c478bd9Sstevel@tonic-gate {
351*1fa2a664SJoshua M. Clulow   int fd;  /* The file descriptor of the subsidiary device */
3527c478bd9Sstevel@tonic-gate /*
3537c478bd9Sstevel@tonic-gate  * Place the process in its own process group. In system-V based
3547c478bd9Sstevel@tonic-gate  * OS's, this ensures that when the pseudo-terminal is opened, it
3557c478bd9Sstevel@tonic-gate  * becomes the controlling terminal of the process.
3567c478bd9Sstevel@tonic-gate  */
3577c478bd9Sstevel@tonic-gate   if(setsid() < 0) {
3587c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog,
3597c478bd9Sstevel@tonic-gate 	    strerror(errno));
3607c478bd9Sstevel@tonic-gate     return -1;
3617c478bd9Sstevel@tonic-gate   };
3627c478bd9Sstevel@tonic-gate /*
3637c478bd9Sstevel@tonic-gate  * Attempt to open the specified device.
3647c478bd9Sstevel@tonic-gate  */
365*1fa2a664SJoshua M. Clulow   fd = open(subsid_name, O_RDWR);
3667c478bd9Sstevel@tonic-gate   if(fd < 0) {
367*1fa2a664SJoshua M. Clulow     fprintf(stderr, "%s: Unable to open pty subsidiary device (%s).\n",
3687c478bd9Sstevel@tonic-gate 	    prog, strerror(errno));
3697c478bd9Sstevel@tonic-gate     return -1;
3707c478bd9Sstevel@tonic-gate   };
3717c478bd9Sstevel@tonic-gate /*
3727c478bd9Sstevel@tonic-gate  * On system-V streams based systems, we need to push the stream modules
3737c478bd9Sstevel@tonic-gate  * that implement pseudo-terminal and termio interfaces. At least on
374*1fa2a664SJoshua M. Clulow  * Solaris, which pushes these automatically when a subsidiary is opened,
3757c478bd9Sstevel@tonic-gate  * this is redundant, so ignore errors when pushing the modules.
3767c478bd9Sstevel@tonic-gate  */
3777c478bd9Sstevel@tonic-gate #if HAVE_SYSV_PTY
3787c478bd9Sstevel@tonic-gate   (void) ioctl(fd, I_PUSH, "ptem");
3797c478bd9Sstevel@tonic-gate   (void) ioctl(fd, I_PUSH, "ldterm");
3807c478bd9Sstevel@tonic-gate /*
3817c478bd9Sstevel@tonic-gate  * On BSD based systems other than SunOS 4.x, the following makes the
3827c478bd9Sstevel@tonic-gate  * pseudo-terminal the controlling terminal of the child process.
3837c478bd9Sstevel@tonic-gate  * According to the pseudo-terminal example code in Steven's
3847c478bd9Sstevel@tonic-gate  * Advanced programming in the unix environment, the !defined(CIBAUD)
3857c478bd9Sstevel@tonic-gate  * part of the clause prevents this from being used under SunOS. Since
3867c478bd9Sstevel@tonic-gate  * I only have his code with me, and won't have access to the book,
3877c478bd9Sstevel@tonic-gate  * I don't know why this is necessary.
3887c478bd9Sstevel@tonic-gate  */
3897c478bd9Sstevel@tonic-gate #elif defined(TIOCSCTTY) && !defined(CIBAUD)
3907c478bd9Sstevel@tonic-gate   if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) {
3917c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n",
3927c478bd9Sstevel@tonic-gate 	    prog, strerror(errno));
3937c478bd9Sstevel@tonic-gate     close(fd);
3947c478bd9Sstevel@tonic-gate     return -1;
3957c478bd9Sstevel@tonic-gate   };
3967c478bd9Sstevel@tonic-gate #endif
3977c478bd9Sstevel@tonic-gate   return fd;
3987c478bd9Sstevel@tonic-gate }
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate /*.......................................................................
4017c478bd9Sstevel@tonic-gate  * Read input from the controlling terminal of the program, using
4027c478bd9Sstevel@tonic-gate  * gl_get_line(), and feed it to the user's program running in a child
4037c478bd9Sstevel@tonic-gate  * process, via the controller side of the pseudo-terminal. Also pass
4047c478bd9Sstevel@tonic-gate  * data received from the user's program via the conroller end of
4057c478bd9Sstevel@tonic-gate  * the pseudo-terminal, to stdout.
4067c478bd9Sstevel@tonic-gate  *
4077c478bd9Sstevel@tonic-gate  * Input:
4087c478bd9Sstevel@tonic-gate  *  prog  const char *  The name of this program.
4097c478bd9Sstevel@tonic-gate  *  cntrl        int    The file descriptor of the controller end of the
4107c478bd9Sstevel@tonic-gate  *                      pseudo-terminal.
4117c478bd9Sstevel@tonic-gate  * Output:
4127c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
4137c478bd9Sstevel@tonic-gate  *                      1 - Error.
4147c478bd9Sstevel@tonic-gate  */
pty_parent(const char * prog,int cntrl)4157c478bd9Sstevel@tonic-gate static int pty_parent(const char *prog, int cntrl)
4167c478bd9Sstevel@tonic-gate {
4177c478bd9Sstevel@tonic-gate   GetLine *gl = NULL;  /* The gl_get_line() resource object */
4187c478bd9Sstevel@tonic-gate   char *line;          /* An input line read from the user */
4197c478bd9Sstevel@tonic-gate   char *rbuff=NULL;    /* A buffer for reading from the pseudo terminal */
4207c478bd9Sstevel@tonic-gate /*
4217c478bd9Sstevel@tonic-gate  * Allocate the gl_get_line() resource object.
4227c478bd9Sstevel@tonic-gate  */
4237c478bd9Sstevel@tonic-gate   gl = new_GetLine(PTY_MAX_LINE, PTY_HIST_SIZE);
4247c478bd9Sstevel@tonic-gate   if(!gl)
4257c478bd9Sstevel@tonic-gate     return pty_stop_parent(1, cntrl, gl, rbuff);
4267c478bd9Sstevel@tonic-gate /*
4277c478bd9Sstevel@tonic-gate  * Allocate a buffer to use to accumulate bytes read from the
4287c478bd9Sstevel@tonic-gate  * pseudo-terminal.
4297c478bd9Sstevel@tonic-gate  */
4307c478bd9Sstevel@tonic-gate   rbuff = (char *) malloc(PTY_MAX_READ+1);
4317c478bd9Sstevel@tonic-gate   if(!rbuff)
4327c478bd9Sstevel@tonic-gate     return pty_stop_parent(1, cntrl, gl, rbuff);
4337c478bd9Sstevel@tonic-gate   rbuff[0] = '\0';
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate  * Register an event handler to watch for data appearing from the
4367c478bd9Sstevel@tonic-gate  * user's program on the controller end of the pseudo terminal.
4377c478bd9Sstevel@tonic-gate  */
4387c478bd9Sstevel@tonic-gate   if(gl_watch_fd(gl, cntrl, GLFD_READ, pty_read_from_program, rbuff))
4397c478bd9Sstevel@tonic-gate     return pty_stop_parent(1, cntrl, gl, rbuff);
4407c478bd9Sstevel@tonic-gate /*
4417c478bd9Sstevel@tonic-gate  * Read input lines from the user and pass them on to the user's program,
4427c478bd9Sstevel@tonic-gate  * by writing to the controller end of the pseudo-terminal.
4437c478bd9Sstevel@tonic-gate  */
4447c478bd9Sstevel@tonic-gate   while((line=gl_get_line(gl, rbuff, NULL, 0))) {
4457c478bd9Sstevel@tonic-gate     if(pty_write_to_fd(cntrl, line, strlen(line)))
4467c478bd9Sstevel@tonic-gate        return pty_stop_parent(1, cntrl, gl, rbuff);
4477c478bd9Sstevel@tonic-gate     rbuff[0] = '\0';
4487c478bd9Sstevel@tonic-gate   };
4497c478bd9Sstevel@tonic-gate   return pty_stop_parent(0, cntrl, gl, rbuff);
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate /*.......................................................................
4537c478bd9Sstevel@tonic-gate  * This is a private return function of pty_parent(), used to release
4547c478bd9Sstevel@tonic-gate  * dynamically allocated resources, close the controller end of the
4557c478bd9Sstevel@tonic-gate  * pseudo-terminal, and wait for the child to exit. It returns the
4567c478bd9Sstevel@tonic-gate  * exit status of the child process, unless the caller reports an
4577c478bd9Sstevel@tonic-gate  * error itself, in which case the caller's error status is returned.
4587c478bd9Sstevel@tonic-gate  *
4597c478bd9Sstevel@tonic-gate  * Input:
4607c478bd9Sstevel@tonic-gate  *  waserr   int    True if the caller is calling this function because
4617c478bd9Sstevel@tonic-gate  *                  an error occured.
4627c478bd9Sstevel@tonic-gate  *  cntrl    int    The file descriptor of the controller end of the
4637c478bd9Sstevel@tonic-gate  *                  pseudo-terminal.
4647c478bd9Sstevel@tonic-gate  *  gl   GetLine *  The resource object of gl_get_line().
4657c478bd9Sstevel@tonic-gate  *  rbuff   char *  The buffer used to accumulate bytes read from
4667c478bd9Sstevel@tonic-gate  *                  the pseudo-terminal.
4677c478bd9Sstevel@tonic-gate  * Output:
4687c478bd9Sstevel@tonic-gate  *  return  int    The desired exit status of the program.
4697c478bd9Sstevel@tonic-gate  */
pty_stop_parent(int waserr,int cntrl,GetLine * gl,char * rbuff)4707c478bd9Sstevel@tonic-gate static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff)
4717c478bd9Sstevel@tonic-gate {
4727c478bd9Sstevel@tonic-gate   int status;  /* The return status of the child process */
4737c478bd9Sstevel@tonic-gate /*
4747c478bd9Sstevel@tonic-gate  * Close the controller end of the terminal.
4757c478bd9Sstevel@tonic-gate  */
4767c478bd9Sstevel@tonic-gate   close(cntrl);
4777c478bd9Sstevel@tonic-gate /*
4787c478bd9Sstevel@tonic-gate  * Delete the resource object.
4797c478bd9Sstevel@tonic-gate  */
4807c478bd9Sstevel@tonic-gate   gl = del_GetLine(gl);
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate  * Delete the read buffer.
4837c478bd9Sstevel@tonic-gate  */
4847c478bd9Sstevel@tonic-gate   if(rbuff)
4857c478bd9Sstevel@tonic-gate     free(rbuff);
4867c478bd9Sstevel@tonic-gate /*
4877c478bd9Sstevel@tonic-gate  * Wait for the user's program to end.
4887c478bd9Sstevel@tonic-gate  */
4897c478bd9Sstevel@tonic-gate   (void) wait(&status);
4907c478bd9Sstevel@tonic-gate /*
4917c478bd9Sstevel@tonic-gate  * Return either our error status, or the return status of the child
4927c478bd9Sstevel@tonic-gate  * program.
4937c478bd9Sstevel@tonic-gate  */
4947c478bd9Sstevel@tonic-gate   return waserr ? 1 : status;
4957c478bd9Sstevel@tonic-gate }
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate /*.......................................................................
4987c478bd9Sstevel@tonic-gate  * Run the user's program, with its stdin and stdout connected to the
499*1fa2a664SJoshua M. Clulow  * subsidiary end of the psuedo-terminal.
5007c478bd9Sstevel@tonic-gate  *
5017c478bd9Sstevel@tonic-gate  * Input:
5027c478bd9Sstevel@tonic-gate  *  prog  const char *   The name of this program.
503*1fa2a664SJoshua M. Clulow  *  subsid        int     The file descriptor of the subsidiary end of the
5047c478bd9Sstevel@tonic-gate  *                       pseudo terminal.
5057c478bd9Sstevel@tonic-gate  *  argv        char *[] The argument vector to pass to the user's program,
5067c478bd9Sstevel@tonic-gate  *                       where argv[0] is the name of the user's program,
5077c478bd9Sstevel@tonic-gate  *                       and the last argument is followed by a pointer
5087c478bd9Sstevel@tonic-gate  *                       to NULL.
5097c478bd9Sstevel@tonic-gate  * Output:
5107c478bd9Sstevel@tonic-gate  *  return   int         If this function returns at all, an error must
5117c478bd9Sstevel@tonic-gate  *                       have occured when trying to overlay the process
5127c478bd9Sstevel@tonic-gate  *                       with the user's program. In this case 1 is
5137c478bd9Sstevel@tonic-gate  *                       returned.
5147c478bd9Sstevel@tonic-gate  */
pty_child(const char * prog,int subsid,char * argv[])515*1fa2a664SJoshua M. Clulow static int pty_child(const char *prog, int subsid, char *argv[])
5167c478bd9Sstevel@tonic-gate {
5177c478bd9Sstevel@tonic-gate   struct termios attr; /* The terminal attributes */
5187c478bd9Sstevel@tonic-gate /*
5197c478bd9Sstevel@tonic-gate  * We need to stop the pseudo-terminal from echoing everything that we send it.
5207c478bd9Sstevel@tonic-gate  */
521*1fa2a664SJoshua M. Clulow   if(tcgetattr(subsid, &attr)) {
5227c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog,
5237c478bd9Sstevel@tonic-gate 	    strerror(errno));
5247c478bd9Sstevel@tonic-gate     return 1;
5257c478bd9Sstevel@tonic-gate   };
5267c478bd9Sstevel@tonic-gate   attr.c_lflag &= ~(ECHO);
527*1fa2a664SJoshua M. Clulow   while(tcsetattr(subsid, TCSADRAIN, &attr)) {
5287c478bd9Sstevel@tonic-gate     if(errno != EINTR) {
5297c478bd9Sstevel@tonic-gate       fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno));
5307c478bd9Sstevel@tonic-gate       return 1;
5317c478bd9Sstevel@tonic-gate     };
5327c478bd9Sstevel@tonic-gate   };
5337c478bd9Sstevel@tonic-gate /*
534*1fa2a664SJoshua M. Clulow  * Arrange for stdin, stdout and stderr to be connected to the subsidiary
535*1fa2a664SJoshua M. Clulow  * device, ignoring errors that imply that either stdin or stdout is closed.
5367c478bd9Sstevel@tonic-gate  */
537*1fa2a664SJoshua M. Clulow   while(dup2(subsid, STDIN_FILENO) < 0 && errno==EINTR)
5387c478bd9Sstevel@tonic-gate     ;
539*1fa2a664SJoshua M. Clulow   while(dup2(subsid, STDOUT_FILENO) < 0 && errno==EINTR)
5407c478bd9Sstevel@tonic-gate     ;
541*1fa2a664SJoshua M. Clulow   while(dup2(subsid, STDERR_FILENO) < 0 && errno==EINTR)
5427c478bd9Sstevel@tonic-gate     ;
5437c478bd9Sstevel@tonic-gate /*
5447c478bd9Sstevel@tonic-gate  * Run the user's program.
5457c478bd9Sstevel@tonic-gate  */
5467c478bd9Sstevel@tonic-gate   if(execvp(argv[0], argv) < 0) {
5477c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0],
5487c478bd9Sstevel@tonic-gate 	    strerror(errno));
5497c478bd9Sstevel@tonic-gate     fflush(stderr);
5507c478bd9Sstevel@tonic-gate     _exit(1);
5517c478bd9Sstevel@tonic-gate   };
5527c478bd9Sstevel@tonic-gate   return 0;  /* This should never be reached */
5537c478bd9Sstevel@tonic-gate }
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate /*.......................................................................
5567c478bd9Sstevel@tonic-gate  * This is the event-handler that is called by gl_get_line() whenever
5577c478bd9Sstevel@tonic-gate  * there is tet waiting to be read from the user's program, via the
5587c478bd9Sstevel@tonic-gate  * controller end of the pseudo-terminal. See libtecla.h for details
5597c478bd9Sstevel@tonic-gate  * about its arguments.
5607c478bd9Sstevel@tonic-gate  */
GL_FD_EVENT_FN(pty_read_from_program)5617c478bd9Sstevel@tonic-gate static GL_FD_EVENT_FN(pty_read_from_program)
5627c478bd9Sstevel@tonic-gate {
5637c478bd9Sstevel@tonic-gate   char *nlptr;   /* A pointer to the last newline in the accumulated string */
5647c478bd9Sstevel@tonic-gate   char *crptr;   /* A pointer to the last '\r' in the accumulated string */
5657c478bd9Sstevel@tonic-gate   char *nextp;   /* A pointer to the next unprocessed character */
5667c478bd9Sstevel@tonic-gate /*
5677c478bd9Sstevel@tonic-gate  * Get the read buffer in which we are accumulating a line to be
5687c478bd9Sstevel@tonic-gate  * forwarded to stdout.
5697c478bd9Sstevel@tonic-gate  */
5707c478bd9Sstevel@tonic-gate   char *rbuff = (char *) data;
5717c478bd9Sstevel@tonic-gate /*
5727c478bd9Sstevel@tonic-gate  * New data may arrive while we are processing the current read, and
5737c478bd9Sstevel@tonic-gate  * it is more efficient to display this here than to keep returning to
5747c478bd9Sstevel@tonic-gate  * gl_get_line() and have it display the latest prefix as a prompt,
5757c478bd9Sstevel@tonic-gate  * followed by the current input line, so we loop, delaying a bit at
5767c478bd9Sstevel@tonic-gate  * the end of each iteration to check for more data arriving from
5777c478bd9Sstevel@tonic-gate  * the application, before finally returning to gl_get_line() when
5787c478bd9Sstevel@tonic-gate  * no more input is available.
5797c478bd9Sstevel@tonic-gate  */
5807c478bd9Sstevel@tonic-gate   do {
5817c478bd9Sstevel@tonic-gate /*
5827c478bd9Sstevel@tonic-gate  * Get the current length of the output string.
5837c478bd9Sstevel@tonic-gate  */
5847c478bd9Sstevel@tonic-gate     int len = strlen(rbuff);
5857c478bd9Sstevel@tonic-gate /*
5867c478bd9Sstevel@tonic-gate  * Read the text from the program.
5877c478bd9Sstevel@tonic-gate  */
5887c478bd9Sstevel@tonic-gate     int nnew = read(fd, rbuff + len, PTY_MAX_READ - len);
5897c478bd9Sstevel@tonic-gate     if(nnew < 0)
5907c478bd9Sstevel@tonic-gate       return GLFD_ABORT;
5917c478bd9Sstevel@tonic-gate     len += nnew;
5927c478bd9Sstevel@tonic-gate /*
5937c478bd9Sstevel@tonic-gate  * Nul terminate the accumulated string.
5947c478bd9Sstevel@tonic-gate  */
5957c478bd9Sstevel@tonic-gate     rbuff[len] = '\0';
5967c478bd9Sstevel@tonic-gate /*
5977c478bd9Sstevel@tonic-gate  * Find the last newline and last carriage return in the buffer, if any.
5987c478bd9Sstevel@tonic-gate  */
5997c478bd9Sstevel@tonic-gate     nlptr = strrchr(rbuff, '\n');
6007c478bd9Sstevel@tonic-gate     crptr = strrchr(rbuff, '\r');
6017c478bd9Sstevel@tonic-gate /*
6027c478bd9Sstevel@tonic-gate  * We want to output up to just before the last newline or carriage
6037c478bd9Sstevel@tonic-gate  * return. If there are no newlines of carriage returns in the line,
6047c478bd9Sstevel@tonic-gate  * and the buffer is full, then we should output the whole line. In
6057c478bd9Sstevel@tonic-gate  * all cases a new output line will be started after the latest text
6067c478bd9Sstevel@tonic-gate  * has been output. The intention is to leave any incomplete line
6077c478bd9Sstevel@tonic-gate  * in the buffer, for (perhaps temporary) use as the current prompt.
6087c478bd9Sstevel@tonic-gate  */
6097c478bd9Sstevel@tonic-gate     if(nlptr) {
6107c478bd9Sstevel@tonic-gate       nextp = crptr && crptr < nlptr ? crptr : nlptr;
6117c478bd9Sstevel@tonic-gate     } else if(crptr) {
6127c478bd9Sstevel@tonic-gate       nextp = crptr;
6137c478bd9Sstevel@tonic-gate     } else if(len >= PTY_MAX_READ) {
6147c478bd9Sstevel@tonic-gate       nextp = rbuff + len;
6157c478bd9Sstevel@tonic-gate     } else {
6167c478bd9Sstevel@tonic-gate       nextp = NULL;
6177c478bd9Sstevel@tonic-gate     };
6187c478bd9Sstevel@tonic-gate /*
6197c478bd9Sstevel@tonic-gate  * Do we have any text to output yet?
6207c478bd9Sstevel@tonic-gate  */
6217c478bd9Sstevel@tonic-gate     if(nextp) {
6227c478bd9Sstevel@tonic-gate /*
6237c478bd9Sstevel@tonic-gate  * If there was already some text in rbuff before this function
6247c478bd9Sstevel@tonic-gate  * was called, then it will have been used as a prompt. Arrange
6257c478bd9Sstevel@tonic-gate  * to rewrite this prefix, plus the new suffix, by moving back to
6267c478bd9Sstevel@tonic-gate  * the start of the line.
6277c478bd9Sstevel@tonic-gate  */
6287c478bd9Sstevel@tonic-gate       if(len > 0)
6297c478bd9Sstevel@tonic-gate 	(void) pty_write_to_fd(STDOUT_FILENO, "\r", 1);
6307c478bd9Sstevel@tonic-gate /*
6317c478bd9Sstevel@tonic-gate  * Write everything up to the last newline to stdout.
6327c478bd9Sstevel@tonic-gate  */
6337c478bd9Sstevel@tonic-gate       (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff);
6347c478bd9Sstevel@tonic-gate /*
6357c478bd9Sstevel@tonic-gate  * Start a new line.
6367c478bd9Sstevel@tonic-gate  */
6377c478bd9Sstevel@tonic-gate       (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2);
6387c478bd9Sstevel@tonic-gate /*
6397c478bd9Sstevel@tonic-gate  * Skip trailing carriage returns and newlines.
6407c478bd9Sstevel@tonic-gate  */
6417c478bd9Sstevel@tonic-gate       while(*nextp=='\n' || *nextp=='\r')
6427c478bd9Sstevel@tonic-gate 	nextp++;
6437c478bd9Sstevel@tonic-gate /*
6447c478bd9Sstevel@tonic-gate  * Move any unwritten text following the newline, to the start of the
6457c478bd9Sstevel@tonic-gate  * buffer.
6467c478bd9Sstevel@tonic-gate  */
6477c478bd9Sstevel@tonic-gate       memmove(rbuff, nextp, len - (nextp - rbuff) + 1);
6487c478bd9Sstevel@tonic-gate     };
649*1fa2a664SJoshua M. Clulow   } while(pty_manager_readable(fd, PTY_READ_TIMEOUT));
6507c478bd9Sstevel@tonic-gate /*
6517c478bd9Sstevel@tonic-gate  * Make the incomplete line in the output buffer the current prompt.
6527c478bd9Sstevel@tonic-gate  */
6537c478bd9Sstevel@tonic-gate   gl_replace_prompt(gl, rbuff);
6547c478bd9Sstevel@tonic-gate   return GLFD_REFRESH;
6557c478bd9Sstevel@tonic-gate }
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate /*.......................................................................
6587c478bd9Sstevel@tonic-gate  * Write a given string to a specified file descriptor.
6597c478bd9Sstevel@tonic-gate  *
6607c478bd9Sstevel@tonic-gate  * Input:
6617c478bd9Sstevel@tonic-gate  *  fd             int     The file descriptor to write to.
6627c478bd9Sstevel@tonic-gate  *  string  const char *   The string to write (of at least 'n' characters).
6637c478bd9Sstevel@tonic-gate  *  n              int     The number of characters to write.
6647c478bd9Sstevel@tonic-gate  * Output:
6657c478bd9Sstevel@tonic-gate  *  return         int     0 - OK.
6667c478bd9Sstevel@tonic-gate  *                         1 - Error.
6677c478bd9Sstevel@tonic-gate  */
pty_write_to_fd(int fd,const char * string,int n)6687c478bd9Sstevel@tonic-gate static int pty_write_to_fd(int fd, const char *string, int n)
6697c478bd9Sstevel@tonic-gate {
6707c478bd9Sstevel@tonic-gate   int ndone = 0;  /* The number of characters written so far */
6717c478bd9Sstevel@tonic-gate /*
6727c478bd9Sstevel@tonic-gate  * Do as many writes as are needed to write the whole string.
6737c478bd9Sstevel@tonic-gate  */
6747c478bd9Sstevel@tonic-gate   while(ndone < n) {
6757c478bd9Sstevel@tonic-gate     int nnew = write(fd, string + ndone, n - ndone);
6767c478bd9Sstevel@tonic-gate     if(nnew > 0)
6777c478bd9Sstevel@tonic-gate       ndone += nnew;
6787c478bd9Sstevel@tonic-gate     else if(errno != EINTR)
6797c478bd9Sstevel@tonic-gate       return 1;
6807c478bd9Sstevel@tonic-gate   };
6817c478bd9Sstevel@tonic-gate   return 0;
6827c478bd9Sstevel@tonic-gate }
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate /*.......................................................................
6857c478bd9Sstevel@tonic-gate  * This is the signal handler that is called when the child process
6867c478bd9Sstevel@tonic-gate  * that is running the user's program exits for any reason. It closes
687*1fa2a664SJoshua M. Clulow  * the subsidiary end of the terminal, so that gl_get_line() in the parent
6887c478bd9Sstevel@tonic-gate  * process sees an end of file.
6897c478bd9Sstevel@tonic-gate  */
pty_child_exited(int sig)6907c478bd9Sstevel@tonic-gate static void pty_child_exited(int sig)
6917c478bd9Sstevel@tonic-gate {
6927c478bd9Sstevel@tonic-gate   raise(SIGINT);
6937c478bd9Sstevel@tonic-gate }
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate /*.......................................................................
6967c478bd9Sstevel@tonic-gate  * Return non-zero after a given amount of time if there is data waiting
6977c478bd9Sstevel@tonic-gate  * to be read from a given file descriptor.
6987c478bd9Sstevel@tonic-gate  *
6997c478bd9Sstevel@tonic-gate  * Input:
7007c478bd9Sstevel@tonic-gate  *  fd        int  The descriptor to watch.
7017c478bd9Sstevel@tonic-gate  *  usec     long  The number of micro-seconds to wait for input to
7027c478bd9Sstevel@tonic-gate  *                 arrive before giving up.
7037c478bd9Sstevel@tonic-gate  * Output:
7047c478bd9Sstevel@tonic-gate  *  return    int  0 - No data is waiting to be read (or select isn't
7057c478bd9Sstevel@tonic-gate  *                     available).
7067c478bd9Sstevel@tonic-gate  *                 1 - Data is waiting to be read.
7077c478bd9Sstevel@tonic-gate  */
pty_manager_readable(int fd,long usec)708*1fa2a664SJoshua M. Clulow static int pty_manager_readable(int fd, long usec)
7097c478bd9Sstevel@tonic-gate {
7107c478bd9Sstevel@tonic-gate #if HAVE_SELECT
7117c478bd9Sstevel@tonic-gate   fd_set rfds;             /* The set of file descriptors to check */
7127c478bd9Sstevel@tonic-gate   struct timeval timeout;  /* The timeout */
7137c478bd9Sstevel@tonic-gate   FD_ZERO(&rfds);
7147c478bd9Sstevel@tonic-gate   FD_SET(fd, &rfds);
7157c478bd9Sstevel@tonic-gate   timeout.tv_sec = 0;
7167c478bd9Sstevel@tonic-gate   timeout.tv_usec = usec;
7177c478bd9Sstevel@tonic-gate   return select(fd+1, &rfds, NULL, NULL, &timeout) == 1;
7187c478bd9Sstevel@tonic-gate #else
7197c478bd9Sstevel@tonic-gate   return 0;
7207c478bd9Sstevel@tonic-gate #endif
7217c478bd9Sstevel@tonic-gate }
722