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