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