1 /* $OpenBSD: tty.c,v 1.19 2009/10/27 23:59:40 deraadt 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 /* 34 * Mail -- a mail program 35 * 36 * Generally useful tty stuff. 37 */ 38 39 #include "rcv.h" 40 #include "extern.h" 41 #include <sys/ioctl.h> 42 #include <errno.h> 43 44 static cc_t c_erase; /* Current erase char */ 45 static cc_t c_kill; /* Current kill char */ 46 #ifndef TIOCSTI 47 static int ttyset; /* We must now do erase/kill */ 48 #endif 49 static volatile sig_atomic_t ttysignal; /* Interrupted by a signal? */ 50 51 /* 52 * Read all relevant header fields. 53 */ 54 int 55 grabh(struct header *hp, int gflags) 56 { 57 struct termios ttybuf; 58 #ifndef TIOCSTI 59 struct sigaction savequit; 60 #else 61 # ifdef TIOCEXT 62 int extproc; 63 int flag; 64 # endif /* TIOCEXT */ 65 #endif 66 struct sigaction savetstp; 67 struct sigaction savettou; 68 struct sigaction savettin; 69 struct sigaction act; 70 char *s; 71 int error; 72 73 sigemptyset(&act.sa_mask); 74 act.sa_flags = SA_RESTART; 75 act.sa_handler = SIG_DFL; 76 (void)sigaction(SIGTSTP, &act, &savetstp); 77 (void)sigaction(SIGTTOU, &act, &savettou); 78 (void)sigaction(SIGTTIN, &act, &savettin); 79 error = 1; 80 #ifndef TIOCSTI 81 ttyset = 0; 82 #endif 83 if (tcgetattr(fileno(stdin), &ttybuf) < 0) { 84 warn("tcgetattr"); 85 return(-1); 86 } 87 c_erase = ttybuf.c_cc[VERASE]; 88 c_kill = ttybuf.c_cc[VKILL]; 89 #ifndef TIOCSTI 90 ttybuf.c_cc[VERASE] = 0; 91 ttybuf.c_cc[VKILL] = 0; 92 act.sa_handler = SIG_IGN; 93 if (sigaction(SIGQUIT, &act, &savequit) == 0 && 94 savequit.sa_handler == SIG_DFL) 95 (void)sigaction(SIGQUIT, &savequit, NULL); 96 #else 97 # ifdef TIOCEXT 98 extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0); 99 if (extproc) { 100 flag = 0; 101 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) 102 warn("TIOCEXT: off"); 103 } 104 # endif /* TIOCEXT */ 105 #endif 106 if (gflags & GTO) { 107 #ifndef TIOCSTI 108 if (!ttyset && hp->h_to != NULL) 109 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 110 #endif 111 s = readtty("To: ", detract(hp->h_to, 0)); 112 if (s == NULL) 113 goto out; 114 hp->h_to = extract(s, GTO); 115 } 116 if (gflags & GSUBJECT) { 117 #ifndef TIOCSTI 118 if (!ttyset && hp->h_subject != NULL) 119 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 120 #endif 121 s = readtty("Subject: ", hp->h_subject); 122 if (s == NULL) 123 goto out; 124 hp->h_subject = s; 125 } 126 if (gflags & GCC) { 127 #ifndef TIOCSTI 128 if (!ttyset && hp->h_cc != NULL) 129 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 130 #endif 131 s = readtty("Cc: ", detract(hp->h_cc, 0)); 132 if (s == NULL) 133 goto out; 134 hp->h_cc = extract(s, GCC); 135 } 136 if (gflags & GBCC) { 137 #ifndef TIOCSTI 138 if (!ttyset && hp->h_bcc != NULL) 139 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 140 #endif 141 s = readtty("Bcc: ", detract(hp->h_bcc, 0)); 142 if (s == NULL) 143 goto out; 144 hp->h_bcc = extract(s, GBCC); 145 } 146 error = 0; 147 out: 148 (void)sigaction(SIGTSTP, &savetstp, NULL); 149 (void)sigaction(SIGTTOU, &savettou, NULL); 150 (void)sigaction(SIGTTIN, &savettin, NULL); 151 #ifndef TIOCSTI 152 ttybuf.c_cc[VERASE] = c_erase; 153 ttybuf.c_cc[VKILL] = c_kill; 154 if (ttyset) 155 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 156 (void)sigaction(SIGQUIT, &savequit, NULL); 157 #else 158 # ifdef TIOCEXT 159 if (extproc) { 160 flag = 1; 161 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) 162 warn("TIOCEXT: on"); 163 } 164 # endif /* TIOCEXT */ 165 #endif 166 return(error); 167 } 168 169 /* 170 * Read up a header from standard input. 171 * The source string has the preliminary contents to 172 * be read. 173 * 174 */ 175 char * 176 readtty(char *pr, char *src) 177 { 178 struct sigaction act, saveint; 179 char ch, canonb[BUFSIZ]; 180 char *cp, *cp2; 181 sigset_t oset; 182 int c; 183 184 fputs(pr, stdout); 185 fflush(stdout); 186 if (src != NULL && strlen(src) > sizeof(canonb) - 2) { 187 puts("too long to edit"); 188 return(src); 189 } 190 #ifndef TIOCSTI 191 if (src != NULL) 192 cp = copy(src, canonb); /* safe, bounds checked above */ 193 else 194 cp = copy("", canonb); 195 fputs(canonb, stdout); 196 fflush(stdout); 197 #else 198 cp = src == NULL ? "" : src; 199 while ((c = *cp++) != '\0') { 200 if ((c_erase != _POSIX_VDISABLE && c == c_erase) || 201 (c_kill != _POSIX_VDISABLE && c == c_kill)) { 202 ch = '\\'; 203 ioctl(0, TIOCSTI, &ch); 204 } 205 ch = c; 206 ioctl(0, TIOCSTI, &ch); 207 } 208 cp = canonb; 209 *cp = 0; 210 #endif 211 sigemptyset(&act.sa_mask); 212 act.sa_flags = 0; /* Note: will not restart syscalls */ 213 act.sa_handler = ttyint; 214 (void)sigaction(SIGINT, &act, &saveint); 215 act.sa_handler = ttystop; 216 (void)sigaction(SIGTSTP, &act, NULL); 217 (void)sigaction(SIGTTOU, &act, NULL); 218 (void)sigaction(SIGTTIN, &act, NULL); 219 (void)sigprocmask(SIG_UNBLOCK, &intset, &oset); 220 clearerr(stdin); 221 memset(cp, 0, canonb + sizeof(canonb) - cp); 222 for (cp2 = cp; cp2 < canonb + sizeof(canonb) - 1; ) { 223 c = getc(stdin); 224 switch (ttysignal) { 225 case SIGINT: 226 ttysignal = 0; 227 cp2 = NULL; 228 c = EOF; 229 /* FALLTHROUGH */ 230 case 0: 231 break; 232 default: 233 ttysignal = 0; 234 goto redo; 235 } 236 if (c == EOF || c == '\n') 237 break; 238 *cp2++ = c; 239 } 240 act.sa_handler = SIG_DFL; 241 sigemptyset(&act.sa_mask); 242 act.sa_flags = SA_RESTART; 243 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 244 (void)sigaction(SIGTSTP, &act, NULL); 245 (void)sigaction(SIGTTOU, &act, NULL); 246 (void)sigaction(SIGTTIN, &act, NULL); 247 (void)sigaction(SIGINT, &saveint, NULL); 248 if (cp2 == NULL) 249 return(NULL); /* user hit ^C */ 250 *cp2 = '\0'; 251 if (c == EOF && ferror(stdin)) { 252 redo: 253 cp = strlen(canonb) > 0 ? canonb : NULL; 254 clearerr(stdin); 255 /* XXX - make iterative, not recursive */ 256 return(readtty(pr, cp)); 257 } 258 #ifndef TIOCSTI 259 if (cp == NULL || *cp == '\0') 260 return(src); 261 cp2 = cp; 262 if (!ttyset) 263 return(strlen(canonb) > 0 ? savestr(canonb) : NULL); 264 while (*cp != '\0') { 265 c = *cp++; 266 if (c_erase != _POSIX_VDISABLE && c == c_erase) { 267 if (cp2 == canonb) 268 continue; 269 if (cp2[-1] == '\\') { 270 cp2[-1] = c; 271 continue; 272 } 273 cp2--; 274 continue; 275 } 276 if (c_kill != _POSIX_VDISABLE && c == c_kill) { 277 if (cp2 == canonb) 278 continue; 279 if (cp2[-1] == '\\') { 280 cp2[-1] = c; 281 continue; 282 } 283 cp2 = canonb; 284 continue; 285 } 286 *cp2++ = c; 287 } 288 *cp2 = '\0'; 289 #endif 290 if (equal("", canonb)) 291 return(""); 292 return(savestr(canonb)); 293 } 294 295 /* 296 * Receipt continuation. 297 */ 298 void 299 ttystop(int s) 300 { 301 struct sigaction act, oact; 302 sigset_t nset; 303 int save_errno; 304 305 /* 306 * Save old handler and set to default. 307 * Unblock receipt of 's' and then resend it. 308 */ 309 save_errno = errno; 310 (void)sigemptyset(&act.sa_mask); 311 act.sa_flags = SA_RESTART; 312 act.sa_handler = SIG_DFL; 313 (void)sigaction(s, &act, &oact); 314 (void)sigemptyset(&nset); 315 (void)sigaddset(&nset, s); 316 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 317 (void)kill(0, s); 318 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 319 (void)sigaction(s, &oact, NULL); 320 ttysignal = s; 321 errno = save_errno; 322 } 323 324 /*ARGSUSED*/ 325 void 326 ttyint(int s) 327 { 328 329 ttysignal = s; 330 } 331