1 /* $OpenBSD: tty.c,v 1.18 2005/07/11 14:08:23 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.18 2005/07/11 14:08:23 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) > sizeof(canonb) - 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 sigemptyset(&act.sa_mask); 220 act.sa_flags = 0; /* Note: will not restart syscalls */ 221 act.sa_handler = ttyint; 222 (void)sigaction(SIGINT, &act, &saveint); 223 act.sa_handler = ttystop; 224 (void)sigaction(SIGTSTP, &act, NULL); 225 (void)sigaction(SIGTTOU, &act, NULL); 226 (void)sigaction(SIGTTIN, &act, NULL); 227 (void)sigprocmask(SIG_UNBLOCK, &intset, &oset); 228 clearerr(stdin); 229 memset(cp, 0, canonb + sizeof(canonb) - cp); 230 for (cp2 = cp; cp2 < canonb + sizeof(canonb) - 1; ) { 231 c = getc(stdin); 232 switch (ttysignal) { 233 case SIGINT: 234 ttysignal = 0; 235 cp2 = NULL; 236 c = EOF; 237 /* FALLTHROUGH */ 238 case 0: 239 break; 240 default: 241 ttysignal = 0; 242 goto redo; 243 } 244 if (c == EOF || c == '\n') 245 break; 246 *cp2++ = c; 247 } 248 act.sa_handler = SIG_DFL; 249 sigemptyset(&act.sa_mask); 250 act.sa_flags = SA_RESTART; 251 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 252 (void)sigaction(SIGTSTP, &act, NULL); 253 (void)sigaction(SIGTTOU, &act, NULL); 254 (void)sigaction(SIGTTIN, &act, NULL); 255 (void)sigaction(SIGINT, &saveint, NULL); 256 if (cp2 == NULL) 257 return(NULL); /* user hit ^C */ 258 *cp2 = '\0'; 259 if (c == EOF && ferror(stdin)) { 260 redo: 261 cp = strlen(canonb) > 0 ? canonb : NULL; 262 clearerr(stdin); 263 /* XXX - make iterative, not recursive */ 264 return(readtty(pr, cp)); 265 } 266 #ifndef TIOCSTI 267 if (cp == NULL || *cp == '\0') 268 return(src); 269 cp2 = cp; 270 if (!ttyset) 271 return(strlen(canonb) > 0 ? savestr(canonb) : NULL); 272 while (*cp != '\0') { 273 c = *cp++; 274 if (c_erase != _POSIX_VDISABLE && c == c_erase) { 275 if (cp2 == canonb) 276 continue; 277 if (cp2[-1] == '\\') { 278 cp2[-1] = c; 279 continue; 280 } 281 cp2--; 282 continue; 283 } 284 if (c_kill != _POSIX_VDISABLE && c == c_kill) { 285 if (cp2 == canonb) 286 continue; 287 if (cp2[-1] == '\\') { 288 cp2[-1] = c; 289 continue; 290 } 291 cp2 = canonb; 292 continue; 293 } 294 *cp2++ = c; 295 } 296 *cp2 = '\0'; 297 #endif 298 if (equal("", canonb)) 299 return(""); 300 return(savestr(canonb)); 301 } 302 303 /* 304 * Receipt continuation. 305 */ 306 void 307 ttystop(int s) 308 { 309 struct sigaction act, oact; 310 sigset_t nset; 311 int save_errno; 312 313 /* 314 * Save old handler and set to default. 315 * Unblock receipt of 's' and then resend it. 316 */ 317 save_errno = errno; 318 (void)sigemptyset(&act.sa_mask); 319 act.sa_flags = SA_RESTART; 320 act.sa_handler = SIG_DFL; 321 (void)sigaction(s, &act, &oact); 322 (void)sigemptyset(&nset); 323 (void)sigaddset(&nset, s); 324 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 325 (void)kill(0, s); 326 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 327 (void)sigaction(s, &oact, NULL); 328 ttysignal = s; 329 errno = save_errno; 330 } 331 332 /*ARGSUSED*/ 333 void 334 ttyint(int s) 335 { 336 337 ttysignal = s; 338 } 339