/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Generally useful tty stuff. */ #include "rcv.h" #include #ifdef USG_TTY static char *readtty(char pr[], char src[]); static int savetty(void); static void ttycont(int); static int c_erase; /* Current erase char */ static int c_kill; /* Current kill char */ static int c_intr; /* interrupt char */ static int c_quit; /* quit character */ static struct termio savtty; static char canonb[LINESIZE]; /* canonical buffer for input */ /* processing */ #ifndef TIOCSTI static void Echo(int cc); static int countcol(void); static void outstr(register char *s); static void resetty(void); static void rubout(register char *cp); static int setty(void); static int c_word; /* Current word erase char */ static int Col; /* current output column */ static int Pcol; /* end column of prompt string */ static int Out; /* file descriptor of stdout */ static int erasing; /* we are erasing characters */ static struct termio ttybuf; #else static jmp_buf rewrite; /* Place to go when continued */ #endif #ifdef SIGCONT # ifdef preSVr4 typedef int sig_atomic_t; # endif static sig_atomic_t hadcont; /* Saw continue signal */ /*ARGSUSED*/ static void #ifdef __cplusplus ttycont(int) #else /* ARGSUSED */ ttycont(int s) #endif { hadcont++; longjmp(rewrite, 1); } #ifndef TIOCSTI /*ARGSUSED*/ static void ttystop(int s) { resetty(); kill(mypid, SIGSTOP); } #endif #endif /* * Read all relevant header fields. */ int grabh(register struct header *hp, int gflags, int subjtop) { #ifdef SIGCONT void (*savecont)(int); #ifndef TIOCSTI void (*savestop)(int); #endif #endif if (savetty()) return -1; #ifdef SIGCONT savecont = sigset(SIGCONT, ttycont); #ifndef TIOCSTI savestop = sigset(SIGTSTP, ttystop); #endif #endif if (gflags & GTO) { hp->h_to = addto(NOSTR, readtty("To: ", hp->h_to)); if (hp->h_to != NOSTR) hp->h_seq++; } if (gflags & GSUBJECT && subjtop) { hp->h_subject = readtty("Subject: ", hp->h_subject); if (hp->h_subject != NOSTR) hp->h_seq++; } if (gflags & GCC) { hp->h_cc = addto(NOSTR, readtty("Cc: ", hp->h_cc)); if (hp->h_cc != NOSTR) hp->h_seq++; } if (gflags & GBCC) { hp->h_bcc = addto(NOSTR, readtty("Bcc: ", hp->h_bcc)); if (hp->h_bcc != NOSTR) hp->h_seq++; } if (gflags & GSUBJECT && !subjtop) { hp->h_subject = readtty("Subject: ", hp->h_subject); if (hp->h_subject != NOSTR) hp->h_seq++; } #ifdef SIGCONT (void) sigset(SIGCONT, savecont); #ifndef TIOCSTI (void) sigset(SIGTSTP, savestop); #endif #endif return(0); } /* * Read up a header from standard input. * The source string has the preliminary contents to * be read. * */ static char * readtty(char pr[], char src[]) { int c; register char *cp; #ifndef TIOCSTI register char *cp2; erasing = 0; Col = 0; outstr(pr); Pcol = Col; #else fputs(pr, stdout); #endif fflush(stdout); if (src != NOSTR && (int)strlen(src) > LINESIZE - 2) { printf(gettext("too long to edit\n")); return(src); } #ifndef TIOCSTI if (setty()) return(src); cp2 = src==NOSTR ? "" : src; for (cp=canonb; *cp2; cp++, cp2++) *cp = *cp2; *cp = '\0'; outstr(canonb); #else cp = src == NOSTR ? "" : src; while (c = *cp++) { char ch; if (c == c_erase || c == c_kill) { ch = '\\'; ioctl(0, TIOCSTI, &ch); } ch = c; ioctl(0, TIOCSTI, &ch); } cp = canonb; *cp = 0; if (setjmp(rewrite)) goto redo; #endif for (;;) { fflush(stdout); #ifdef SIGCONT hadcont = 0; #endif c = getc(stdin); #ifndef TIOCSTI if (c==c_erase) { if (cp > canonb) if (cp[-1]=='\\' && !erasing) { *cp++ = (char)c; Echo(c); } else { rubout(--cp); } } else if (c==c_kill) { if (cp > canonb && cp[-1]=='\\') { *cp++ = (char)c; Echo(c); } else while (cp > canonb) { rubout(--cp); } } else if (c==c_word) { if (cp > canonb) if (cp[-1]=='\\' && !erasing) { *cp++ = (char)c; Echo(c); } else { while (--cp >= canonb) if (!isspace(*cp)) break; else rubout(cp); while (cp >= canonb) if (!isspace(*cp)) rubout(cp--); else break; if (cp < canonb) cp = canonb; else if (*cp) cp++; } } else #endif if (c==EOF || ferror(stdin) || c==c_intr || c==c_quit) { #ifdef SIGCONT if (hadcont) { #ifndef TIOCSTI (void) setty(); outstr("(continue)\n"); Col = 0; outstr(pr); *cp = '\0'; outstr(canonb); clearerr(stdin); continue; #else redo: hadcont = 0; cp = canonb[0] != 0 ? canonb : src; clearerr(stdin); return(readtty(pr, cp)); #endif } #endif #ifndef TIOCSTI resetty(); #endif savedead(c==c_quit? SIGQUIT: SIGINT); } else switch (c) { case '\n': case '\r': #ifndef TIOCSTI resetty(); putchar('\n'); fflush(stdout); #endif if (canonb[0]=='\0') return(NOSTR); return(savestr(canonb)); default: *cp++ = (char)c; *cp = '\0'; #ifndef TIOCSTI erasing = 0; Echo(c); #endif } } } static int savetty(void) { if (ioctl(fileno(stdout), TCGETA, &savtty) < 0) { perror("ioctl"); return(-1); } c_erase = savtty.c_cc[VERASE]; c_kill = savtty.c_cc[VKILL]; c_intr = savtty.c_cc[VINTR]; c_quit = savtty.c_cc[VQUIT]; #ifndef TIOCSTI c_word = 'W' & 037; /* erase word character */ Out = fileno(stdout); ttybuf = savtty; #ifdef u370 ttybuf.c_cflag &= ~PARENB; /* disable parity */ ttybuf.c_cflag |= CS8; /* character size = 8 */ #endif /* u370 */ ttybuf.c_cc[VTIME] = 0; ttybuf.c_cc[VMIN] = 1; ttybuf.c_iflag &= ~(BRKINT); ttybuf.c_lflag &= ~(ICANON|ISIG|ECHO); #endif return 0; } #ifndef TIOCSTI static int setty(void) { if (ioctl(Out, TCSETAW, &ttybuf) < 0) { perror("ioctl"); return(-1); } return(0); } static void resetty(void) { if (ioctl(Out, TCSETAW, &savtty) < 0) perror("ioctl"); } static void outstr(register char *s) { while (*s) Echo(*s++); } static void rubout(register char *cp) { register int oldcol; register int c = *cp; erasing = 1; *cp = '\0'; switch (c) { case '\t': oldcol = countcol(); do putchar('\b'); while (--Col > oldcol); break; case '\b': if (isprint(cp[-1])) putchar(*(cp-1)); else putchar(' '); Col++; break; default: if (isprint(c)) { fputs("\b \b", stdout); Col--; } } } static int countcol(void) { register int col; register char *s; for (col=Pcol, s=canonb; *s; s++) switch (*s) { case '\t': while (++col % 8) ; break; case '\b': col--; break; default: if (isprint(*s)) col++; } return(col); } static void Echo(int cc) { char c = (char)cc; switch (c) { case '\t': do putchar(' '); while (++Col % 8); break; case '\b': if (Col > 0) { putchar('\b'); Col--; } break; case '\r': case '\n': Col = 0; fputs("\r\n", stdout); break; default: if (isprint(c)) { Col++; putchar(c); } } } #endif #else #ifdef SIGCONT static void signull(int); #endif static int c_erase; /* Current erase char */ static int c_kill; /* Current kill char */ static int hadcont; /* Saw continue signal */ static jmp_buf rewrite; /* Place to go when continued */ #ifndef TIOCSTI static int ttyset; /* We must now do erase/kill */ #endif /* * Read all relevant header fields. */ int grabh(struct header *hp, int gflags, int subjtop) { struct sgttyb ttybuf; void (*savecont)(int); register int s; int errs; #ifndef TIOCSTI void (*savesigs[2])(int); #endif #ifdef SIGCONT savecont = sigset(SIGCONT, signull); #endif errs = 0; #ifndef TIOCSTI ttyset = 0; #endif if (gtty(fileno(stdin), &ttybuf) < 0) { perror("gtty"); return(-1); } c_erase = ttybuf.sg_erase; c_kill = ttybuf.sg_kill; #ifndef TIOCSTI ttybuf.sg_erase = 0; ttybuf.sg_kill = 0; for (s = SIGINT; s <= SIGQUIT; s++) if ((savesigs[s-SIGINT] = sigset(s, SIG_IGN)) == SIG_DFL) sigset(s, SIG_DFL); #endif if (gflags & GTO) { #ifndef TIOCSTI if (!ttyset && hp->h_to != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_to = addto(NOSTR, readtty("To: ", hp->h_to)); if (hp->h_to != NOSTR) hp->h_seq++; } if (gflags & GSUBJECT && subjtop) { #ifndef TIOCSTI if (!ttyset && hp->h_subject != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_subject = readtty("Subject: ", hp->h_subject); if (hp->h_subject != NOSTR) hp->h_seq++; } if (gflags & GCC) { #ifndef TIOCSTI if (!ttyset && hp->h_cc != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_cc = addto(NOSTR, readtty("Cc: ", hp->h_cc)); if (hp->h_cc != NOSTR) hp->h_seq++; } if (gflags & GBCC) { #ifndef TIOCSTI if (!ttyset && hp->h_bcc != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_bcc = addto(NOSTR, readtty("Bcc: ", hp->h_bcc)); if (hp->h_bcc != NOSTR) hp->h_seq++; } if (gflags & GSUBJECT && !subjtop) { #ifndef TIOCSTI if (!ttyset && hp->h_subject != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_subject = readtty("Subject: ", hp->h_subject); if (hp->h_subject != NOSTR) hp->h_seq++; } #ifdef SIGCONT sigset(SIGCONT, savecont); #endif #ifndef TIOCSTI ttybuf.sg_erase = c_erase; ttybuf.sg_kill = c_kill; if (ttyset) stty(fileno(stdin), &ttybuf); for (s = SIGINT; s <= SIGQUIT; s++) sigset(s, savesigs[s-SIGINT]); #endif return(errs); } /* * Read up a header from standard input. * The source string has the preliminary contents to * be read. * */ char * readtty(char pr[], char src[]) { char ch, canonb[LINESIZE]; int c; register char *cp, *cp2; fputs(pr, stdout); fflush(stdout); if (src != NOSTR && strlen(src) > LINESIZE - 2) { printf(gettext("too long to edit\n")); return(src); } #ifndef TIOCSTI if (src != NOSTR) cp = copy(src, canonb); else cp = copy("", canonb); fputs(canonb, stdout); fflush(stdout); #else cp = src == NOSTR ? "" : src; while (c = *cp++) { if (c == c_erase || c == c_kill) { ch = '\\'; ioctl(0, TIOCSTI, &ch); } ch = c; ioctl(0, TIOCSTI, &ch); } cp = canonb; *cp = 0; #endif cp2 = cp; while (cp2 < canonb + LINESIZE) *cp2++ = 0; cp2 = cp; if (setjmp(rewrite)) goto redo; #ifdef SIGCONT sigset(SIGCONT, ttycont); #endif clearerr(stdin); while (cp2 < canonb + LINESIZE) { c = getc(stdin); if (c == EOF || c == '\n') break; *cp2++ = c; } *cp2 = 0; #ifdef SIGCONT sigset(SIGCONT, signull); #endif if (c == EOF && ferror(stdin) && hadcont) { redo: hadcont = 0; cp = strlen(canonb) > 0 ? canonb : NOSTR; clearerr(stdin); return(readtty(pr, cp)); } clearerr(stdin); #ifndef TIOCSTI if (cp == NOSTR || *cp == '\0') return(src); cp2 = cp; if (!ttyset) return(strlen(canonb) > 0 ? savestr(canonb) : NOSTR); while (*cp != '\0') { c = *cp++; if (c == c_erase) { if (cp2 == canonb) continue; if (cp2[-1] == '\\') { cp2[-1] = c; continue; } cp2--; continue; } if (c == c_kill) { if (cp2 == canonb) continue; if (cp2[-1] == '\\') { cp2[-1] = c; continue; } cp2 = canonb; continue; } *cp2++ = c; } *cp2 = '\0'; #endif if (equal("", canonb)) return(NOSTR); return(savestr(canonb)); } #ifdef SIGCONT /* * Receipt continuation. */ /*ARGSUSED*/ void ttycont(int) { hadcont++; longjmp(rewrite, 1); } /* * Null routine to allow us to hold SIGCONT */ /*ARGSUSED*/ static void signull(int) {} #endif #endif /* USG_TTY */