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