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