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