1 /* $NetBSD: tty.c,v 1.27 2006/11/28 18:45:32 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.27 2006/11/28 18:45:32 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 up a header from standard input. 68 * The source string has the preliminary contents to 69 * be read. 70 * 71 */ 72 #ifdef USE_EDITLINE 73 static char * 74 readtty(const char pr[], char src[]) 75 { 76 char *line; 77 line = my_gets(&elm.string, pr, src); 78 #if 0 79 return line ? savestr(line) : __UNCONST(""); 80 #else 81 if (line) 82 return savestr(line); 83 else 84 return __UNCONST(""); 85 #endif 86 } 87 88 #else /* USE_EDITLINE */ 89 90 /* 91 * Receipt continuation. 92 */ 93 static void 94 ttystop(int s) 95 { 96 sig_t old_action = signal(s, SIG_DFL); 97 sigset_t nset; 98 99 (void)sigemptyset(&nset); 100 (void)sigaddset(&nset, s); 101 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 102 (void)kill(0, s); 103 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 104 (void)signal(s, old_action); 105 longjmp(rewrite, 1); 106 } 107 108 static char * 109 readtty(const char pr[], char src[]) 110 { 111 /* XXX - watch for potential setjmp/longjmp clobbering! 112 * Currently there appear to be none. 113 */ 114 char canonb[LINESIZE]; 115 int c; 116 char *cp, *cp2; 117 #ifdef TIOCSTI 118 char ch; 119 static char empty[] = ""; 120 #endif 121 (void)fputs(pr, stdout); 122 (void)fflush(stdout); 123 if (src != NULL && strlen(src) > sizeof(canonb) - 2) { 124 (void)printf("too long to edit\n"); 125 return src; 126 } 127 #ifndef TIOCSTI 128 if (src != NULL) 129 cp = copy(src, canonb); 130 else 131 cp = copy("", canonb); 132 (void)fputs(canonb, stdout); 133 (void)fflush(stdout); 134 #else 135 cp = src == NULL ? empty : src; 136 while ((c = *cp++) != '\0') { 137 if ((c_erase != _POSIX_VDISABLE && c == c_erase) || 138 (c_kill != _POSIX_VDISABLE && c == c_kill)) { 139 ch = '\\'; 140 (void)ioctl(0, TIOCSTI, &ch); 141 } 142 ch = c; 143 (void)ioctl(0, TIOCSTI, &ch); 144 } 145 cp = canonb; 146 *cp = 0; 147 #endif 148 cp2 = cp; 149 while (cp2 < canonb + sizeof(canonb)) 150 *cp2++ = 0; 151 cp2 = cp; 152 if (setjmp(rewrite)) 153 goto redo; 154 (void)signal(SIGTSTP, ttystop); 155 (void)signal(SIGTTOU, ttystop); 156 (void)signal(SIGTTIN, ttystop); 157 clearerr(stdin); 158 while (cp2 < canonb + sizeof(canonb)) { 159 c = getc(stdin); 160 if (c == EOF || c == '\n') 161 break; 162 *cp2++ = c; 163 } 164 *cp2 = 0; 165 (void)signal(SIGTSTP, SIG_DFL); 166 (void)signal(SIGTTOU, SIG_DFL); 167 (void)signal(SIGTTIN, SIG_DFL); 168 if (c == EOF && ferror(stdin)) { 169 redo: 170 cp = strlen(canonb) > 0 ? canonb : NULL; 171 clearerr(stdin); 172 return readtty(pr, cp); 173 } 174 #ifndef TIOCSTI 175 if (cp == NULL || *cp == '\0') 176 return src; 177 cp2 = cp; 178 if (!ttyset) 179 return strlen(canonb) > 0 ? savestr(canonb) : NULL; 180 while (*cp != '\0') { 181 c = *cp++; 182 if (c_erase != _POSIX_VDISABLE && c == c_erase) { 183 if (cp2 == canonb) 184 continue; 185 if (cp2[-1] == '\\') { 186 cp2[-1] = c; 187 continue; 188 } 189 cp2--; 190 continue; 191 } 192 if (c_kill != _POSIX_VDISABLE && c == c_kill) { 193 if (cp2 == canonb) 194 continue; 195 if (cp2[-1] == '\\') { 196 cp2[-1] = c; 197 continue; 198 } 199 cp2 = canonb; 200 continue; 201 } 202 *cp2++ = c; 203 } 204 *cp2 = '\0'; 205 #endif 206 if (equal("", canonb)) 207 return NULL; 208 return savestr(canonb); 209 } 210 #endif /* USE_EDITLINE */ 211 212 213 /*ARGSUSED*/ 214 static void 215 ttyint(int s __unused) 216 { 217 longjmp(intjmp, 1); 218 } 219 220 /* 221 * Read all relevant header fields. 222 */ 223 PUBLIC int 224 grabh(struct header *hp, int gflags) 225 { 226 struct termios ttybuf; 227 228 /* The following are declared volatile to avoid longjmp 229 * clobbering, though they seem safe without it! */ 230 sig_t volatile saveint; 231 sig_t volatile savetstp; 232 sig_t volatile savettou; 233 sig_t volatile savettin; 234 #ifndef TIOCSTI 235 sig_t volatile savequit; 236 #else 237 # ifdef TIOCEXT 238 int volatile extproc; 239 # endif /* TIOCEXT */ 240 #endif /* TIOCSTI */ 241 int retval; 242 243 savetstp = signal(SIGTSTP, SIG_DFL); 244 savettou = signal(SIGTTOU, SIG_DFL); 245 savettin = signal(SIGTTIN, SIG_DFL); 246 #ifndef TIOCSTI 247 ttyset = 0; 248 #endif 249 if (tcgetattr(fileno(stdin), &ttybuf) < 0) { 250 warn("tcgetattr"); 251 return -1; 252 } 253 #if !defined(USE_EDITLINE) || !defined(TIOCSTI) 254 c_erase = ttybuf.c_cc[VERASE]; 255 c_kill = ttybuf.c_cc[VKILL]; 256 #endif 257 #ifndef TIOCSTI 258 ttybuf.c_cc[VERASE] = _POSIX_VDISABLE; 259 ttybuf.c_cc[VKILL] = _POSIX_VDISABLE; 260 if ((saveint = signal(SIGINT, SIG_IGN)) == SIG_DFL) 261 (void)signal(SIGINT, SIG_DFL); 262 if ((savequit = signal(SIGQUIT, SIG_IGN)) == SIG_DFL) 263 (void)signal(SIGQUIT, SIG_DFL); 264 #else 265 # ifdef TIOCEXT 266 extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0); 267 if (extproc) { 268 int flag; 269 flag = 0; 270 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) 271 warn("TIOCEXT: off"); 272 } 273 # endif /* TIOCEXT */ 274 saveint = signal(SIGINT, ttyint); /* must precede setjmp to be saved */ 275 if ((retval = setjmp(intjmp)) != 0) { 276 (void)fputc('\n', stdout); 277 goto out; 278 } 279 #endif 280 if (gflags & GTO) { 281 #ifndef TIOCSTI 282 if (!ttyset && hp->h_to != NULL) 283 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 284 #endif 285 hp->h_to = 286 extract(readtty("To: ", detract(hp->h_to, 0)), GTO); 287 } 288 if (gflags & GSUBJECT) { 289 #ifndef TIOCSTI 290 if (!ttyset && hp->h_subject != NULL) 291 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 292 #endif 293 hp->h_subject = readtty("Subject: ", hp->h_subject); 294 } 295 if (gflags & GCC) { 296 #ifndef TIOCSTI 297 if (!ttyset && hp->h_cc != NULL) 298 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 299 #endif 300 hp->h_cc = 301 extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC); 302 } 303 if (gflags & GBCC) { 304 #ifndef TIOCSTI 305 if (!ttyset && hp->h_bcc != NULL) 306 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 307 #endif 308 hp->h_bcc = 309 extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC); 310 } 311 if (gflags & GSMOPTS) { 312 char *smopts; 313 #ifndef TIOCSTI 314 if (!ttyset && hp->h_smopts != NULL) 315 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 316 #endif 317 smopts = readtty("Smopts: ", detract(hp->h_smopts, GSMOPTS)); 318 319 /* Parse smopts with getrawlist() rather than expand() 320 * to get a shell-like expansion. 321 */ 322 hp->h_smopts = NULL; 323 if (smopts) { 324 struct name *np, *t; 325 char *argv[MAXARGC]; 326 int argc, i; 327 328 np = NULL; 329 argc = getrawlist(smopts, argv, sizeof(argv)/sizeof(*argv)); 330 for (i = 0; i < argc; i++) { 331 t = nalloc(argv[i], GSMOPTS); 332 if (hp->h_smopts == NULL) 333 hp->h_smopts = t; 334 else 335 np->n_flink = t; 336 t->n_blink = np; 337 np = t; 338 } 339 } 340 #ifdef MIME_SUPPORT 341 if (hp->h_attach) { 342 struct attachment *ap; 343 int i; 344 i = 0; 345 for (ap = hp->h_attach; ap; ap = ap->a_flink) 346 i++; 347 (void)printf("Attachment%s: %d\n", i > 1 ? "s" : "", i); 348 } 349 #endif 350 } 351 #ifdef TIOCSTI 352 out: 353 #endif 354 (void)signal(SIGTSTP, savetstp); 355 (void)signal(SIGTTOU, savettou); 356 (void)signal(SIGTTIN, savettin); 357 #ifndef TIOCSTI 358 ttybuf.c_cc[VERASE] = c_erase; 359 ttybuf.c_cc[VKILL] = c_kill; 360 if (ttyset) 361 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); 362 (void)signal(SIGQUIT, savequit); 363 #else 364 # ifdef TIOCEXT 365 if (extproc) { 366 int flag; 367 flag = 1; 368 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) 369 warn("TIOCEXT: on"); 370 } 371 # endif /* TIOCEXT */ 372 #endif 373 (void)signal(SIGINT, saveint); 374 return retval; 375 } 376