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
ttycont(int)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
ttystop(int s)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
grabh(register struct header * hp,int gflags,int subjtop)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 *
readtty(char pr[],char src[])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
savetty(void)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
setty(void)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
resetty(void)351 resetty(void)
352 {
353 if (ioctl(Out, TCSETAW, &savtty) < 0)
354 perror("ioctl");
355 }
356
357 static void
outstr(register char * s)358 outstr(register char *s)
359 {
360 while (*s)
361 Echo(*s++);
362 }
363
364 static void
rubout(register char * cp)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
countcol(void)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
Echo(int cc)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
grabh(struct header * hp,int gflags,int subjtop)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 *
readtty(char pr[],char src[])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
ttycont(int)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
signull(int)678 signull(int)
679 {}
680 #endif
681 #endif /* USG_TTY */
682