1 /* $NetBSD: tty.c,v 1.28 2009/04/10 13:08:25 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.28 2009/04/10 13:08:25 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 #include "sig.h" 53 54 static jmp_buf tty_jmpbuf; /* Place to go when interrupted */ 55 56 #ifndef USE_EDITLINE 57 static cc_t c_erase; /* Current erase char */ 58 static cc_t c_kill; /* Current kill char */ 59 # ifndef TIOCSTI 60 static int ttyset; /* We must now do erase/kill */ 61 # endif 62 #endif /* USE_EDITLINE */ 63 64 /* 65 * Read up a header from standard input. 66 * The source string has the preliminary contents to 67 * be read. 68 * 69 * Returns: an salloc'ed copy of the line read if successful, or NULL 70 * if no characters were read or if an error occurred. 71 * 72 */ 73 #ifdef USE_EDITLINE 74 static char * 75 readtty(const char *pr, char *src) 76 { 77 char *line; 78 79 line = my_gets(&elm.string, pr, src); 80 sig_check(); 81 if (line == NULL) 82 (void)putc('\n', stdout); 83 84 return line ? savestr(line) : __UNCONST(""); 85 } 86 87 #else /* not USE_EDITLINE */ 88 89 static char * 90 readtty(const char *pr, char *src) 91 { 92 char canonb[LINESIZE]; 93 char *cp, *cp2; 94 int c; 95 #ifdef TIOCSTI 96 char ch; 97 #endif 98 99 (void)fputs(pr, stdout); 100 (void)fflush(stdout); 101 if (src != NULL && strlen(src) > sizeof(canonb) - 2) { 102 (void)printf("too long to edit\n"); 103 return src; 104 } 105 #ifndef TIOCSTI 106 if (src != NULL) 107 cp = copy(src, canonb); 108 else 109 cp = copy(__UNCONST(""), canonb); 110 c = *cp; 111 (void)fputs(canonb, stdout); 112 (void)fflush(stdout); 113 #else 114 cp = src == NULL ? __UNCONST("") : src; 115 while ((c = *cp++) != '\0') { 116 if ((c_erase != _POSIX_VDISABLE && c == c_erase) || 117 (c_kill != _POSIX_VDISABLE && c == c_kill)) { 118 ch = '\\'; 119 (void)ioctl(0, TIOCSTI, &ch); 120 } 121 ch = c; 122 (void)ioctl(0, TIOCSTI, &ch); 123 } 124 cp = canonb; 125 *cp = '\0'; 126 #endif 127 clearerr(stdin); 128 cp2 = cp; 129 while (cp2 < canonb + sizeof(canonb) - 1) { 130 c = getc(stdin); 131 sig_check(); 132 if (c == EOF) { 133 if (feof(stdin)) 134 (void)putc('\n', stdout); 135 break; 136 } 137 if (c == '\n') 138 break; 139 *cp2++ = c; 140 } 141 *cp2 = '\0'; 142 143 if (c == EOF && ferror(stdin)) { 144 cp = strlen(canonb) > 0 ? canonb : NULL; 145 clearerr(stdin); 146 return readtty(pr, cp); 147 } 148 #ifndef TIOCSTI 149 if (cp == NULL || *cp == '\0') 150 return src; 151 if (ttyset == 0) 152 return strlen(canonb) > 0 ? savestr(canonb) : NULL; 153 154 /* 155 * Do erase and kill. 156 */ 157 cp2 = cp; 158 while (*cp != '\0') { 159 c = *cp++; 160 if (c_erase != _POSIX_VDISABLE && c == c_erase) { 161 if (cp2 == canonb) 162 continue; 163 if (cp2[-1] == '\\') { 164 cp2[-1] = c; 165 continue; 166 } 167 cp2--; 168 continue; 169 } 170 if (c_kill != _POSIX_VDISABLE && c == c_kill) { 171 if (cp2 == canonb) 172 continue; 173 if (cp2[-1] == '\\') { 174 cp2[-1] = c; 175 continue; 176 } 177 cp2 = canonb; 178 continue; 179 } 180 *cp2++ = c; 181 } 182 *cp2 = '\0'; 183 #endif 184 if (canonb[0] == '\0') 185 return __UNCONST(""); 186 return savestr(canonb); 187 } 188 #endif /* USE_EDITLINE */ 189 190 #ifdef USE_EDITLINE 191 # define save_erase_and_kill(t) 0 192 #else 193 static int 194 save_erase_and_kill(struct termios *t) 195 { 196 197 # ifndef TIOCSTI 198 ttyset = 0; 199 #endif 200 if (tcgetattr(fileno(stdin), t) == -1) { 201 warn("tcgetattr"); 202 return -1; 203 } 204 c_erase = t->c_cc[VERASE]; 205 c_kill = t->c_cc[VKILL]; 206 return 0; 207 } 208 #endif 209 210 #if defined(USE_EDITLINE) || defined(TIOCSTI) 211 # define disable_erase_and_kill(t) 212 #else 213 static void 214 disable_erase_and_kill(struct termios *t) 215 { 216 217 if (ttyset == 0) { 218 ttyset = 1; 219 t->c_cc[VERASE] = _POSIX_VDISABLE; 220 t->c_cc[VKILL] = _POSIX_VDISABLE; 221 (void)tcsetattr(fileno(stdin), TCSADRAIN, t); 222 } 223 } 224 #endif 225 226 #if defined(USE_EDITLINE) || defined(TIOCSTI) 227 # define restore_erase_and_kill(t) 228 #else 229 static void 230 restore_erase_and_kill(struct termios *t) 231 { 232 233 if (ttyset != 0) { 234 ttyset = 0; 235 t->c_cc[VERASE] = c_erase; 236 t->c_cc[VKILL] = c_kill; 237 (void)tcsetattr(fileno(stdin), TCSADRAIN, t); 238 } 239 } 240 #endif 241 242 /* 243 * Do a shell-like extraction of a line 244 * and make a list of name from it. 245 * Return the list or NULL if none found. 246 */ 247 static struct name * 248 shextract(char *line, int ntype) 249 { 250 struct name *begin, *np, *t; 251 char *argv[MAXARGC]; 252 size_t argc, i; 253 254 begin = NULL; 255 if (line) { 256 np = NULL; 257 argc = getrawlist(line, argv, (int)__arraycount(argv)); 258 for (i = 0; i < argc; i++) { 259 t = nalloc(argv[i], ntype); 260 if (begin == NULL) 261 begin = t; 262 else 263 np->n_flink = t; 264 t->n_blink = np; 265 np = t; 266 } 267 } 268 return begin; 269 } 270 271 /*ARGSUSED*/ 272 static void 273 tty_sigint(int signo __unused) 274 { 275 276 longjmp(tty_jmpbuf, 1); 277 } 278 279 /* 280 * Read all relevant header fields. 281 * Returns 0 on success; 1 if there was an error or signal. 282 */ 283 PUBLIC int 284 grabh(struct header *hp, int gflags) 285 { 286 sig_t volatile old_sigint; 287 int retval; 288 #ifndef USE_EDITLINE 289 struct termios ttybuf; 290 # if defined(TIOCSTI) && defined(TIOCEXT) 291 int extproc; 292 # endif 293 294 if (save_erase_and_kill(&ttybuf)) 295 return -1; 296 297 # if defined(TIOCSTI) && defined(TIOCEXT) 298 extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0); 299 if (extproc) { 300 int flag; 301 302 flag = 0; 303 if (ioctl(fileno(stdin), TIOCEXT, &flag) == -1) 304 warn("TIOCEXT: off"); 305 } 306 # endif 307 #endif /* USE_EDITLINE */ 308 309 sig_check(); 310 old_sigint = sig_signal(SIGINT, tty_sigint); 311 312 /* return here if we detect a SIGINT */ 313 if ((retval = setjmp(tty_jmpbuf)) != 0) { 314 (void)putc('\n', stdout); 315 goto out; 316 } 317 318 /* 319 * Do this irrespective of whether the initial string is empty. 320 * Otherwise, the editing is inconsistent. 321 */ 322 disable_erase_and_kill(&ttybuf); 323 324 if (gflags & GTO) { 325 hp->h_to = 326 extract(readtty("To: ", detract(hp->h_to, 0)), GTO); 327 } 328 if (gflags & GSUBJECT) { 329 hp->h_subject = readtty("Subject: ", hp->h_subject); 330 } 331 if (gflags & GCC) { 332 hp->h_cc = 333 extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC); 334 } 335 if (gflags & GBCC) { 336 hp->h_bcc = 337 extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC); 338 } 339 if (gflags & GSMOPTS) { 340 hp->h_smopts = 341 shextract(readtty("Smopts: ", detract(hp->h_smopts, 0)), 342 GSMOPTS); 343 } 344 #ifdef MIME_SUPPORT 345 if (gflags & GSMOPTS) { /* XXX - Use a new flag for this? */ 346 if (hp->h_attach) { 347 struct attachment *ap; 348 int i; 349 350 i = 0; 351 for (ap = hp->h_attach; ap; ap = ap->a_flink) 352 i++; 353 (void)printf("Attachment%s: %d\n", i > 1 ? "s" : "", i); 354 } 355 } 356 #endif 357 out: 358 restore_erase_and_kill(&ttybuf); 359 360 #ifndef USE_EDITLINE 361 # if defined(TIOCSTI) && defined(TIOCEXT) 362 if (extproc) { 363 int flag; 364 flag = 1; 365 if (ioctl(fileno(stdin), TIOCEXT, &flag) == -1) 366 warn("TIOCEXT: on"); 367 } 368 # endif 369 #endif 370 (void)sig_signal(SIGINT, old_sigint); 371 sig_check(); 372 return retval; 373 } 374