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