?? enhance.c
字號:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <signal.h>#include <locale.h>#include <unistd.h>#include <termios.h>#include <fcntl.h>#include <sys/termios.h>#include <sys/time.h>#include <sys/types.h>#include <sys/wait.h>#include <dirent.h>#if HAVE_SYSV_PTY#include <stropts.h> /* System-V stream I/O */char *ptsname(int fd);int grantpt(int fd);int unlockpt(int fd);#endif#include "libtecla.h"/* * Pseudo-terminal devices are found in the following directory. */#define PTY_DEV_DIR "/dev/"/* * Pseudo-terminal controller device file names start with the following * prefix. */#define PTY_CNTRL "pty"/* * Pseudo-terminal slave device file names start with the following * prefix. */#define PTY_SLAVE "tty"/* * Specify the maximum suffix length for the control and slave device * names. */#define PTY_MAX_SUFFIX 10/* * Set the maximum length of the master and slave terminal device filenames, * including space for a terminating '\0'. */#define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \ (sizeof(PTY_SLAVE) > sizeof(PTY_CNTRL) ? \ sizeof(PTY_SLAVE) : sizeof(PTY_CNTRL))-1 \ + PTY_MAX_SUFFIX + 1)/* * Set the maximum length of an input line. */#define PTY_MAX_LINE 4096/* * Set the size of the buffer used for accumulating bytes written by the * user's terminal to its stdout. */#define PTY_MAX_READ 1000/* * Set the amount of memory used to record history. */#define PTY_HIST_SIZE 10000/* * Set the timeout delay used to check for quickly arriving * sequential output from the application. */#define PTY_READ_TIMEOUT 100000 /* micro-seconds */static int pty_open_master(const char *prog, int *cntrl, char *slave_name);static int pty_open_slave(const char *prog, char *slave_name);static int pty_child(const char *prog, int slave, char *argv[]);static int pty_parent(const char *prog, int cntrl);static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff);static GL_FD_EVENT_FN(pty_read_from_program);static int pty_write_to_fd(int fd, const char *string, int n);static void pty_child_exited(int sig);static int pty_master_readable(int fd, long usec);/*....................................................................... * Run a program with enhanced terminal editing facilities. * * Usage: * enhance program [args...] */int main(int argc, char *argv[]){ int cntrl = -1; /* The fd of the pseudo-terminal controller device */ int slave = -1; /* The fd of the pseudo-terminal slave device */ pid_t pid; /* The return value of fork() */ int status; /* The return statuses of the parent and child functions */ char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */ /* pseudo-terminal. */ char *prog; /* The name of the program (ie. argv[0]) *//* * Check the arguments. */ if(argc < 2) { fprintf(stderr, "Usage: %s <program> [arguments...]\n", argv[0]); return 1; };/* * Get the name of the program. */ prog = argv[0];/* * If the user has the LC_CTYPE or LC_ALL environment variables set, * enable display of characters corresponding to the specified locale. */ (void) setlocale(LC_CTYPE, "");/* * If the program is taking its input from a pipe or a file, or * sending its output to something other than a terminal, run the * program without tecla. */ if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) { if(execvp(argv[1], argv + 1) < 0) { fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1], strerror(errno)); fflush(stderr); _exit(1); }; };/* * Open the master side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the * slave end of the pseudo-terminal. */ if(pty_open_master(prog, &cntrl, slave_name)) return 1;/* * Set up a signal handler to watch for the child process exiting. */ signal(SIGCHLD, pty_child_exited);/* * The above signal handler sends the parent process a SIGINT signal. * This signal is caught by gl_get_line(), which resets the terminal * settings, and if the application signal handler for this signal * doesn't abort the process, gl_get_line() returns NULL with errno * set to EINTR. Arrange to ignore the signal, so that gl_get_line() * returns and we have a chance to cleanup. */ signal(SIGINT, SIG_IGN);/* * We will read user input in one process, and run the user's program * in a child process. */ pid = fork(); if(pid < 0) { fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog, strerror(errno)); return 1; };/* * Are we the parent? */ if(pid!=0) { status = pty_parent(prog, cntrl); close(cntrl); } else { close(cntrl); /* The child doesn't use the slave device */ signal(SIGCHLD, pty_child_exited); if((slave = pty_open_slave(prog, slave_name)) >= 0) { status = pty_child(prog, slave, argv + 1); close(slave); } else { status = 1; }; }; return status;}/*....................................................................... * Open the master side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the * slave end of the pseudo-terminal. * * Input/Output: * prog const char * The name of this program. * cntrl int * The file descriptor of the pseudo-terminal * controller device will be assigned tp *cntrl. * slave_name char * The file-name of the pseudo-terminal slave device * will be recorded in slave_name[], which must have * at least PTY_MAX_NAME elements. * Output: * return int 0 - OK. * 1 - Error. */static int pty_open_master(const char *prog, int *cntrl, char *slave_name){ char master_name[PTY_MAX_NAME]; /* The filename of the master device */ DIR *dir; /* The directory iterator */ struct dirent *file; /* A file in "/dev" *//* * Mark the controller device as not opened yet. */ *cntrl = -1;/* * On systems with the Sys-V pseudo-terminal interface, we don't * have to search for a free master terminal. We just open /dev/ptmx, * and if there is a free master terminal device, we are given a file * descriptor connected to it. */#if HAVE_SYSV_PTY *cntrl = open("/dev/ptmx", O_RDWR); if(*cntrl >= 0) {/* * Get the filename of the slave side of the pseudo-terminal. */ char *name = ptsname(*cntrl); if(name) { if(strlen(name)+1 > PTY_MAX_NAME) { fprintf(stderr, "%s: Slave pty filename too long.\n", prog); return 1; }; strcpy(slave_name, name);/* * If unable to get the slave name, discard the controller file descriptor, * ready to try a search instead. */ } else { close(*cntrl); *cntrl = -1; }; } else {#endif/* * On systems without /dev/ptmx, or if opening /dev/ptmx failed, * we open one master terminal after another, until one that isn't * in use by another program is found. * * Open the devices directory. */ dir = opendir(PTY_DEV_DIR); if(!dir) { fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR, strerror(errno)); return 1; };/* * Look for pseudo-terminal controller device files in the devices * directory. */ while(*cntrl < 0 && (file = readdir(dir))) { if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) {/* * Get the common extension of the control and slave filenames. */ const char *ext = file->d_name + sizeof(PTY_CNTRL)-1; if(strlen(ext) > PTY_MAX_SUFFIX) continue;/* * Attempt to open the control file. */ strcpy(master_name, PTY_DEV_DIR); strcat(master_name, PTY_CNTRL); strcat(master_name, ext); *cntrl = open(master_name, O_RDWR); if(*cntrl < 0) continue;/* * Attempt to open the matching slave file. */ strcpy(slave_name, PTY_DEV_DIR); strcat(slave_name, PTY_SLAVE); strcat(slave_name, ext); }; }; closedir(dir);#if HAVE_SYSV_PTY };#endif/* * Did we fail to find a pseudo-terminal pair that we could open? */ if(*cntrl < 0) { fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog); return 1; };/* * System V systems require the program that opens the master to * grant access to the slave side of the pseudo-terminal. */#ifdef HAVE_SYSV_PTY if(grantpt(*cntrl) < 0 || unlockpt(*cntrl) < 0) { fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog, strerror(errno)); return 1; };#endif/* * Success. */ return 0;}/*....................................................................... * Open the slave end of a pseudo-terminal. * * Input: * prog const char * The name of this program. * slave_name char * The filename of the slave device. * Output: * return int The file descriptor of the successfully opened * slave device, or < 0 on error. */static int pty_open_slave(const char *prog, char *slave_name){ int fd; /* The file descriptor of the slave device *//* * Place the process in its own process group. In system-V based * OS's, this ensures that when the pseudo-terminal is opened, it * becomes the controlling terminal of the process. */ if(setsid() < 0) { fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog, strerror(errno)); return -1; };/* * Attempt to open the specified device. */ fd = open(slave_name, O_RDWR); if(fd < 0) { fprintf(stderr, "%s: Unable to open pseudo-terminal slave device (%s).\n", prog, strerror(errno)); return -1; };/* * On system-V streams based systems, we need to push the stream modules * that implement pseudo-terminal and termio interfaces. At least on * Solaris, which pushes these automatically when a slave is opened, * this is redundant, so ignore errors when pushing the modules. */#if HAVE_SYSV_PTY
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -