1 /* $NetBSD: tty.c,v 1.26 2006/10/31 20:07:33 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)tty.c 8.2 (Berkeley) 6/6/93"; 36 #else 37 __RCSID("$NetBSD: tty.c,v 1.26 2006/10/31 20:07:33 christos 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 #ifdef USE_EDITLINE 50 #include "complete.h" 51 #endif 52 53 #if !defined(USE_EDITLINE) || !defined(TIOCSTI) 54 static cc_t c_erase; /* Current erase char */ 55 static cc_t c_kill; /* Current kill char */ 56 #endif 57 #ifndef USE_EDITLINE 58 static jmp_buf rewrite; /* Place to go when continued */ 59 #endif 60 static jmp_buf intjmp; /* Place to go when interrupted */ 61 #ifndef TIOCSTI 62 static int ttyset; /* We must now do erase/kill */ 63 #endif 64 65 66 /* 67 * Read all relevant header fields. 68 */ 69 70 int 71 grabh(struct header *hp, int gflags) 72 { 73 struct termios ttybuf; 74 75 /* The following are declared volatile to avoid longjmp 76 * clobbering, though they seem safe without it! */ 77 sig_t volatile saveint; 78 sig_t volatile savetstp; 79 sig_t volatile savettou; 80 sig_t volatile savettin; 81 #ifndef TIOCSTI 82 sig_t volatile savequit; 83 #else 84 # ifdef TIOCEXT 85 int volatile extproc; 86 # endif /* TIOCEXT */ 87 #endif /* TIOCSTI */ 88 int retval; 89 90 savetstp = signal(SIGTSTP, SIG_DFL); 91 savettou = signal(SIGTTOU, SIG_DFL); 92 savettin = signal(SIGTTIN, SIG_DFL); 93 #ifndef TIOCSTI 94 ttyset = 0; 95 #endif 96 if (tcgetattr(fileno(stdin), &ttybuf) < 0) { 97 warn("tcgetattr"); 98 return(-1); 99 } 100 #if !defined(USE_EDITLINE) || !defined(TIOCSTI) 101 c_erase = ttybuf.c_cc[VERASE]; 102 c_kill = ttybuf.c_cc[VKILL]; 103 #endif 104 #ifndef TIOCSTI 105 ttybuf.c_cc[VERASE] = _POSIX_VDISABLE; 106 ttybuf.c_cc[VKILL] = _POSIX_VDISABLE; 107 if ((saveint = signal(SIGINT, SIG_IGN)) == SIG_DFL) 108 (void)signal(SIGINT, SIG_DFL); 109 if ((savequit = signal(SIGQUIT, SIG_IGN)) == SIG_DFL) 110 (void)signal(SIGQUIT, SIG_DFL); 111 #else 112 # ifdef TIOCEXT 113 extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0); 114 if (extproc) { 115 int flag; 116 flag = 0; 117 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) 118 warn("TIOCEXT: off"); 119 } 120 # endif /* TIOCEXT */ 121 saveint = signal(SIGINT, ttyint); /* must precede setjmp to be saved */ 122 if ((retval = setjmp(intjmp)) != 0) { 123 (void)fputc('\n', stdout); 124 goto out; 125 } 126 #endif 127 if (gflags & GTO) { 128 #ifndef TIOCSTI 129 if (!ttyset && hp->h_to != NULL) 130 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 131 #endif 132 hp->h_to = 133 extract(readtty("To: ", detract(hp->h_to, 0)), GTO); 134 } 135 if (gflags & GSUBJECT) { 136 #ifndef TIOCSTI 137 if (!ttyset && hp->h_subject != NULL) 138 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 139 #endif 140 hp->h_subject = readtty("Subject: ", hp->h_subject); 141 } 142 if (gflags & GCC) { 143 #ifndef TIOCSTI 144 if (!ttyset && hp->h_cc != NULL) 145 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 146 #endif 147 hp->h_cc = 148 extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC); 149 } 150 if (gflags & GBCC) { 151 #ifndef TIOCSTI 152 if (!ttyset && hp->h_bcc != NULL) 153 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 154 #endif 155 hp->h_bcc = 156 extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC); 157 } 158 if (gflags & GSMOPTS) { 159 char *smopts; 160 #ifndef TIOCSTI 161 if (!ttyset && hp->h_smopts != NULL) 162 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 163 #endif 164 smopts = readtty("Smopts: ", detract(hp->h_smopts, GSMOPTS)); 165 166 /* Parse smopts with getrawlist() rather than expand() 167 * to get a shell-like expansion. 168 */ 169 hp->h_smopts = NULL; 170 if (smopts) { 171 struct name *np, *t; 172 char *argv[MAXARGC]; 173 int argc, i; 174 175 np = NULL; 176 argc = getrawlist(smopts, argv, sizeof(argv)/sizeof(*argv)); 177 for (i = 0; i < argc; i++) { 178 t = nalloc(argv[i], GSMOPTS); 179 if (hp->h_smopts == NULL) 180 hp->h_smopts = t; 181 else 182 np->n_flink = t; 183 t->n_blink = np; 184 np = t; 185 } 186 } 187 #ifdef MIME_SUPPORT 188 if (hp->h_attach) { 189 struct attachment *ap; 190 int i; 191 i = 0; 192 for (ap = hp->h_attach; ap; ap = ap->a_flink) 193 i++; 194 (void)printf("Attachment%s: %d\n", i > 1 ? "s" : "", i); 195 } 196 #endif 197 } 198 #ifdef TIOCSTI 199 out: 200 #endif 201 (void)signal(SIGTSTP, savetstp); 202 (void)signal(SIGTTOU, savettou); 203 (void)signal(SIGTTIN, savettin); 204 #ifndef TIOCSTI 205 ttybuf.c_cc[VERASE] = c_erase; 206 ttybuf.c_cc[VKILL] = c_kill; 207 if (ttyset) 208 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 209 (void)signal(SIGQUIT, savequit); 210 #else 211 # ifdef TIOCEXT 212 if (extproc) { 213 int flag; 214 flag = 1; 215 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) 216 warn("TIOCEXT: on"); 217 } 218 # endif /* TIOCEXT */ 219 #endif 220 (void)signal(SIGINT, saveint); 221 return retval; 222 } 223 224 /* 225 * Read up a header from standard input. 226 * The source string has the preliminary contents to 227 * be read. 228 * 229 */ 230 231 #ifdef USE_EDITLINE 232 char * 233 readtty(const char pr[], char src[]) 234 { 235 char *line; 236 line = my_gets(&elm.string, pr, src); 237 #if 0 238 return line ? savestr(line) : __UNCONST(""); 239 #else 240 if (line) 241 return savestr(line); 242 else 243 return __UNCONST(""); 244 #endif 245 } 246 247 #else /* USE_EDITLINE */ 248 249 char * 250 readtty(const char pr[], char src[]) 251 { 252 /* XXX - watch for potential setjmp/longjmp clobbering! 253 * Currently there appear to be none. 254 */ 255 char canonb[BUFSIZ]; 256 int c; 257 char *cp, *cp2; 258 #ifdef TIOCSTI 259 char ch; 260 static char empty[] = ""; 261 #endif 262 (void)fputs(pr, stdout); 263 (void)fflush(stdout); 264 if (src != NULL && strlen(src) > BUFSIZ - 2) { 265 (void)printf("too long to edit\n"); 266 return(src); 267 } 268 #ifndef TIOCSTI 269 if (src != NULL) 270 cp = copy(src, canonb); 271 else 272 cp = copy("", canonb); 273 (void)fputs(canonb, stdout); 274 (void)fflush(stdout); 275 #else 276 cp = src == NULL ? empty : src; 277 while ((c = *cp++) != '\0') { 278 if ((c_erase != _POSIX_VDISABLE && c == c_erase) || 279 (c_kill != _POSIX_VDISABLE && c == c_kill)) { 280 ch = '\\'; 281 (void)ioctl(0, TIOCSTI, &ch); 282 } 283 ch = c; 284 (void)ioctl(0, TIOCSTI, &ch); 285 } 286 cp = canonb; 287 *cp = 0; 288 #endif 289 cp2 = cp; 290 while (cp2 < canonb + BUFSIZ) 291 *cp2++ = 0; 292 cp2 = cp; 293 if (setjmp(rewrite)) 294 goto redo; 295 (void)signal(SIGTSTP, ttystop); 296 (void)signal(SIGTTOU, ttystop); 297 (void)signal(SIGTTIN, ttystop); 298 clearerr(stdin); 299 while (cp2 < canonb + BUFSIZ) { 300 c = getc(stdin); 301 if (c == EOF || c == '\n') 302 break; 303 *cp2++ = c; 304 } 305 *cp2 = 0; 306 (void)signal(SIGTSTP, SIG_DFL); 307 (void)signal(SIGTTOU, SIG_DFL); 308 (void)signal(SIGTTIN, SIG_DFL); 309 if (c == EOF && ferror(stdin)) { 310 redo: 311 cp = strlen(canonb) > 0 ? canonb : NULL; 312 clearerr(stdin); 313 return(readtty(pr, cp)); 314 } 315 #ifndef TIOCSTI 316 if (cp == NULL || *cp == '\0') 317 return(src); 318 cp2 = cp; 319 if (!ttyset) 320 return(strlen(canonb) > 0 ? savestr(canonb) : NULL); 321 while (*cp != '\0') { 322 c = *cp++; 323 if (c_erase != _POSIX_VDISABLE && c == c_erase) { 324 if (cp2 == canonb) 325 continue; 326 if (cp2[-1] == '\\') { 327 cp2[-1] = c; 328 continue; 329 } 330 cp2--; 331 continue; 332 } 333 if (c_kill != _POSIX_VDISABLE && c == c_kill) { 334 if (cp2 == canonb) 335 continue; 336 if (cp2[-1] == '\\') { 337 cp2[-1] = c; 338 continue; 339 } 340 cp2 = canonb; 341 continue; 342 } 343 *cp2++ = c; 344 } 345 *cp2 = '\0'; 346 #endif 347 if (equal("", canonb)) 348 return(NULL); 349 return(savestr(canonb)); 350 } 351 352 /* 353 * Receipt continuation. 354 */ 355 void 356 ttystop(int s) 357 { 358 sig_t old_action = signal(s, SIG_DFL); 359 sigset_t nset; 360 361 (void)sigemptyset(&nset); 362 (void)sigaddset(&nset, s); 363 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 364 (void)kill(0, s); 365 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 366 (void)signal(s, old_action); 367 longjmp(rewrite, 1); 368 } 369 #endif /* USE_EDITLINE */ 370 371 /*ARGSUSED*/ 372 void 373 ttyint(int s __unused) 374 { 375 longjmp(intjmp, 1); 376 } 377