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