xref: /illumos-gate/usr/src/cmd/mailx/tty.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * University Copyright- Copyright (c) 1982, 1986, 1988
28  * The Regents of the University of California
29  * All Rights Reserved
30  *
31  * University Acknowledgment- Portions of this document are derived from
32  * software developed by the University of California, Berkeley, and its
33  * contributors.
34  */
35 
36 #pragma ident	"%Z%%M%	%I%	%E% SMI"
37 
38 /*
39  * mailx -- a modified version of a University of California at Berkeley
40  *	mail program
41  *
42  * Generally useful tty stuff.
43  */
44 
45 #include "rcv.h"
46 #include <locale.h>
47 
48 #ifdef	USG_TTY
49 
50 static char	*readtty(char pr[], char src[]);
51 static int	savetty(void);
52 static void	ttycont(int);
53 
54 static	int	c_erase;		/* Current erase char */
55 static	int	c_kill;			/* Current kill char */
56 static	int	c_intr;			/* interrupt char */
57 static	int	c_quit;			/* quit character */
58 static	struct termio savtty;
59 static	char canonb[LINESIZE];		/* canonical buffer for input */
60 					/* processing */
61 
62 #ifndef TIOCSTI
63 static void	Echo(int cc);
64 static int	countcol(void);
65 static void	outstr(register char *s);
66 static void	resetty(void);
67 static void	rubout(register char *cp);
68 static int	setty(void);
69 
70 static	int	c_word;			/* Current word erase char */
71 static	int	Col;			/* current output column */
72 static	int	Pcol;			/* end column of prompt string */
73 static	int	Out;			/* file descriptor of stdout */
74 static	int	erasing;		/* we are erasing characters */
75 static	struct termio ttybuf;
76 #else
77 static	jmp_buf	rewrite;		/* Place to go when continued */
78 #endif
79 
80 #ifdef SIGCONT
81 # ifdef preSVr4
82 typedef int	sig_atomic_t;
83 # endif
84 static	sig_atomic_t	hadcont;		/* Saw continue signal */
85 
86 /*ARGSUSED*/
87 static void
88 #ifdef	__cplusplus
89 ttycont(int)
90 #else
91 /* ARGSUSED */
92 ttycont(int s)
93 #endif
94 {
95 	hadcont++;
96 	longjmp(rewrite, 1);
97 }
98 
99 #ifndef TIOCSTI
100 /*ARGSUSED*/
101 static void
102 ttystop(int s)
103 {
104 	resetty();
105 	kill(mypid, SIGSTOP);
106 }
107 #endif
108 #endif
109 
110 /*
111  * Read all relevant header fields.
112  */
113 
114 int
115 grabh(register struct header *hp, int gflags, int subjtop)
116 {
117 #ifdef SIGCONT
118 	void (*savecont)(int);
119 #ifndef TIOCSTI
120 	void (*savestop)(int);
121 #endif
122 #endif
123 	if (savetty())
124 		return -1;
125 #ifdef SIGCONT
126 	savecont = sigset(SIGCONT, ttycont);
127 #ifndef TIOCSTI
128 	savestop = sigset(SIGTSTP, ttystop);
129 #endif
130 #endif
131 	if (gflags & GTO) {
132 		hp->h_to = addto(NOSTR, readtty("To: ", hp->h_to));
133 		if (hp->h_to != NOSTR)
134 			hp->h_seq++;
135 	}
136 	if (gflags & GSUBJECT && subjtop) {
137 		hp->h_subject = readtty("Subject: ", hp->h_subject);
138 		if (hp->h_subject != NOSTR)
139 			hp->h_seq++;
140 	}
141 	if (gflags & GCC) {
142 		hp->h_cc = addto(NOSTR, readtty("Cc: ", hp->h_cc));
143 		if (hp->h_cc != NOSTR)
144 			hp->h_seq++;
145 	}
146 	if (gflags & GBCC) {
147 		hp->h_bcc = addto(NOSTR, readtty("Bcc: ", hp->h_bcc));
148 		if (hp->h_bcc != NOSTR)
149 			hp->h_seq++;
150 	}
151 	if (gflags & GSUBJECT && !subjtop) {
152 		hp->h_subject = readtty("Subject: ", hp->h_subject);
153 		if (hp->h_subject != NOSTR)
154 			hp->h_seq++;
155 	}
156 #ifdef SIGCONT
157 	(void) sigset(SIGCONT, savecont);
158 #ifndef TIOCSTI
159 	(void) sigset(SIGTSTP, savestop);
160 #endif
161 #endif
162 	return(0);
163 }
164 
165 /*
166  * Read up a header from standard input.
167  * The source string has the preliminary contents to
168  * be read.
169  *
170  */
171 
172 static char *
173 readtty(char pr[], char src[])
174 {
175 	int c;
176 	register char *cp;
177 
178 #ifndef TIOCSTI
179 	register char *cp2;
180 
181 	erasing = 0;
182 	Col = 0;
183 	outstr(pr);
184 	Pcol = Col;
185 #else
186 	fputs(pr, stdout);
187 #endif
188 	fflush(stdout);
189 	if (src != NOSTR && (int)strlen(src) > LINESIZE - 2) {
190 		printf(gettext("too long to edit\n"));
191 		return(src);
192 	}
193 #ifndef TIOCSTI
194 	if (setty())
195 		return(src);
196 	cp2 = src==NOSTR ? "" : src;
197 	for (cp=canonb; *cp2; cp++, cp2++)
198 		*cp = *cp2;
199 	*cp = '\0';
200 	outstr(canonb);
201 #else
202 	cp = src == NOSTR ? "" : src;
203 	while (c = *cp++) {
204 		char ch;
205 
206 		if (c == c_erase || c == c_kill) {
207 			ch = '\\';
208 			ioctl(0, TIOCSTI, &ch);
209 		}
210 		ch = c;
211 		ioctl(0, TIOCSTI, &ch);
212 	}
213 	cp = canonb;
214 	*cp = 0;
215 	if (setjmp(rewrite))
216 		goto redo;
217 #endif
218 
219 	for (;;) {
220 		fflush(stdout);
221 #ifdef SIGCONT
222 		hadcont = 0;
223 #endif
224 		c = getc(stdin);
225 
226 #ifndef TIOCSTI
227 		if (c==c_erase) {
228 			if (cp > canonb)
229 				if (cp[-1]=='\\' && !erasing) {
230 					*cp++ = (char)c;
231 					Echo(c);
232 				} else {
233 					rubout(--cp);
234 				}
235 		} else if (c==c_kill) {
236 			if (cp > canonb && cp[-1]=='\\') {
237 				*cp++ = (char)c;
238 				Echo(c);
239 			} else while (cp > canonb) {
240 				rubout(--cp);
241 			}
242 		} else if (c==c_word) {
243 			if (cp > canonb)
244 				if (cp[-1]=='\\' && !erasing) {
245 					*cp++ = (char)c;
246 					Echo(c);
247 				} else {
248 					while (--cp >= canonb)
249 						if (!isspace(*cp))
250 							break;
251 						else
252 							rubout(cp);
253 					while (cp >= canonb)
254 						if (!isspace(*cp))
255 							rubout(cp--);
256 						else
257 							break;
258 					if (cp < canonb)
259 						cp = canonb;
260 					else if (*cp)
261 						cp++;
262 				}
263 		} else
264 #endif
265 		if (c==EOF || ferror(stdin) || c==c_intr || c==c_quit) {
266 #ifdef SIGCONT
267 			if (hadcont) {
268 #ifndef TIOCSTI
269 				(void) setty();
270 				outstr("(continue)\n");
271 				Col = 0;
272 				outstr(pr);
273 				*cp = '\0';
274 				outstr(canonb);
275 				clearerr(stdin);
276 				continue;
277 #else
278 			redo:
279 				hadcont = 0;
280 				cp = canonb[0] != 0 ? canonb : src;
281 				clearerr(stdin);
282 				return(readtty(pr, cp));
283 #endif
284 			}
285 #endif
286 #ifndef TIOCSTI
287 			resetty();
288 #endif
289 			savedead(c==c_quit? SIGQUIT: SIGINT);
290 		} else switch (c) {
291 			case '\n':
292 			case '\r':
293 #ifndef TIOCSTI
294 				resetty();
295 				putchar('\n');
296 				fflush(stdout);
297 #endif
298 				if (canonb[0]=='\0')
299 					return(NOSTR);
300 				return(savestr(canonb));
301 			default:
302 				*cp++ = (char)c;
303 				*cp = '\0';
304 #ifndef TIOCSTI
305 				erasing = 0;
306 				Echo(c);
307 #endif
308 		}
309 	}
310 }
311 
312 static int
313 savetty(void)
314 {
315 	if (ioctl(fileno(stdout), TCGETA, &savtty) < 0)
316 	{	perror("ioctl");
317 		return(-1);
318 	}
319 	c_erase = savtty.c_cc[VERASE];
320 	c_kill = savtty.c_cc[VKILL];
321 	c_intr = savtty.c_cc[VINTR];
322 	c_quit = savtty.c_cc[VQUIT];
323 #ifndef TIOCSTI
324 	c_word = 'W' & 037;	/* erase word character */
325 	Out = fileno(stdout);
326 	ttybuf = savtty;
327 #ifdef	u370
328 	ttybuf.c_cflag &= ~PARENB;	/* disable parity */
329 	ttybuf.c_cflag |= CS8;		/* character size = 8 */
330 #endif	/* u370 */
331 	ttybuf.c_cc[VTIME] = 0;
332 	ttybuf.c_cc[VMIN] = 1;
333 	ttybuf.c_iflag &= ~(BRKINT);
334 	ttybuf.c_lflag &= ~(ICANON|ISIG|ECHO);
335 #endif
336 	return 0;
337 }
338 
339 #ifndef TIOCSTI
340 static int
341 setty(void)
342 {
343 	if (ioctl(Out, TCSETAW, &ttybuf) < 0) {
344 		perror("ioctl");
345 		return(-1);
346 	}
347 	return(0);
348 }
349 
350 static void
351 resetty(void)
352 {
353 	if (ioctl(Out, TCSETAW, &savtty) < 0)
354 		perror("ioctl");
355 }
356 
357 static void
358 outstr(register char *s)
359 {
360 	while (*s)
361 		Echo(*s++);
362 }
363 
364 static void
365 rubout(register char *cp)
366 {
367 	register int oldcol;
368 	register int c = *cp;
369 
370 	erasing = 1;
371 	*cp = '\0';
372 	switch (c) {
373 	case '\t':
374 		oldcol = countcol();
375 		do
376 			putchar('\b');
377 		while (--Col > oldcol);
378 		break;
379 	case '\b':
380 		if (isprint(cp[-1]))
381 			putchar(*(cp-1));
382 		else
383 			putchar(' ');
384 		Col++;
385 		break;
386 	default:
387 		if (isprint(c)) {
388 			fputs("\b \b", stdout);
389 			Col--;
390 		}
391 	}
392 }
393 
394 static int
395 countcol(void)
396 {
397 	register int col;
398 	register char *s;
399 
400 	for (col=Pcol, s=canonb; *s; s++)
401 		switch (*s) {
402 		case '\t':
403 			while (++col % 8)
404 				;
405 			break;
406 		case '\b':
407 			col--;
408 			break;
409 		default:
410 			if (isprint(*s))
411 				col++;
412 		}
413 	return(col);
414 }
415 
416 static void
417 Echo(int cc)
418 {
419 	char c = (char)cc;
420 
421 	switch (c) {
422 	case '\t':
423 		do
424 			putchar(' ');
425 		while (++Col % 8);
426 		break;
427 	case '\b':
428 		if (Col > 0) {
429 			putchar('\b');
430 			Col--;
431 		}
432 		break;
433 	case '\r':
434 	case '\n':
435 		Col = 0;
436 		fputs("\r\n", stdout);
437 		break;
438 	default:
439 		if (isprint(c)) {
440 			Col++;
441 			putchar(c);
442 		}
443 	}
444 }
445 #endif
446 
447 #else
448 
449 #ifdef SIGCONT
450 static void	signull(int);
451 #endif
452 
453 static	int	c_erase;		/* Current erase char */
454 static	int	c_kill;			/* Current kill char */
455 static	int	hadcont;		/* Saw continue signal */
456 static	jmp_buf	rewrite;		/* Place to go when continued */
457 #ifndef TIOCSTI
458 static	int	ttyset;			/* We must now do erase/kill */
459 #endif
460 
461 /*
462  * Read all relevant header fields.
463  */
464 
465 int
466 grabh(struct header *hp, int gflags, int subjtop)
467 {
468 	struct sgttyb ttybuf;
469 	void (*savecont)(int);
470 	register int s;
471 	int errs;
472 #ifndef TIOCSTI
473 	void (*savesigs[2])(int);
474 #endif
475 
476 #ifdef SIGCONT
477 	savecont = sigset(SIGCONT, signull);
478 #endif
479 	errs = 0;
480 #ifndef TIOCSTI
481 	ttyset = 0;
482 #endif
483 	if (gtty(fileno(stdin), &ttybuf) < 0) {
484 		perror("gtty");
485 		return(-1);
486 	}
487 	c_erase = ttybuf.sg_erase;
488 	c_kill = ttybuf.sg_kill;
489 #ifndef TIOCSTI
490 	ttybuf.sg_erase = 0;
491 	ttybuf.sg_kill = 0;
492 	for (s = SIGINT; s <= SIGQUIT; s++)
493 		if ((savesigs[s-SIGINT] = sigset(s, SIG_IGN)) == SIG_DFL)
494 			sigset(s, SIG_DFL);
495 #endif
496 	if (gflags & GTO) {
497 #ifndef TIOCSTI
498 		if (!ttyset && hp->h_to != NOSTR)
499 			ttyset++, stty(fileno(stdin), &ttybuf);
500 #endif
501 		hp->h_to = addto(NOSTR, readtty("To: ", hp->h_to));
502 		if (hp->h_to != NOSTR)
503 			hp->h_seq++;
504 	}
505 	if (gflags & GSUBJECT && subjtop) {
506 #ifndef TIOCSTI
507 		if (!ttyset && hp->h_subject != NOSTR)
508 			ttyset++, stty(fileno(stdin), &ttybuf);
509 #endif
510 		hp->h_subject = readtty("Subject: ", hp->h_subject);
511 		if (hp->h_subject != NOSTR)
512 			hp->h_seq++;
513 	}
514 	if (gflags & GCC) {
515 #ifndef TIOCSTI
516 		if (!ttyset && hp->h_cc != NOSTR)
517 			ttyset++, stty(fileno(stdin), &ttybuf);
518 #endif
519 		hp->h_cc = addto(NOSTR, readtty("Cc: ", hp->h_cc));
520 		if (hp->h_cc != NOSTR)
521 			hp->h_seq++;
522 	}
523 	if (gflags & GBCC) {
524 #ifndef TIOCSTI
525 		if (!ttyset && hp->h_bcc != NOSTR)
526 			ttyset++, stty(fileno(stdin), &ttybuf);
527 #endif
528 		hp->h_bcc = addto(NOSTR, readtty("Bcc: ", hp->h_bcc));
529 		if (hp->h_bcc != NOSTR)
530 			hp->h_seq++;
531 	}
532 	if (gflags & GSUBJECT && !subjtop) {
533 #ifndef TIOCSTI
534 		if (!ttyset && hp->h_subject != NOSTR)
535 			ttyset++, stty(fileno(stdin), &ttybuf);
536 #endif
537 		hp->h_subject = readtty("Subject: ", hp->h_subject);
538 		if (hp->h_subject != NOSTR)
539 			hp->h_seq++;
540 	}
541 #ifdef SIGCONT
542 	sigset(SIGCONT, savecont);
543 #endif
544 #ifndef TIOCSTI
545 	ttybuf.sg_erase = c_erase;
546 	ttybuf.sg_kill = c_kill;
547 	if (ttyset)
548 		stty(fileno(stdin), &ttybuf);
549 	for (s = SIGINT; s <= SIGQUIT; s++)
550 		sigset(s, savesigs[s-SIGINT]);
551 #endif
552 	return(errs);
553 }
554 
555 /*
556  * Read up a header from standard input.
557  * The source string has the preliminary contents to
558  * be read.
559  *
560  */
561 
562 char *
563 readtty(char pr[], char src[])
564 {
565 	char ch, canonb[LINESIZE];
566 	int c;
567 	register char *cp, *cp2;
568 
569 	fputs(pr, stdout);
570 	fflush(stdout);
571 	if (src != NOSTR && strlen(src) > LINESIZE - 2) {
572 		printf(gettext("too long to edit\n"));
573 		return(src);
574 	}
575 #ifndef TIOCSTI
576 	if (src != NOSTR)
577 		cp = copy(src, canonb);
578 	else
579 		cp = copy("", canonb);
580 	fputs(canonb, stdout);
581 	fflush(stdout);
582 #else
583 	cp = src == NOSTR ? "" : src;
584 	while (c = *cp++) {
585 		if (c == c_erase || c == c_kill) {
586 			ch = '\\';
587 			ioctl(0, TIOCSTI, &ch);
588 		}
589 		ch = c;
590 		ioctl(0, TIOCSTI, &ch);
591 	}
592 	cp = canonb;
593 	*cp = 0;
594 #endif
595 	cp2 = cp;
596 	while (cp2 < canonb + LINESIZE)
597 		*cp2++ = 0;
598 	cp2 = cp;
599 	if (setjmp(rewrite))
600 		goto redo;
601 #ifdef SIGCONT
602 	sigset(SIGCONT, ttycont);
603 #endif
604 	clearerr(stdin);
605 	while (cp2 < canonb + LINESIZE) {
606 		c = getc(stdin);
607 		if (c == EOF || c == '\n')
608 			break;
609 		*cp2++ = c;
610 	}
611 	*cp2 = 0;
612 #ifdef SIGCONT
613 	sigset(SIGCONT, signull);
614 #endif
615 	if (c == EOF && ferror(stdin) && hadcont) {
616 redo:
617 		hadcont = 0;
618 		cp = strlen(canonb) > 0 ? canonb : NOSTR;
619 		clearerr(stdin);
620 		return(readtty(pr, cp));
621 	}
622 	clearerr(stdin);
623 #ifndef TIOCSTI
624 	if (cp == NOSTR || *cp == '\0')
625 		return(src);
626 	cp2 = cp;
627 	if (!ttyset)
628 		return(strlen(canonb) > 0 ? savestr(canonb) : NOSTR);
629 	while (*cp != '\0') {
630 		c = *cp++;
631 		if (c == c_erase) {
632 			if (cp2 == canonb)
633 				continue;
634 			if (cp2[-1] == '\\') {
635 				cp2[-1] = c;
636 				continue;
637 			}
638 			cp2--;
639 			continue;
640 		}
641 		if (c == c_kill) {
642 			if (cp2 == canonb)
643 				continue;
644 			if (cp2[-1] == '\\') {
645 				cp2[-1] = c;
646 				continue;
647 			}
648 			cp2 = canonb;
649 			continue;
650 		}
651 		*cp2++ = c;
652 	}
653 	*cp2 = '\0';
654 #endif
655 	if (equal("", canonb))
656 		return(NOSTR);
657 	return(savestr(canonb));
658 }
659 
660 #ifdef SIGCONT
661 /*
662  * Receipt continuation.
663  */
664 /*ARGSUSED*/
665 void
666 ttycont(int)
667 {
668 
669 	hadcont++;
670 	longjmp(rewrite, 1);
671 }
672 
673 /*
674  * Null routine to allow us to hold SIGCONT
675  */
676 /*ARGSUSED*/
677 static void
678 signull(int)
679 {}
680 #endif
681 #endif	/* USG_TTY */
682