xref: /titanic_51/usr/src/cmd/enhance/enhance.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <stdio.h>
30*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
31*7c478bd9Sstevel@tonic-gate #include <string.h>
32*7c478bd9Sstevel@tonic-gate #include <errno.h>
33*7c478bd9Sstevel@tonic-gate #include <signal.h>
34*7c478bd9Sstevel@tonic-gate #include <locale.h>
35*7c478bd9Sstevel@tonic-gate 
36*7c478bd9Sstevel@tonic-gate #include <unistd.h>
37*7c478bd9Sstevel@tonic-gate #include <termios.h>
38*7c478bd9Sstevel@tonic-gate 
39*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
40*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_SELECT_H
41*7c478bd9Sstevel@tonic-gate #include <sys/select.h>
42*7c478bd9Sstevel@tonic-gate #endif
43*7c478bd9Sstevel@tonic-gate #endif
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/wait.h>
49*7c478bd9Sstevel@tonic-gate #include <dirent.h>
50*7c478bd9Sstevel@tonic-gate 
51*7c478bd9Sstevel@tonic-gate #if HAVE_SYSV_PTY
52*7c478bd9Sstevel@tonic-gate #include <stropts.h>    /* System-V stream I/O */
53*7c478bd9Sstevel@tonic-gate char *ptsname(int fd);
54*7c478bd9Sstevel@tonic-gate int grantpt(int fd);
55*7c478bd9Sstevel@tonic-gate int unlockpt(int fd);
56*7c478bd9Sstevel@tonic-gate #endif
57*7c478bd9Sstevel@tonic-gate 
58*7c478bd9Sstevel@tonic-gate #include "libtecla.h"
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate /*
61*7c478bd9Sstevel@tonic-gate  * Pseudo-terminal devices are found in the following directory.
62*7c478bd9Sstevel@tonic-gate  */
63*7c478bd9Sstevel@tonic-gate #define PTY_DEV_DIR "/dev/"
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate /*
66*7c478bd9Sstevel@tonic-gate  * Pseudo-terminal controller device file names start with the following
67*7c478bd9Sstevel@tonic-gate  * prefix.
68*7c478bd9Sstevel@tonic-gate  */
69*7c478bd9Sstevel@tonic-gate #define PTY_CNTRL "pty"
70*7c478bd9Sstevel@tonic-gate 
71*7c478bd9Sstevel@tonic-gate /*
72*7c478bd9Sstevel@tonic-gate  * Pseudo-terminal slave device file names start with the following
73*7c478bd9Sstevel@tonic-gate  * prefix.
74*7c478bd9Sstevel@tonic-gate  */
75*7c478bd9Sstevel@tonic-gate #define PTY_SLAVE "tty"
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate /*
78*7c478bd9Sstevel@tonic-gate  * Specify the maximum suffix length for the control and slave device
79*7c478bd9Sstevel@tonic-gate  * names.
80*7c478bd9Sstevel@tonic-gate  */
81*7c478bd9Sstevel@tonic-gate #define PTY_MAX_SUFFIX 10
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate /*
84*7c478bd9Sstevel@tonic-gate  * Set the maximum length of the master and slave terminal device filenames,
85*7c478bd9Sstevel@tonic-gate  * including space for a terminating '\0'.
86*7c478bd9Sstevel@tonic-gate  */
87*7c478bd9Sstevel@tonic-gate #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \
88*7c478bd9Sstevel@tonic-gate 		      (sizeof(PTY_SLAVE) > sizeof(PTY_CNTRL) ? \
89*7c478bd9Sstevel@tonic-gate 		       sizeof(PTY_SLAVE) : sizeof(PTY_CNTRL))-1 \
90*7c478bd9Sstevel@tonic-gate 		      + PTY_MAX_SUFFIX + 1)
91*7c478bd9Sstevel@tonic-gate /*
92*7c478bd9Sstevel@tonic-gate  * Set the maximum length of an input line.
93*7c478bd9Sstevel@tonic-gate  */
94*7c478bd9Sstevel@tonic-gate #define PTY_MAX_LINE 4096
95*7c478bd9Sstevel@tonic-gate 
96*7c478bd9Sstevel@tonic-gate /*
97*7c478bd9Sstevel@tonic-gate  * Set the size of the buffer used for accumulating bytes written by the
98*7c478bd9Sstevel@tonic-gate  * user's terminal to its stdout.
99*7c478bd9Sstevel@tonic-gate  */
100*7c478bd9Sstevel@tonic-gate #define PTY_MAX_READ 1000
101*7c478bd9Sstevel@tonic-gate 
102*7c478bd9Sstevel@tonic-gate /*
103*7c478bd9Sstevel@tonic-gate  * Set the amount of memory used to record history.
104*7c478bd9Sstevel@tonic-gate  */
105*7c478bd9Sstevel@tonic-gate #define PTY_HIST_SIZE 10000
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate /*
108*7c478bd9Sstevel@tonic-gate  * Set the timeout delay used to check for quickly arriving
109*7c478bd9Sstevel@tonic-gate  * sequential output from the application.
110*7c478bd9Sstevel@tonic-gate  */
111*7c478bd9Sstevel@tonic-gate #define PTY_READ_TIMEOUT 100000    /* micro-seconds */
112*7c478bd9Sstevel@tonic-gate 
113*7c478bd9Sstevel@tonic-gate static int pty_open_master(const char *prog, int *cntrl, char *slave_name);
114*7c478bd9Sstevel@tonic-gate static int pty_open_slave(const char *prog, char *slave_name);
115*7c478bd9Sstevel@tonic-gate static int pty_child(const char *prog, int slave, char *argv[]);
116*7c478bd9Sstevel@tonic-gate static int pty_parent(const char *prog, int cntrl);
117*7c478bd9Sstevel@tonic-gate static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff);
118*7c478bd9Sstevel@tonic-gate static GL_FD_EVENT_FN(pty_read_from_program);
119*7c478bd9Sstevel@tonic-gate static int pty_write_to_fd(int fd, const char *string, int n);
120*7c478bd9Sstevel@tonic-gate static void pty_child_exited(int sig);
121*7c478bd9Sstevel@tonic-gate static int pty_master_readable(int fd, long usec);
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate /*.......................................................................
124*7c478bd9Sstevel@tonic-gate  * Run a program with enhanced terminal editing facilities.
125*7c478bd9Sstevel@tonic-gate  *
126*7c478bd9Sstevel@tonic-gate  * Usage:
127*7c478bd9Sstevel@tonic-gate  *  enhance program [args...]
128*7c478bd9Sstevel@tonic-gate  */
129*7c478bd9Sstevel@tonic-gate int main(int argc, char *argv[])
130*7c478bd9Sstevel@tonic-gate {
131*7c478bd9Sstevel@tonic-gate   int cntrl = -1;  /* The fd of the pseudo-terminal controller device */
132*7c478bd9Sstevel@tonic-gate   int slave = -1;  /* The fd of the pseudo-terminal slave device */
133*7c478bd9Sstevel@tonic-gate   pid_t pid;       /* The return value of fork() */
134*7c478bd9Sstevel@tonic-gate   int status;      /* The return statuses of the parent and child functions */
135*7c478bd9Sstevel@tonic-gate   char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */
136*7c478bd9Sstevel@tonic-gate                                  /*  pseudo-terminal. */
137*7c478bd9Sstevel@tonic-gate   char *prog;      /* The name of the program (ie. argv[0]) */
138*7c478bd9Sstevel@tonic-gate /*
139*7c478bd9Sstevel@tonic-gate  * Check the arguments.
140*7c478bd9Sstevel@tonic-gate  */
141*7c478bd9Sstevel@tonic-gate   if(argc < 2) {
142*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "Usage: %s <program> [arguments...]\n", argv[0]);
143*7c478bd9Sstevel@tonic-gate     return 1;
144*7c478bd9Sstevel@tonic-gate   };
145*7c478bd9Sstevel@tonic-gate /*
146*7c478bd9Sstevel@tonic-gate  * Get the name of the program.
147*7c478bd9Sstevel@tonic-gate  */
148*7c478bd9Sstevel@tonic-gate   prog = argv[0];
149*7c478bd9Sstevel@tonic-gate /*
150*7c478bd9Sstevel@tonic-gate  * If the user has the LC_CTYPE or LC_ALL environment variables set,
151*7c478bd9Sstevel@tonic-gate  * enable display of characters corresponding to the specified locale.
152*7c478bd9Sstevel@tonic-gate  */
153*7c478bd9Sstevel@tonic-gate   (void) setlocale(LC_CTYPE, "");
154*7c478bd9Sstevel@tonic-gate /*
155*7c478bd9Sstevel@tonic-gate  * If the program is taking its input from a pipe or a file, or
156*7c478bd9Sstevel@tonic-gate  * sending its output to something other than a terminal, run the
157*7c478bd9Sstevel@tonic-gate  * program without tecla.
158*7c478bd9Sstevel@tonic-gate  */
159*7c478bd9Sstevel@tonic-gate   if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
160*7c478bd9Sstevel@tonic-gate     if(execvp(argv[1], argv + 1) < 0) {
161*7c478bd9Sstevel@tonic-gate       fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1],
162*7c478bd9Sstevel@tonic-gate 	      strerror(errno));
163*7c478bd9Sstevel@tonic-gate       fflush(stderr);
164*7c478bd9Sstevel@tonic-gate       _exit(1);
165*7c478bd9Sstevel@tonic-gate     };
166*7c478bd9Sstevel@tonic-gate   };
167*7c478bd9Sstevel@tonic-gate /*
168*7c478bd9Sstevel@tonic-gate  * Open the master side of a pseudo-terminal pair, and return
169*7c478bd9Sstevel@tonic-gate  * the corresponding file descriptor and the filename of the
170*7c478bd9Sstevel@tonic-gate  * slave end of the pseudo-terminal.
171*7c478bd9Sstevel@tonic-gate  */
172*7c478bd9Sstevel@tonic-gate   if(pty_open_master(prog, &cntrl, slave_name))
173*7c478bd9Sstevel@tonic-gate     return 1;
174*7c478bd9Sstevel@tonic-gate /*
175*7c478bd9Sstevel@tonic-gate  * Set up a signal handler to watch for the child process exiting.
176*7c478bd9Sstevel@tonic-gate  */
177*7c478bd9Sstevel@tonic-gate   signal(SIGCHLD, pty_child_exited);
178*7c478bd9Sstevel@tonic-gate /*
179*7c478bd9Sstevel@tonic-gate  * The above signal handler sends the parent process a SIGINT signal.
180*7c478bd9Sstevel@tonic-gate  * This signal is caught by gl_get_line(), which resets the terminal
181*7c478bd9Sstevel@tonic-gate  * settings, and if the application signal handler for this signal
182*7c478bd9Sstevel@tonic-gate  * doesn't abort the process, gl_get_line() returns NULL with errno
183*7c478bd9Sstevel@tonic-gate  * set to EINTR. Arrange to ignore the signal, so that gl_get_line()
184*7c478bd9Sstevel@tonic-gate  * returns and we have a chance to cleanup.
185*7c478bd9Sstevel@tonic-gate  */
186*7c478bd9Sstevel@tonic-gate   signal(SIGINT, SIG_IGN);
187*7c478bd9Sstevel@tonic-gate /*
188*7c478bd9Sstevel@tonic-gate  * We will read user input in one process, and run the user's program
189*7c478bd9Sstevel@tonic-gate  * in a child process.
190*7c478bd9Sstevel@tonic-gate  */
191*7c478bd9Sstevel@tonic-gate   pid = fork();
192*7c478bd9Sstevel@tonic-gate   if(pid < 0) {
193*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog,
194*7c478bd9Sstevel@tonic-gate 	    strerror(errno));
195*7c478bd9Sstevel@tonic-gate     return 1;
196*7c478bd9Sstevel@tonic-gate   };
197*7c478bd9Sstevel@tonic-gate /*
198*7c478bd9Sstevel@tonic-gate  * Are we the parent?
199*7c478bd9Sstevel@tonic-gate  */
200*7c478bd9Sstevel@tonic-gate   if(pid!=0) {
201*7c478bd9Sstevel@tonic-gate     status = pty_parent(prog, cntrl);
202*7c478bd9Sstevel@tonic-gate     close(cntrl);
203*7c478bd9Sstevel@tonic-gate   } else {
204*7c478bd9Sstevel@tonic-gate     close(cntrl); /* The child doesn't use the slave device */
205*7c478bd9Sstevel@tonic-gate     signal(SIGCHLD, pty_child_exited);
206*7c478bd9Sstevel@tonic-gate     if((slave = pty_open_slave(prog, slave_name)) >= 0) {
207*7c478bd9Sstevel@tonic-gate       status = pty_child(prog, slave, argv + 1);
208*7c478bd9Sstevel@tonic-gate       close(slave);
209*7c478bd9Sstevel@tonic-gate     } else {
210*7c478bd9Sstevel@tonic-gate       status = 1;
211*7c478bd9Sstevel@tonic-gate     };
212*7c478bd9Sstevel@tonic-gate   };
213*7c478bd9Sstevel@tonic-gate   return status;
214*7c478bd9Sstevel@tonic-gate }
215*7c478bd9Sstevel@tonic-gate 
216*7c478bd9Sstevel@tonic-gate /*.......................................................................
217*7c478bd9Sstevel@tonic-gate  * Open the master side of a pseudo-terminal pair, and return
218*7c478bd9Sstevel@tonic-gate  * the corresponding file descriptor and the filename of the
219*7c478bd9Sstevel@tonic-gate  * slave end of the pseudo-terminal.
220*7c478bd9Sstevel@tonic-gate  *
221*7c478bd9Sstevel@tonic-gate  * Input/Output:
222*7c478bd9Sstevel@tonic-gate  *  prog  const char *  The name of this program.
223*7c478bd9Sstevel@tonic-gate  *  cntrl        int *  The file descriptor of the pseudo-terminal
224*7c478bd9Sstevel@tonic-gate  *                      controller device will be assigned tp *cntrl.
225*7c478bd9Sstevel@tonic-gate  *  slave_name  char *  The file-name of the pseudo-terminal slave device
226*7c478bd9Sstevel@tonic-gate  *                      will be recorded in slave_name[], which must have
227*7c478bd9Sstevel@tonic-gate  *                      at least PTY_MAX_NAME elements.
228*7c478bd9Sstevel@tonic-gate  * Output:
229*7c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
230*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
231*7c478bd9Sstevel@tonic-gate  */
232*7c478bd9Sstevel@tonic-gate static int pty_open_master(const char *prog, int *cntrl, char *slave_name)
233*7c478bd9Sstevel@tonic-gate {
234*7c478bd9Sstevel@tonic-gate   char master_name[PTY_MAX_NAME]; /* The filename of the master device */
235*7c478bd9Sstevel@tonic-gate   DIR *dir;                       /* The directory iterator */
236*7c478bd9Sstevel@tonic-gate   struct dirent *file;            /* A file in "/dev" */
237*7c478bd9Sstevel@tonic-gate /*
238*7c478bd9Sstevel@tonic-gate  * Mark the controller device as not opened yet.
239*7c478bd9Sstevel@tonic-gate  */
240*7c478bd9Sstevel@tonic-gate   *cntrl = -1;
241*7c478bd9Sstevel@tonic-gate /*
242*7c478bd9Sstevel@tonic-gate  * On systems with the Sys-V pseudo-terminal interface, we don't
243*7c478bd9Sstevel@tonic-gate  * have to search for a free master terminal. We just open /dev/ptmx,
244*7c478bd9Sstevel@tonic-gate  * and if there is a free master terminal device, we are given a file
245*7c478bd9Sstevel@tonic-gate  * descriptor connected to it.
246*7c478bd9Sstevel@tonic-gate  */
247*7c478bd9Sstevel@tonic-gate #if HAVE_SYSV_PTY
248*7c478bd9Sstevel@tonic-gate   *cntrl = open("/dev/ptmx", O_RDWR);
249*7c478bd9Sstevel@tonic-gate   if(*cntrl >= 0) {
250*7c478bd9Sstevel@tonic-gate /*
251*7c478bd9Sstevel@tonic-gate  * Get the filename of the slave side of the pseudo-terminal.
252*7c478bd9Sstevel@tonic-gate  */
253*7c478bd9Sstevel@tonic-gate     char *name = ptsname(*cntrl);
254*7c478bd9Sstevel@tonic-gate     if(name) {
255*7c478bd9Sstevel@tonic-gate       if(strlen(name)+1 > PTY_MAX_NAME) {
256*7c478bd9Sstevel@tonic-gate 	fprintf(stderr, "%s: Slave pty filename too long.\n", prog);
257*7c478bd9Sstevel@tonic-gate 	return 1;
258*7c478bd9Sstevel@tonic-gate       };
259*7c478bd9Sstevel@tonic-gate       strlcpy(slave_name, name, PTY_MAX_NAME);
260*7c478bd9Sstevel@tonic-gate /*
261*7c478bd9Sstevel@tonic-gate  * If unable to get the slave name, discard the controller file descriptor,
262*7c478bd9Sstevel@tonic-gate  * ready to try a search instead.
263*7c478bd9Sstevel@tonic-gate  */
264*7c478bd9Sstevel@tonic-gate     } else {
265*7c478bd9Sstevel@tonic-gate       close(*cntrl);
266*7c478bd9Sstevel@tonic-gate       *cntrl = -1;
267*7c478bd9Sstevel@tonic-gate     };
268*7c478bd9Sstevel@tonic-gate   } else {
269*7c478bd9Sstevel@tonic-gate #endif
270*7c478bd9Sstevel@tonic-gate /*
271*7c478bd9Sstevel@tonic-gate  * On systems without /dev/ptmx, or if opening /dev/ptmx failed,
272*7c478bd9Sstevel@tonic-gate  * we open one master terminal after another, until one that isn't
273*7c478bd9Sstevel@tonic-gate  * in use by another program is found.
274*7c478bd9Sstevel@tonic-gate  *
275*7c478bd9Sstevel@tonic-gate  * Open the devices directory.
276*7c478bd9Sstevel@tonic-gate  */
277*7c478bd9Sstevel@tonic-gate     dir = opendir(PTY_DEV_DIR);
278*7c478bd9Sstevel@tonic-gate     if(!dir) {
279*7c478bd9Sstevel@tonic-gate       fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR,
280*7c478bd9Sstevel@tonic-gate 	      strerror(errno));
281*7c478bd9Sstevel@tonic-gate       return 1;
282*7c478bd9Sstevel@tonic-gate     };
283*7c478bd9Sstevel@tonic-gate /*
284*7c478bd9Sstevel@tonic-gate  * Look for pseudo-terminal controller device files in the devices
285*7c478bd9Sstevel@tonic-gate  * directory.
286*7c478bd9Sstevel@tonic-gate  */
287*7c478bd9Sstevel@tonic-gate     while(*cntrl < 0 && (file = readdir(dir))) {
288*7c478bd9Sstevel@tonic-gate       if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) {
289*7c478bd9Sstevel@tonic-gate /*
290*7c478bd9Sstevel@tonic-gate  * Get the common extension of the control and slave filenames.
291*7c478bd9Sstevel@tonic-gate  */
292*7c478bd9Sstevel@tonic-gate 	const char *ext = file->d_name + sizeof(PTY_CNTRL)-1;
293*7c478bd9Sstevel@tonic-gate 	if(strlen(ext) > PTY_MAX_SUFFIX)
294*7c478bd9Sstevel@tonic-gate 	  continue;
295*7c478bd9Sstevel@tonic-gate /*
296*7c478bd9Sstevel@tonic-gate  * Attempt to open the control file.
297*7c478bd9Sstevel@tonic-gate  */
298*7c478bd9Sstevel@tonic-gate 	strlcpy(master_name, PTY_DEV_DIR, sizeof(master_name));
299*7c478bd9Sstevel@tonic-gate 	strlcat(master_name, PTY_CNTRL, sizeof(master_name));
300*7c478bd9Sstevel@tonic-gate 	strlcat(master_name, ext, sizeof(master_name));
301*7c478bd9Sstevel@tonic-gate 	*cntrl = open(master_name, O_RDWR);
302*7c478bd9Sstevel@tonic-gate 	if(*cntrl < 0)
303*7c478bd9Sstevel@tonic-gate 	  continue;
304*7c478bd9Sstevel@tonic-gate /*
305*7c478bd9Sstevel@tonic-gate  * Attempt to open the matching slave file.
306*7c478bd9Sstevel@tonic-gate  */
307*7c478bd9Sstevel@tonic-gate 	strlcpy(slave_name, PTY_DEV_DIR, PTY_MAX_NAME);
308*7c478bd9Sstevel@tonic-gate 	strlcat(slave_name, PTY_SLAVE, PTY_MAX_NAME);
309*7c478bd9Sstevel@tonic-gate 	strlcat(slave_name, ext, PTY_MAX_NAME);
310*7c478bd9Sstevel@tonic-gate       };
311*7c478bd9Sstevel@tonic-gate     };
312*7c478bd9Sstevel@tonic-gate     closedir(dir);
313*7c478bd9Sstevel@tonic-gate #if HAVE_SYSV_PTY
314*7c478bd9Sstevel@tonic-gate   };
315*7c478bd9Sstevel@tonic-gate #endif
316*7c478bd9Sstevel@tonic-gate /*
317*7c478bd9Sstevel@tonic-gate  * Did we fail to find a pseudo-terminal pair that we could open?
318*7c478bd9Sstevel@tonic-gate  */
319*7c478bd9Sstevel@tonic-gate   if(*cntrl < 0) {
320*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog);
321*7c478bd9Sstevel@tonic-gate     return 1;
322*7c478bd9Sstevel@tonic-gate   };
323*7c478bd9Sstevel@tonic-gate /*
324*7c478bd9Sstevel@tonic-gate  * System V systems require the program that opens the master to
325*7c478bd9Sstevel@tonic-gate  * grant access to the slave side of the pseudo-terminal.
326*7c478bd9Sstevel@tonic-gate  */
327*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SYSV_PTY
328*7c478bd9Sstevel@tonic-gate   if(grantpt(*cntrl) < 0 ||
329*7c478bd9Sstevel@tonic-gate      unlockpt(*cntrl) < 0) {
330*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog,
331*7c478bd9Sstevel@tonic-gate 	    strerror(errno));
332*7c478bd9Sstevel@tonic-gate     return 1;
333*7c478bd9Sstevel@tonic-gate   };
334*7c478bd9Sstevel@tonic-gate #endif
335*7c478bd9Sstevel@tonic-gate /*
336*7c478bd9Sstevel@tonic-gate  * Success.
337*7c478bd9Sstevel@tonic-gate  */
338*7c478bd9Sstevel@tonic-gate   return 0;
339*7c478bd9Sstevel@tonic-gate }
340*7c478bd9Sstevel@tonic-gate 
341*7c478bd9Sstevel@tonic-gate /*.......................................................................
342*7c478bd9Sstevel@tonic-gate  * Open the slave end of a pseudo-terminal.
343*7c478bd9Sstevel@tonic-gate  *
344*7c478bd9Sstevel@tonic-gate  * Input:
345*7c478bd9Sstevel@tonic-gate  *  prog   const char *  The name of this program.
346*7c478bd9Sstevel@tonic-gate  *  slave_name   char *  The filename of the slave device.
347*7c478bd9Sstevel@tonic-gate  * Output:
348*7c478bd9Sstevel@tonic-gate  *  return        int    The file descriptor of the successfully opened
349*7c478bd9Sstevel@tonic-gate  *                       slave device, or < 0 on error.
350*7c478bd9Sstevel@tonic-gate  */
351*7c478bd9Sstevel@tonic-gate static int pty_open_slave(const char *prog, char *slave_name)
352*7c478bd9Sstevel@tonic-gate {
353*7c478bd9Sstevel@tonic-gate   int fd;  /* The file descriptor of the slave device */
354*7c478bd9Sstevel@tonic-gate /*
355*7c478bd9Sstevel@tonic-gate  * Place the process in its own process group. In system-V based
356*7c478bd9Sstevel@tonic-gate  * OS's, this ensures that when the pseudo-terminal is opened, it
357*7c478bd9Sstevel@tonic-gate  * becomes the controlling terminal of the process.
358*7c478bd9Sstevel@tonic-gate  */
359*7c478bd9Sstevel@tonic-gate   if(setsid() < 0) {
360*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog,
361*7c478bd9Sstevel@tonic-gate 	    strerror(errno));
362*7c478bd9Sstevel@tonic-gate     return -1;
363*7c478bd9Sstevel@tonic-gate   };
364*7c478bd9Sstevel@tonic-gate /*
365*7c478bd9Sstevel@tonic-gate  * Attempt to open the specified device.
366*7c478bd9Sstevel@tonic-gate  */
367*7c478bd9Sstevel@tonic-gate   fd = open(slave_name, O_RDWR);
368*7c478bd9Sstevel@tonic-gate   if(fd < 0) {
369*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to open pseudo-terminal slave device (%s).\n",
370*7c478bd9Sstevel@tonic-gate 	    prog, strerror(errno));
371*7c478bd9Sstevel@tonic-gate     return -1;
372*7c478bd9Sstevel@tonic-gate   };
373*7c478bd9Sstevel@tonic-gate /*
374*7c478bd9Sstevel@tonic-gate  * On system-V streams based systems, we need to push the stream modules
375*7c478bd9Sstevel@tonic-gate  * that implement pseudo-terminal and termio interfaces. At least on
376*7c478bd9Sstevel@tonic-gate  * Solaris, which pushes these automatically when a slave is opened,
377*7c478bd9Sstevel@tonic-gate  * this is redundant, so ignore errors when pushing the modules.
378*7c478bd9Sstevel@tonic-gate  */
379*7c478bd9Sstevel@tonic-gate #if HAVE_SYSV_PTY
380*7c478bd9Sstevel@tonic-gate   (void) ioctl(fd, I_PUSH, "ptem");
381*7c478bd9Sstevel@tonic-gate   (void) ioctl(fd, I_PUSH, "ldterm");
382*7c478bd9Sstevel@tonic-gate /*
383*7c478bd9Sstevel@tonic-gate  * On BSD based systems other than SunOS 4.x, the following makes the
384*7c478bd9Sstevel@tonic-gate  * pseudo-terminal the controlling terminal of the child process.
385*7c478bd9Sstevel@tonic-gate  * According to the pseudo-terminal example code in Steven's
386*7c478bd9Sstevel@tonic-gate  * Advanced programming in the unix environment, the !defined(CIBAUD)
387*7c478bd9Sstevel@tonic-gate  * part of the clause prevents this from being used under SunOS. Since
388*7c478bd9Sstevel@tonic-gate  * I only have his code with me, and won't have access to the book,
389*7c478bd9Sstevel@tonic-gate  * I don't know why this is necessary.
390*7c478bd9Sstevel@tonic-gate  */
391*7c478bd9Sstevel@tonic-gate #elif defined(TIOCSCTTY) && !defined(CIBAUD)
392*7c478bd9Sstevel@tonic-gate   if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) {
393*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n",
394*7c478bd9Sstevel@tonic-gate 	    prog, strerror(errno));
395*7c478bd9Sstevel@tonic-gate     close(fd);
396*7c478bd9Sstevel@tonic-gate     return -1;
397*7c478bd9Sstevel@tonic-gate   };
398*7c478bd9Sstevel@tonic-gate #endif
399*7c478bd9Sstevel@tonic-gate   return fd;
400*7c478bd9Sstevel@tonic-gate }
401*7c478bd9Sstevel@tonic-gate 
402*7c478bd9Sstevel@tonic-gate /*.......................................................................
403*7c478bd9Sstevel@tonic-gate  * Read input from the controlling terminal of the program, using
404*7c478bd9Sstevel@tonic-gate  * gl_get_line(), and feed it to the user's program running in a child
405*7c478bd9Sstevel@tonic-gate  * process, via the controller side of the pseudo-terminal. Also pass
406*7c478bd9Sstevel@tonic-gate  * data received from the user's program via the conroller end of
407*7c478bd9Sstevel@tonic-gate  * the pseudo-terminal, to stdout.
408*7c478bd9Sstevel@tonic-gate  *
409*7c478bd9Sstevel@tonic-gate  * Input:
410*7c478bd9Sstevel@tonic-gate  *  prog  const char *  The name of this program.
411*7c478bd9Sstevel@tonic-gate  *  cntrl        int    The file descriptor of the controller end of the
412*7c478bd9Sstevel@tonic-gate  *                      pseudo-terminal.
413*7c478bd9Sstevel@tonic-gate  * Output:
414*7c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
415*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
416*7c478bd9Sstevel@tonic-gate  */
417*7c478bd9Sstevel@tonic-gate static int pty_parent(const char *prog, int cntrl)
418*7c478bd9Sstevel@tonic-gate {
419*7c478bd9Sstevel@tonic-gate   GetLine *gl = NULL;  /* The gl_get_line() resource object */
420*7c478bd9Sstevel@tonic-gate   char *line;          /* An input line read from the user */
421*7c478bd9Sstevel@tonic-gate   char *rbuff=NULL;    /* A buffer for reading from the pseudo terminal */
422*7c478bd9Sstevel@tonic-gate /*
423*7c478bd9Sstevel@tonic-gate  * Allocate the gl_get_line() resource object.
424*7c478bd9Sstevel@tonic-gate  */
425*7c478bd9Sstevel@tonic-gate   gl = new_GetLine(PTY_MAX_LINE, PTY_HIST_SIZE);
426*7c478bd9Sstevel@tonic-gate   if(!gl)
427*7c478bd9Sstevel@tonic-gate     return pty_stop_parent(1, cntrl, gl, rbuff);
428*7c478bd9Sstevel@tonic-gate /*
429*7c478bd9Sstevel@tonic-gate  * Allocate a buffer to use to accumulate bytes read from the
430*7c478bd9Sstevel@tonic-gate  * pseudo-terminal.
431*7c478bd9Sstevel@tonic-gate  */
432*7c478bd9Sstevel@tonic-gate   rbuff = (char *) malloc(PTY_MAX_READ+1);
433*7c478bd9Sstevel@tonic-gate   if(!rbuff)
434*7c478bd9Sstevel@tonic-gate     return pty_stop_parent(1, cntrl, gl, rbuff);
435*7c478bd9Sstevel@tonic-gate   rbuff[0] = '\0';
436*7c478bd9Sstevel@tonic-gate /*
437*7c478bd9Sstevel@tonic-gate  * Register an event handler to watch for data appearing from the
438*7c478bd9Sstevel@tonic-gate  * user's program on the controller end of the pseudo terminal.
439*7c478bd9Sstevel@tonic-gate  */
440*7c478bd9Sstevel@tonic-gate   if(gl_watch_fd(gl, cntrl, GLFD_READ, pty_read_from_program, rbuff))
441*7c478bd9Sstevel@tonic-gate     return pty_stop_parent(1, cntrl, gl, rbuff);
442*7c478bd9Sstevel@tonic-gate /*
443*7c478bd9Sstevel@tonic-gate  * Read input lines from the user and pass them on to the user's program,
444*7c478bd9Sstevel@tonic-gate  * by writing to the controller end of the pseudo-terminal.
445*7c478bd9Sstevel@tonic-gate  */
446*7c478bd9Sstevel@tonic-gate   while((line=gl_get_line(gl, rbuff, NULL, 0))) {
447*7c478bd9Sstevel@tonic-gate     if(pty_write_to_fd(cntrl, line, strlen(line)))
448*7c478bd9Sstevel@tonic-gate        return pty_stop_parent(1, cntrl, gl, rbuff);
449*7c478bd9Sstevel@tonic-gate     rbuff[0] = '\0';
450*7c478bd9Sstevel@tonic-gate   };
451*7c478bd9Sstevel@tonic-gate   return pty_stop_parent(0, cntrl, gl, rbuff);
452*7c478bd9Sstevel@tonic-gate }
453*7c478bd9Sstevel@tonic-gate 
454*7c478bd9Sstevel@tonic-gate /*.......................................................................
455*7c478bd9Sstevel@tonic-gate  * This is a private return function of pty_parent(), used to release
456*7c478bd9Sstevel@tonic-gate  * dynamically allocated resources, close the controller end of the
457*7c478bd9Sstevel@tonic-gate  * pseudo-terminal, and wait for the child to exit. It returns the
458*7c478bd9Sstevel@tonic-gate  * exit status of the child process, unless the caller reports an
459*7c478bd9Sstevel@tonic-gate  * error itself, in which case the caller's error status is returned.
460*7c478bd9Sstevel@tonic-gate  *
461*7c478bd9Sstevel@tonic-gate  * Input:
462*7c478bd9Sstevel@tonic-gate  *  waserr   int    True if the caller is calling this function because
463*7c478bd9Sstevel@tonic-gate  *                  an error occured.
464*7c478bd9Sstevel@tonic-gate  *  cntrl    int    The file descriptor of the controller end of the
465*7c478bd9Sstevel@tonic-gate  *                  pseudo-terminal.
466*7c478bd9Sstevel@tonic-gate  *  gl   GetLine *  The resource object of gl_get_line().
467*7c478bd9Sstevel@tonic-gate  *  rbuff   char *  The buffer used to accumulate bytes read from
468*7c478bd9Sstevel@tonic-gate  *                  the pseudo-terminal.
469*7c478bd9Sstevel@tonic-gate  * Output:
470*7c478bd9Sstevel@tonic-gate  *  return  int    The desired exit status of the program.
471*7c478bd9Sstevel@tonic-gate  */
472*7c478bd9Sstevel@tonic-gate static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff)
473*7c478bd9Sstevel@tonic-gate {
474*7c478bd9Sstevel@tonic-gate   int status;  /* The return status of the child process */
475*7c478bd9Sstevel@tonic-gate /*
476*7c478bd9Sstevel@tonic-gate  * Close the controller end of the terminal.
477*7c478bd9Sstevel@tonic-gate  */
478*7c478bd9Sstevel@tonic-gate   close(cntrl);
479*7c478bd9Sstevel@tonic-gate /*
480*7c478bd9Sstevel@tonic-gate  * Delete the resource object.
481*7c478bd9Sstevel@tonic-gate  */
482*7c478bd9Sstevel@tonic-gate   gl = del_GetLine(gl);
483*7c478bd9Sstevel@tonic-gate /*
484*7c478bd9Sstevel@tonic-gate  * Delete the read buffer.
485*7c478bd9Sstevel@tonic-gate  */
486*7c478bd9Sstevel@tonic-gate   if(rbuff)
487*7c478bd9Sstevel@tonic-gate     free(rbuff);
488*7c478bd9Sstevel@tonic-gate /*
489*7c478bd9Sstevel@tonic-gate  * Wait for the user's program to end.
490*7c478bd9Sstevel@tonic-gate  */
491*7c478bd9Sstevel@tonic-gate   (void) wait(&status);
492*7c478bd9Sstevel@tonic-gate /*
493*7c478bd9Sstevel@tonic-gate  * Return either our error status, or the return status of the child
494*7c478bd9Sstevel@tonic-gate  * program.
495*7c478bd9Sstevel@tonic-gate  */
496*7c478bd9Sstevel@tonic-gate   return waserr ? 1 : status;
497*7c478bd9Sstevel@tonic-gate }
498*7c478bd9Sstevel@tonic-gate 
499*7c478bd9Sstevel@tonic-gate /*.......................................................................
500*7c478bd9Sstevel@tonic-gate  * Run the user's program, with its stdin and stdout connected to the
501*7c478bd9Sstevel@tonic-gate  * slave end of the psuedo-terminal.
502*7c478bd9Sstevel@tonic-gate  *
503*7c478bd9Sstevel@tonic-gate  * Input:
504*7c478bd9Sstevel@tonic-gate  *  prog  const char *   The name of this program.
505*7c478bd9Sstevel@tonic-gate  *  slave        int     The file descriptor of the slave end of the
506*7c478bd9Sstevel@tonic-gate  *                       pseudo terminal.
507*7c478bd9Sstevel@tonic-gate  *  argv        char *[] The argument vector to pass to the user's program,
508*7c478bd9Sstevel@tonic-gate  *                       where argv[0] is the name of the user's program,
509*7c478bd9Sstevel@tonic-gate  *                       and the last argument is followed by a pointer
510*7c478bd9Sstevel@tonic-gate  *                       to NULL.
511*7c478bd9Sstevel@tonic-gate  * Output:
512*7c478bd9Sstevel@tonic-gate  *  return   int         If this function returns at all, an error must
513*7c478bd9Sstevel@tonic-gate  *                       have occured when trying to overlay the process
514*7c478bd9Sstevel@tonic-gate  *                       with the user's program. In this case 1 is
515*7c478bd9Sstevel@tonic-gate  *                       returned.
516*7c478bd9Sstevel@tonic-gate  */
517*7c478bd9Sstevel@tonic-gate static int pty_child(const char *prog, int slave, char *argv[])
518*7c478bd9Sstevel@tonic-gate {
519*7c478bd9Sstevel@tonic-gate   struct termios attr; /* The terminal attributes */
520*7c478bd9Sstevel@tonic-gate /*
521*7c478bd9Sstevel@tonic-gate  * We need to stop the pseudo-terminal from echoing everything that we send it.
522*7c478bd9Sstevel@tonic-gate  */
523*7c478bd9Sstevel@tonic-gate   if(tcgetattr(slave, &attr)) {
524*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog,
525*7c478bd9Sstevel@tonic-gate 	    strerror(errno));
526*7c478bd9Sstevel@tonic-gate     return 1;
527*7c478bd9Sstevel@tonic-gate   };
528*7c478bd9Sstevel@tonic-gate   attr.c_lflag &= ~(ECHO);
529*7c478bd9Sstevel@tonic-gate   while(tcsetattr(slave, TCSADRAIN, &attr)) {
530*7c478bd9Sstevel@tonic-gate     if(errno != EINTR) {
531*7c478bd9Sstevel@tonic-gate       fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno));
532*7c478bd9Sstevel@tonic-gate       return 1;
533*7c478bd9Sstevel@tonic-gate     };
534*7c478bd9Sstevel@tonic-gate   };
535*7c478bd9Sstevel@tonic-gate /*
536*7c478bd9Sstevel@tonic-gate  * Arrange for stdin, stdout and stderr to be connected to the slave device,
537*7c478bd9Sstevel@tonic-gate  * ignoring errors that imply that either stdin or stdout is closed.
538*7c478bd9Sstevel@tonic-gate  */
539*7c478bd9Sstevel@tonic-gate   while(dup2(slave, STDIN_FILENO) < 0 && errno==EINTR)
540*7c478bd9Sstevel@tonic-gate     ;
541*7c478bd9Sstevel@tonic-gate   while(dup2(slave, STDOUT_FILENO) < 0 && errno==EINTR)
542*7c478bd9Sstevel@tonic-gate     ;
543*7c478bd9Sstevel@tonic-gate   while(dup2(slave, STDERR_FILENO) < 0 && errno==EINTR)
544*7c478bd9Sstevel@tonic-gate     ;
545*7c478bd9Sstevel@tonic-gate /*
546*7c478bd9Sstevel@tonic-gate  * Run the user's program.
547*7c478bd9Sstevel@tonic-gate  */
548*7c478bd9Sstevel@tonic-gate   if(execvp(argv[0], argv) < 0) {
549*7c478bd9Sstevel@tonic-gate     fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0],
550*7c478bd9Sstevel@tonic-gate 	    strerror(errno));
551*7c478bd9Sstevel@tonic-gate     fflush(stderr);
552*7c478bd9Sstevel@tonic-gate     _exit(1);
553*7c478bd9Sstevel@tonic-gate   };
554*7c478bd9Sstevel@tonic-gate   return 0;  /* This should never be reached */
555*7c478bd9Sstevel@tonic-gate }
556*7c478bd9Sstevel@tonic-gate 
557*7c478bd9Sstevel@tonic-gate /*.......................................................................
558*7c478bd9Sstevel@tonic-gate  * This is the event-handler that is called by gl_get_line() whenever
559*7c478bd9Sstevel@tonic-gate  * there is tet waiting to be read from the user's program, via the
560*7c478bd9Sstevel@tonic-gate  * controller end of the pseudo-terminal. See libtecla.h for details
561*7c478bd9Sstevel@tonic-gate  * about its arguments.
562*7c478bd9Sstevel@tonic-gate  */
563*7c478bd9Sstevel@tonic-gate static GL_FD_EVENT_FN(pty_read_from_program)
564*7c478bd9Sstevel@tonic-gate {
565*7c478bd9Sstevel@tonic-gate   char *nlptr;   /* A pointer to the last newline in the accumulated string */
566*7c478bd9Sstevel@tonic-gate   char *crptr;   /* A pointer to the last '\r' in the accumulated string */
567*7c478bd9Sstevel@tonic-gate   char *nextp;   /* A pointer to the next unprocessed character */
568*7c478bd9Sstevel@tonic-gate /*
569*7c478bd9Sstevel@tonic-gate  * Get the read buffer in which we are accumulating a line to be
570*7c478bd9Sstevel@tonic-gate  * forwarded to stdout.
571*7c478bd9Sstevel@tonic-gate  */
572*7c478bd9Sstevel@tonic-gate   char *rbuff = (char *) data;
573*7c478bd9Sstevel@tonic-gate /*
574*7c478bd9Sstevel@tonic-gate  * New data may arrive while we are processing the current read, and
575*7c478bd9Sstevel@tonic-gate  * it is more efficient to display this here than to keep returning to
576*7c478bd9Sstevel@tonic-gate  * gl_get_line() and have it display the latest prefix as a prompt,
577*7c478bd9Sstevel@tonic-gate  * followed by the current input line, so we loop, delaying a bit at
578*7c478bd9Sstevel@tonic-gate  * the end of each iteration to check for more data arriving from
579*7c478bd9Sstevel@tonic-gate  * the application, before finally returning to gl_get_line() when
580*7c478bd9Sstevel@tonic-gate  * no more input is available.
581*7c478bd9Sstevel@tonic-gate  */
582*7c478bd9Sstevel@tonic-gate   do {
583*7c478bd9Sstevel@tonic-gate /*
584*7c478bd9Sstevel@tonic-gate  * Get the current length of the output string.
585*7c478bd9Sstevel@tonic-gate  */
586*7c478bd9Sstevel@tonic-gate     int len = strlen(rbuff);
587*7c478bd9Sstevel@tonic-gate /*
588*7c478bd9Sstevel@tonic-gate  * Read the text from the program.
589*7c478bd9Sstevel@tonic-gate  */
590*7c478bd9Sstevel@tonic-gate     int nnew = read(fd, rbuff + len, PTY_MAX_READ - len);
591*7c478bd9Sstevel@tonic-gate     if(nnew < 0)
592*7c478bd9Sstevel@tonic-gate       return GLFD_ABORT;
593*7c478bd9Sstevel@tonic-gate     len += nnew;
594*7c478bd9Sstevel@tonic-gate /*
595*7c478bd9Sstevel@tonic-gate  * Nul terminate the accumulated string.
596*7c478bd9Sstevel@tonic-gate  */
597*7c478bd9Sstevel@tonic-gate     rbuff[len] = '\0';
598*7c478bd9Sstevel@tonic-gate /*
599*7c478bd9Sstevel@tonic-gate  * Find the last newline and last carriage return in the buffer, if any.
600*7c478bd9Sstevel@tonic-gate  */
601*7c478bd9Sstevel@tonic-gate     nlptr = strrchr(rbuff, '\n');
602*7c478bd9Sstevel@tonic-gate     crptr = strrchr(rbuff, '\r');
603*7c478bd9Sstevel@tonic-gate /*
604*7c478bd9Sstevel@tonic-gate  * We want to output up to just before the last newline or carriage
605*7c478bd9Sstevel@tonic-gate  * return. If there are no newlines of carriage returns in the line,
606*7c478bd9Sstevel@tonic-gate  * and the buffer is full, then we should output the whole line. In
607*7c478bd9Sstevel@tonic-gate  * all cases a new output line will be started after the latest text
608*7c478bd9Sstevel@tonic-gate  * has been output. The intention is to leave any incomplete line
609*7c478bd9Sstevel@tonic-gate  * in the buffer, for (perhaps temporary) use as the current prompt.
610*7c478bd9Sstevel@tonic-gate  */
611*7c478bd9Sstevel@tonic-gate     if(nlptr) {
612*7c478bd9Sstevel@tonic-gate       nextp = crptr && crptr < nlptr ? crptr : nlptr;
613*7c478bd9Sstevel@tonic-gate     } else if(crptr) {
614*7c478bd9Sstevel@tonic-gate       nextp = crptr;
615*7c478bd9Sstevel@tonic-gate     } else if(len >= PTY_MAX_READ) {
616*7c478bd9Sstevel@tonic-gate       nextp = rbuff + len;
617*7c478bd9Sstevel@tonic-gate     } else {
618*7c478bd9Sstevel@tonic-gate       nextp = NULL;
619*7c478bd9Sstevel@tonic-gate     };
620*7c478bd9Sstevel@tonic-gate /*
621*7c478bd9Sstevel@tonic-gate  * Do we have any text to output yet?
622*7c478bd9Sstevel@tonic-gate  */
623*7c478bd9Sstevel@tonic-gate     if(nextp) {
624*7c478bd9Sstevel@tonic-gate /*
625*7c478bd9Sstevel@tonic-gate  * If there was already some text in rbuff before this function
626*7c478bd9Sstevel@tonic-gate  * was called, then it will have been used as a prompt. Arrange
627*7c478bd9Sstevel@tonic-gate  * to rewrite this prefix, plus the new suffix, by moving back to
628*7c478bd9Sstevel@tonic-gate  * the start of the line.
629*7c478bd9Sstevel@tonic-gate  */
630*7c478bd9Sstevel@tonic-gate       if(len > 0)
631*7c478bd9Sstevel@tonic-gate 	(void) pty_write_to_fd(STDOUT_FILENO, "\r", 1);
632*7c478bd9Sstevel@tonic-gate /*
633*7c478bd9Sstevel@tonic-gate  * Write everything up to the last newline to stdout.
634*7c478bd9Sstevel@tonic-gate  */
635*7c478bd9Sstevel@tonic-gate       (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff);
636*7c478bd9Sstevel@tonic-gate /*
637*7c478bd9Sstevel@tonic-gate  * Start a new line.
638*7c478bd9Sstevel@tonic-gate  */
639*7c478bd9Sstevel@tonic-gate       (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2);
640*7c478bd9Sstevel@tonic-gate /*
641*7c478bd9Sstevel@tonic-gate  * Skip trailing carriage returns and newlines.
642*7c478bd9Sstevel@tonic-gate  */
643*7c478bd9Sstevel@tonic-gate       while(*nextp=='\n' || *nextp=='\r')
644*7c478bd9Sstevel@tonic-gate 	nextp++;
645*7c478bd9Sstevel@tonic-gate /*
646*7c478bd9Sstevel@tonic-gate  * Move any unwritten text following the newline, to the start of the
647*7c478bd9Sstevel@tonic-gate  * buffer.
648*7c478bd9Sstevel@tonic-gate  */
649*7c478bd9Sstevel@tonic-gate       memmove(rbuff, nextp, len - (nextp - rbuff) + 1);
650*7c478bd9Sstevel@tonic-gate     };
651*7c478bd9Sstevel@tonic-gate   } while(pty_master_readable(fd, PTY_READ_TIMEOUT));
652*7c478bd9Sstevel@tonic-gate /*
653*7c478bd9Sstevel@tonic-gate  * Make the incomplete line in the output buffer the current prompt.
654*7c478bd9Sstevel@tonic-gate  */
655*7c478bd9Sstevel@tonic-gate   gl_replace_prompt(gl, rbuff);
656*7c478bd9Sstevel@tonic-gate   return GLFD_REFRESH;
657*7c478bd9Sstevel@tonic-gate }
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate /*.......................................................................
660*7c478bd9Sstevel@tonic-gate  * Write a given string to a specified file descriptor.
661*7c478bd9Sstevel@tonic-gate  *
662*7c478bd9Sstevel@tonic-gate  * Input:
663*7c478bd9Sstevel@tonic-gate  *  fd             int     The file descriptor to write to.
664*7c478bd9Sstevel@tonic-gate  *  string  const char *   The string to write (of at least 'n' characters).
665*7c478bd9Sstevel@tonic-gate  *  n              int     The number of characters to write.
666*7c478bd9Sstevel@tonic-gate  * Output:
667*7c478bd9Sstevel@tonic-gate  *  return         int     0 - OK.
668*7c478bd9Sstevel@tonic-gate  *                         1 - Error.
669*7c478bd9Sstevel@tonic-gate  */
670*7c478bd9Sstevel@tonic-gate static int pty_write_to_fd(int fd, const char *string, int n)
671*7c478bd9Sstevel@tonic-gate {
672*7c478bd9Sstevel@tonic-gate   int ndone = 0;  /* The number of characters written so far */
673*7c478bd9Sstevel@tonic-gate /*
674*7c478bd9Sstevel@tonic-gate  * Do as many writes as are needed to write the whole string.
675*7c478bd9Sstevel@tonic-gate  */
676*7c478bd9Sstevel@tonic-gate   while(ndone < n) {
677*7c478bd9Sstevel@tonic-gate     int nnew = write(fd, string + ndone, n - ndone);
678*7c478bd9Sstevel@tonic-gate     if(nnew > 0)
679*7c478bd9Sstevel@tonic-gate       ndone += nnew;
680*7c478bd9Sstevel@tonic-gate     else if(errno != EINTR)
681*7c478bd9Sstevel@tonic-gate       return 1;
682*7c478bd9Sstevel@tonic-gate   };
683*7c478bd9Sstevel@tonic-gate   return 0;
684*7c478bd9Sstevel@tonic-gate }
685*7c478bd9Sstevel@tonic-gate 
686*7c478bd9Sstevel@tonic-gate /*.......................................................................
687*7c478bd9Sstevel@tonic-gate  * This is the signal handler that is called when the child process
688*7c478bd9Sstevel@tonic-gate  * that is running the user's program exits for any reason. It closes
689*7c478bd9Sstevel@tonic-gate  * the slave end of the terminal, so that gl_get_line() in the parent
690*7c478bd9Sstevel@tonic-gate  * process sees an end of file.
691*7c478bd9Sstevel@tonic-gate  */
692*7c478bd9Sstevel@tonic-gate static void pty_child_exited(int sig)
693*7c478bd9Sstevel@tonic-gate {
694*7c478bd9Sstevel@tonic-gate   raise(SIGINT);
695*7c478bd9Sstevel@tonic-gate }
696*7c478bd9Sstevel@tonic-gate 
697*7c478bd9Sstevel@tonic-gate /*.......................................................................
698*7c478bd9Sstevel@tonic-gate  * Return non-zero after a given amount of time if there is data waiting
699*7c478bd9Sstevel@tonic-gate  * to be read from a given file descriptor.
700*7c478bd9Sstevel@tonic-gate  *
701*7c478bd9Sstevel@tonic-gate  * Input:
702*7c478bd9Sstevel@tonic-gate  *  fd        int  The descriptor to watch.
703*7c478bd9Sstevel@tonic-gate  *  usec     long  The number of micro-seconds to wait for input to
704*7c478bd9Sstevel@tonic-gate  *                 arrive before giving up.
705*7c478bd9Sstevel@tonic-gate  * Output:
706*7c478bd9Sstevel@tonic-gate  *  return    int  0 - No data is waiting to be read (or select isn't
707*7c478bd9Sstevel@tonic-gate  *                     available).
708*7c478bd9Sstevel@tonic-gate  *                 1 - Data is waiting to be read.
709*7c478bd9Sstevel@tonic-gate  */
710*7c478bd9Sstevel@tonic-gate static int pty_master_readable(int fd, long usec)
711*7c478bd9Sstevel@tonic-gate {
712*7c478bd9Sstevel@tonic-gate #if HAVE_SELECT
713*7c478bd9Sstevel@tonic-gate   fd_set rfds;             /* The set of file descriptors to check */
714*7c478bd9Sstevel@tonic-gate   struct timeval timeout;  /* The timeout */
715*7c478bd9Sstevel@tonic-gate   FD_ZERO(&rfds);
716*7c478bd9Sstevel@tonic-gate   FD_SET(fd, &rfds);
717*7c478bd9Sstevel@tonic-gate   timeout.tv_sec = 0;
718*7c478bd9Sstevel@tonic-gate   timeout.tv_usec = usec;
719*7c478bd9Sstevel@tonic-gate   return select(fd+1, &rfds, NULL, NULL, &timeout) == 1;
720*7c478bd9Sstevel@tonic-gate #else
721*7c478bd9Sstevel@tonic-gate   return 0;
722*7c478bd9Sstevel@tonic-gate #endif
723*7c478bd9Sstevel@tonic-gate }
724