1 /* $OpenBSD: tty.c,v 1.16 2001/11/28 01:04:34 millert Exp $ */ 2 /* $NetBSD: tty.c,v 1.7 1997/07/09 05:25:46 mikel Exp $ */ 3 4 /* 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static const char sccsid[] = "@(#)tty.c 8.2 (Berkeley) 4/20/95"; 40 #else 41 static const char rcsid[] = "$OpenBSD: tty.c,v 1.16 2001/11/28 01:04:34 millert Exp $"; 42 #endif 43 #endif /* not lint */ 44 45 /* 46 * Mail -- a mail program 47 * 48 * Generally useful tty stuff. 49 */ 50 51 #include "rcv.h" 52 #include "extern.h" 53 #include <sys/ioctl.h> 54 #include <errno.h> 55 56 static cc_t c_erase; /* Current erase char */ 57 static cc_t c_kill; /* Current kill char */ 58 #ifndef TIOCSTI 59 static int ttyset; /* We must now do erase/kill */ 60 #endif 61 static volatile sig_atomic_t ttysignal; /* Interrupted by a signal? */ 62 63 /* 64 * Read all relevant header fields. 65 */ 66 int 67 grabh(struct header *hp, int gflags) 68 { 69 struct termios ttybuf; 70 #ifndef TIOCSTI 71 struct sigaction savequit; 72 #else 73 # ifdef TIOCEXT 74 int extproc; 75 int flag; 76 # endif /* TIOCEXT */ 77 #endif 78 struct sigaction savetstp; 79 struct sigaction savettou; 80 struct sigaction savettin; 81 struct sigaction act; 82 char *s; 83 int error; 84 85 sigemptyset(&act.sa_mask); 86 act.sa_flags = SA_RESTART; 87 act.sa_handler = SIG_DFL; 88 (void)sigaction(SIGTSTP, &act, &savetstp); 89 (void)sigaction(SIGTTOU, &act, &savettou); 90 (void)sigaction(SIGTTIN, &act, &savettin); 91 error = 1; 92 #ifndef TIOCSTI 93 ttyset = 0; 94 #endif 95 if (tcgetattr(fileno(stdin), &ttybuf) < 0) { 96 warn("tcgetattr"); 97 return(-1); 98 } 99 c_erase = ttybuf.c_cc[VERASE]; 100 c_kill = ttybuf.c_cc[VKILL]; 101 #ifndef TIOCSTI 102 ttybuf.c_cc[VERASE] = 0; 103 ttybuf.c_cc[VKILL] = 0; 104 act.sa_handler = SIG_IGN; 105 if (sigaction(SIGQUIT, &act, &savequit) == 0 && 106 savequit.sa_handler == SIG_DFL) 107 (void)sigaction(SIGQUIT, &savequit, NULL); 108 #else 109 # ifdef TIOCEXT 110 extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0); 111 if (extproc) { 112 flag = 0; 113 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) 114 warn("TIOCEXT: off"); 115 } 116 # endif /* TIOCEXT */ 117 #endif 118 if (gflags & GTO) { 119 #ifndef TIOCSTI 120 if (!ttyset && hp->h_to != NULL) 121 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 122 #endif 123 s = readtty("To: ", detract(hp->h_to, 0)); 124 if (s == NULL) 125 goto out; 126 hp->h_to = extract(s, GTO); 127 } 128 if (gflags & GSUBJECT) { 129 #ifndef TIOCSTI 130 if (!ttyset && hp->h_subject != NULL) 131 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 132 #endif 133 s = readtty("Subject: ", hp->h_subject); 134 if (s == NULL) 135 goto out; 136 hp->h_subject = s; 137 } 138 if (gflags & GCC) { 139 #ifndef TIOCSTI 140 if (!ttyset && hp->h_cc != NULL) 141 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 142 #endif 143 s = readtty("Cc: ", detract(hp->h_cc, 0)); 144 if (s == NULL) 145 goto out; 146 hp->h_cc = extract(s, GCC); 147 } 148 if (gflags & GBCC) { 149 #ifndef TIOCSTI 150 if (!ttyset && hp->h_bcc != NULL) 151 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 152 #endif 153 s = readtty("Bcc: ", detract(hp->h_bcc, 0)); 154 if (s == NULL) 155 goto out; 156 hp->h_bcc = extract(s, GBCC); 157 } 158 error = 0; 159 out: 160 (void)sigaction(SIGTSTP, &savetstp, NULL); 161 (void)sigaction(SIGTTOU, &savettou, NULL); 162 (void)sigaction(SIGTTIN, &savettin, NULL); 163 #ifndef TIOCSTI 164 ttybuf.c_cc[VERASE] = c_erase; 165 ttybuf.c_cc[VKILL] = c_kill; 166 if (ttyset) 167 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 168 (void)sigaction(SIGQUIT, &savequit, NULL); 169 #else 170 # ifdef TIOCEXT 171 if (extproc) { 172 flag = 1; 173 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) 174 warn("TIOCEXT: on"); 175 } 176 # endif /* TIOCEXT */ 177 #endif 178 return(error); 179 } 180 181 /* 182 * Read up a header from standard input. 183 * The source string has the preliminary contents to 184 * be read. 185 * 186 */ 187 char * 188 readtty(char *pr, char *src) 189 { 190 struct sigaction act, saveint; 191 char ch, canonb[BUFSIZ]; 192 char *cp, *cp2; 193 sigset_t oset; 194 int c; 195 196 fputs(pr, stdout); 197 fflush(stdout); 198 if (src != NULL && strlen(src) > BUFSIZ - 2) { 199 puts("too long to edit"); 200 return(src); 201 } 202 #ifndef TIOCSTI 203 if (src != NULL) 204 cp = copy(src, canonb); /* safe, bounds checked above */ 205 else 206 cp = copy("", canonb); 207 fputs(canonb, stdout); 208 fflush(stdout); 209 #else 210 cp = src == NULL ? "" : src; 211 while ((c = *cp++) != '\0') { 212 if ((c_erase != _POSIX_VDISABLE && c == c_erase) || 213 (c_kill != _POSIX_VDISABLE && c == c_kill)) { 214 ch = '\\'; 215 ioctl(0, TIOCSTI, &ch); 216 } 217 ch = c; 218 ioctl(0, TIOCSTI, &ch); 219 } 220 cp = canonb; 221 *cp = 0; 222 #endif 223 cp2 = cp; 224 while (cp2 < canonb + BUFSIZ) 225 *cp2++ = 0; 226 cp2 = cp; 227 sigemptyset(&act.sa_mask); 228 act.sa_flags = 0; /* Note: will not restart syscalls */ 229 act.sa_handler = ttyint; 230 (void)sigaction(SIGINT, &act, &saveint); 231 act.sa_handler = ttystop; 232 (void)sigaction(SIGTSTP, &act, NULL); 233 (void)sigaction(SIGTTOU, &act, NULL); 234 (void)sigaction(SIGTTIN, &act, NULL); 235 (void)sigprocmask(SIG_UNBLOCK, &intset, &oset); 236 clearerr(stdin); 237 while (cp2 < canonb + BUFSIZ) { 238 c = getc(stdin); 239 switch (ttysignal) { 240 case SIGINT: 241 ttysignal = 0; 242 cp2 = NULL; 243 c = EOF; 244 /* FALLTHROUGH */ 245 case 0: 246 break; 247 default: 248 ttysignal = 0; 249 goto redo; 250 } 251 if (c == EOF || c == '\n') 252 break; 253 *cp2++ = c; 254 } 255 act.sa_handler = SIG_DFL; 256 sigemptyset(&act.sa_mask); 257 act.sa_flags = SA_RESTART; 258 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 259 (void)sigaction(SIGTSTP, &act, NULL); 260 (void)sigaction(SIGTTOU, &act, NULL); 261 (void)sigaction(SIGTTIN, &act, NULL); 262 (void)sigaction(SIGINT, &saveint, NULL); 263 if (cp2 == NULL) 264 return(NULL); /* user hit ^C */ 265 *cp2 = '\0'; 266 if (c == EOF && ferror(stdin)) { 267 redo: 268 cp = strlen(canonb) > 0 ? canonb : NULL; 269 clearerr(stdin); 270 /* XXX - make iterative, not recursive */ 271 return(readtty(pr, cp)); 272 } 273 #ifndef TIOCSTI 274 if (cp == NULL || *cp == '\0') 275 return(src); 276 cp2 = cp; 277 if (!ttyset) 278 return(strlen(canonb) > 0 ? savestr(canonb) : NULL); 279 while (*cp != '\0') { 280 c = *cp++; 281 if (c_erase != _POSIX_VDISABLE && c == c_erase) { 282 if (cp2 == canonb) 283 continue; 284 if (cp2[-1] == '\\') { 285 cp2[-1] = c; 286 continue; 287 } 288 cp2--; 289 continue; 290 } 291 if (c_kill != _POSIX_VDISABLE && c == c_kill) { 292 if (cp2 == canonb) 293 continue; 294 if (cp2[-1] == '\\') { 295 cp2[-1] = c; 296 continue; 297 } 298 cp2 = canonb; 299 continue; 300 } 301 *cp2++ = c; 302 } 303 *cp2 = '\0'; 304 #endif 305 if (equal("", canonb)) 306 return(""); 307 return(savestr(canonb)); 308 } 309 310 /* 311 * Receipt continuation. 312 */ 313 void 314 ttystop(int s) 315 { 316 struct sigaction act, oact; 317 sigset_t nset; 318 int save_errno; 319 320 /* 321 * Save old handler and set to default. 322 * Unblock receipt of 's' and then resend it. 323 */ 324 save_errno = errno; 325 (void)sigemptyset(&act.sa_mask); 326 act.sa_flags = SA_RESTART; 327 act.sa_handler = SIG_DFL; 328 (void)sigaction(s, &act, &oact); 329 (void)sigemptyset(&nset); 330 (void)sigaddset(&nset, s); 331 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 332 (void)kill(0, s); 333 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 334 (void)sigaction(s, &oact, NULL); 335 ttysignal = s; 336 errno = save_errno; 337 } 338 339 /*ARGSUSED*/ 340 void 341 ttyint(int s) 342 { 343 344 ttysignal = s; 345 } 346