Annotation of src/usr.bin/mail/collect.c, Revision 1.21
1.21 ! millert 1: /* $OpenBSD: collect.c,v 1.20 2001/01/16 05:36:08 millert Exp $ */
1.6 millert 2: /* $NetBSD: collect.c,v 1.9 1997/07/09 05:25:45 mikel Exp $ */
1.2 deraadt 3:
1.1 deraadt 4: /*
5: * Copyright (c) 1980, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
1.2 deraadt 38: #if 0
39: static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94";
40: #else
1.21 ! millert 41: static char rcsid[] = "$OpenBSD: collect.c,v 1.20 2001/01/16 05:36:08 millert Exp $";
1.2 deraadt 42: #endif
1.1 deraadt 43: #endif /* not lint */
44:
45: /*
46: * Mail -- a mail program
47: *
48: * Collect input from standard input, handling
49: * ~ escapes.
50: */
51:
52: #include "rcv.h"
53: #include "extern.h"
54:
55: /*
56: * Read a message from standard output and return a read file to it
57: * or NULL on error.
58: */
59:
60: /*
61: * The following hokiness with global variables is so that on
62: * receipt of an interrupt signal, the partial message can be salted
63: * away on dead.letter.
64: */
65:
66: static sig_t saveint; /* Previous SIGINT value */
67: static sig_t savehup; /* Previous SIGHUP value */
68: static sig_t savetstp; /* Previous SIGTSTP value */
69: static sig_t savettou; /* Previous SIGTTOU value */
70: static sig_t savettin; /* Previous SIGTTIN value */
71: static FILE *collf; /* File for saving away */
72: static int hadintr; /* Have seen one SIGINT so far */
73:
1.8 millert 74: static sigjmp_buf colljmp; /* To get back to work */
75: static int colljmp_p; /* whether to long jump */
76: static sigjmp_buf collabort; /* To end collection with error */
1.1 deraadt 77:
78: FILE *
79: collect(hp, printheaders)
80: struct header *hp;
81: int printheaders;
82: {
83: FILE *fbuf;
1.21 ! millert 84: int lc, cc, fd, c, t, lastlong, rc;
! 85: volatile int escape, eofcount, longline;
! 86: volatile char getsub;
! 87: char linebuf[LINESIZE], tempname[PATHSIZE], *cp;
1.9 millert 88: sigset_t oset, nset;
1.1 deraadt 89:
90: collf = NULL;
91: /*
92: * Start catching signals from here, but we're still die on interrupts
93: * until we're in the main loop.
94: */
1.9 millert 95: sigemptyset(&nset);
96: sigaddset(&nset, SIGINT);
97: sigaddset(&nset, SIGHUP);
98: sigprocmask(SIG_BLOCK, &nset, &oset);
1.1 deraadt 99: if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
1.13 millert 100: (void)signal(SIGINT, collint);
1.1 deraadt 101: if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
1.13 millert 102: (void)signal(SIGHUP, collhup);
1.1 deraadt 103: savetstp = signal(SIGTSTP, collstop);
104: savettou = signal(SIGTTOU, collstop);
105: savettin = signal(SIGTTIN, collstop);
1.8 millert 106: if (sigsetjmp(collabort, 1) || sigsetjmp(colljmp, 1)) {
1.12 millert 107: (void)rm(tempname);
1.1 deraadt 108: goto err;
109: }
1.9 millert 110: sigdelset(&oset, SIGINT);
111: sigdelset(&oset, SIGHUP);
112: sigprocmask(SIG_SETMASK, &oset, NULL);
1.1 deraadt 113:
114: noreset++;
1.12 millert 115: (void)snprintf(tempname, sizeof(tempname),
116: "%s/mail.RsXXXXXXXXXX", tmpdir);
117: if ((fd = mkstemp(tempname)) == -1 ||
118: (collf = Fdopen(fd, "w+")) == NULL) {
1.19 millert 119: warn("%s", tempname);
1.1 deraadt 120: goto err;
121: }
1.12 millert 122: (void)rm(tempname);
1.1 deraadt 123:
124: /*
125: * If we are going to prompt for a subject,
126: * refrain from printing a newline after
127: * the headers (since some people mind).
128: */
129: t = GTO|GSUBJECT|GCC|GNL;
130: getsub = 0;
1.8 millert 131: if (hp->h_subject == NULL && value("interactive") != NULL &&
132: (value("ask") != NULL || value("asksub") != NULL))
1.1 deraadt 133: t &= ~GNL, getsub++;
134: if (printheaders) {
135: puthead(hp, stdout, t);
136: fflush(stdout);
137: }
1.8 millert 138: if ((cp = value("escape")) != NULL)
1.1 deraadt 139: escape = *cp;
140: else
141: escape = ESCAPE;
142: eofcount = 0;
143: hadintr = 0;
1.6 millert 144: lastlong = 0;
145: longline = 0;
1.1 deraadt 146:
1.8 millert 147: if (!sigsetjmp(colljmp, 1)) {
1.11 millert 148: if (getsub)
149: gethfromtty(hp, GSUBJECT);
1.1 deraadt 150: } else {
151: /*
152: * Come here for printing the after-signal message.
153: * Duplicate messages won't be printed because
154: * the write is aborted if we get a SIGTTOU.
155: */
156: cont:
157: if (hadintr) {
158: fflush(stdout);
1.6 millert 159: fputs("\n(Interrupt -- one more to kill letter)\n",
160: stderr);
1.1 deraadt 161: } else {
1.4 deraadt 162: if (isatty(0)) {
1.6 millert 163: puts("(continue)");
1.4 deraadt 164: fflush(stdout);
165: }
1.1 deraadt 166: }
167: }
168: for (;;) {
169: colljmp_p = 1;
170: c = readline(stdin, linebuf, LINESIZE);
171: colljmp_p = 0;
172: if (c < 0) {
1.8 millert 173: if (value("interactive") != NULL &&
174: value("ignoreeof") != NULL && ++eofcount < 25) {
1.6 millert 175: puts("Use \".\" to terminate letter");
1.1 deraadt 176: continue;
177: }
178: break;
179: }
1.6 millert 180: lastlong = longline;
181: longline = (c == LINESIZE - 1);
1.1 deraadt 182: eofcount = 0;
183: hadintr = 0;
184: if (linebuf[0] == '.' && linebuf[1] == '\0' &&
1.8 millert 185: value("interactive") != NULL && !lastlong &&
186: (value("dot") != NULL || value("ignoreeof") != NULL))
1.1 deraadt 187: break;
1.20 millert 188: if (linebuf[0] != escape || lastlong) {
1.6 millert 189: if (putline(collf, linebuf, !longline) < 0)
1.1 deraadt 190: goto err;
191: continue;
192: }
193: c = linebuf[1];
194: switch (c) {
195: default:
196: /*
197: * On double escape, just send the single one.
198: * Otherwise, it's an error.
199: */
200: if (c == escape) {
1.6 millert 201: if (putline(collf, &linebuf[1], !longline) < 0)
1.1 deraadt 202: goto err;
203: else
204: break;
205: }
1.6 millert 206: puts("Unknown tilde escape.");
1.1 deraadt 207: break;
208: case 'C':
209: /*
210: * Dump core.
211: */
1.2 deraadt 212: core(NULL);
1.1 deraadt 213: break;
214: case '!':
215: /*
216: * Shell escape, send the balance of the
217: * line to sh -c.
218: */
219: shell(&linebuf[2]);
220: break;
221: case ':':
222: case '_':
223: /*
224: * Escape to command mode, but be nice!
225: */
226: execute(&linebuf[2], 1);
227: goto cont;
228: case '.':
229: /*
230: * Simulate end of file on input.
231: */
232: goto out;
233: case 'q':
234: /*
235: * Force a quit of sending mail.
236: * Act like an interrupt happened.
237: */
238: hadintr++;
239: collint(SIGINT);
240: exit(1);
241: case 'h':
242: /*
243: * Grab a bunch of headers.
244: */
245: grabh(hp, GTO|GSUBJECT|GCC|GBCC);
246: goto cont;
247: case 't':
248: /*
249: * Add to the To list.
250: */
251: hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
252: break;
253: case 's':
254: /*
255: * Set the Subject list.
256: */
257: cp = &linebuf[2];
258: while (isspace(*cp))
259: cp++;
260: hp->h_subject = savestr(cp);
261: break;
262: case 'c':
263: /*
264: * Add to the CC list.
265: */
266: hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
267: break;
268: case 'b':
269: /*
270: * Add stuff to blind carbon copies list.
271: */
272: hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
273: break;
274: case 'd':
1.14 millert 275: strncpy(linebuf + 2, getdeadletter(), sizeof(linebuf) - 3);
276: linebuf[sizeof(linebuf) - 1] = '\0';
1.1 deraadt 277: /* fall into . . . */
278: case 'r':
279: case '<':
280: /*
281: * Invoke a file:
282: * Search for the file name,
283: * then open it and copy the contents to collf.
284: */
285: cp = &linebuf[2];
286: while (isspace(*cp))
287: cp++;
288: if (*cp == '\0') {
1.6 millert 289: puts("Interpolate what file?");
1.1 deraadt 290: break;
291: }
292: cp = expand(cp);
1.8 millert 293: if (cp == NULL)
1.1 deraadt 294: break;
295: if (isdir(cp)) {
296: printf("%s: Directory\n", cp);
297: break;
298: }
299: if ((fbuf = Fopen(cp, "r")) == NULL) {
1.19 millert 300: warn("%s", cp);
1.1 deraadt 301: break;
302: }
303: printf("\"%s\" ", cp);
304: fflush(stdout);
305: lc = 0;
306: cc = 0;
1.6 millert 307: while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) {
308: if (rc != LINESIZE - 1)
309: lc++;
310: if ((t = putline(collf, linebuf,
311: rc != LINESIZE-1)) < 0) {
312: (void)Fclose(fbuf);
1.1 deraadt 313: goto err;
314: }
315: cc += t;
316: }
1.6 millert 317: (void)Fclose(fbuf);
1.1 deraadt 318: printf("%d/%d\n", lc, cc);
319: break;
320: case 'w':
321: /*
322: * Write the message on a file.
323: */
324: cp = &linebuf[2];
325: while (*cp == ' ' || *cp == '\t')
326: cp++;
327: if (*cp == '\0') {
1.6 millert 328: fputs("Write what file!?\n", stderr);
1.1 deraadt 329: break;
330: }
1.8 millert 331: if ((cp = expand(cp)) == NULL)
1.1 deraadt 332: break;
333: rewind(collf);
334: exwrite(cp, collf, 1);
335: break;
336: case 'm':
337: case 'M':
338: case 'f':
339: case 'F':
340: /*
341: * Interpolate the named messages, if we
342: * are in receiving mail mode. Does the
343: * standard list processing garbage.
344: * If ~f is given, we don't shift over.
345: */
1.12 millert 346: if (forward(linebuf + 2, collf, tempname, c) < 0)
1.1 deraadt 347: goto err;
348: goto cont;
349: case '?':
350: if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
1.6 millert 351: warn(_PATH_TILDE);
1.1 deraadt 352: break;
353: }
354: while ((t = getc(fbuf)) != EOF)
1.7 millert 355: (void)putchar(t);
1.6 millert 356: (void)Fclose(fbuf);
1.1 deraadt 357: break;
358: case 'p':
359: /*
360: * Print out the current state of the
361: * message without altering anything.
362: */
363: rewind(collf);
1.6 millert 364: puts("-------\nMessage contains:");
1.1 deraadt 365: puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
366: while ((t = getc(collf)) != EOF)
1.7 millert 367: (void)putchar(t);
1.1 deraadt 368: goto cont;
369: case '|':
370: /*
371: * Pipe message through command.
372: * Collect output as new message.
373: */
374: rewind(collf);
375: mespipe(collf, &linebuf[2]);
376: goto cont;
377: case 'v':
378: case 'e':
379: /*
380: * Edit the current message.
381: * 'e' means to use EDITOR
382: * 'v' means to use VISUAL
383: */
384: rewind(collf);
385: mesedit(collf, c);
386: goto cont;
387: }
388: }
1.11 millert 389:
390: if (value("interactive") != NULL) {
391: if (value("askcc") != NULL || value("askbcc") != NULL) {
392: if (value("askcc") != NULL)
393: gethfromtty(hp, GCC);
394: if (value("askbcc") != NULL)
395: gethfromtty(hp, GBCC);
396: } else {
397: puts("EOT");
398: (void)fflush(stdout);
399: }
400: }
1.1 deraadt 401: goto out;
402: err:
403: if (collf != NULL) {
1.6 millert 404: (void)Fclose(collf);
1.1 deraadt 405: collf = NULL;
406: }
407: out:
408: if (collf != NULL)
409: rewind(collf);
410: noreset--;
1.13 millert 411: (void)sigemptyset(&nset);
412: (void)sigaddset(&nset, SIGINT);
413: (void)sigaddset(&nset, SIGHUP);
414: (void)sigprocmask(SIG_BLOCK, &nset, &oset);
415: (void)signal(SIGINT, saveint);
416: (void)signal(SIGHUP, savehup);
417: (void)signal(SIGTSTP, savetstp);
418: (void)signal(SIGTTOU, savettou);
419: (void)signal(SIGTTIN, savettin);
420: (void)sigprocmask(SIG_SETMASK, &oset, NULL);
1.6 millert 421: return(collf);
1.1 deraadt 422: }
423:
424: /*
425: * Write a file, ex-like if f set.
426: */
427: int
428: exwrite(name, fp, f)
429: char name[];
430: FILE *fp;
431: int f;
432: {
1.15 millert 433: FILE *of;
434: int c;
1.11 millert 435: ssize_t cc, lc;
1.1 deraadt 436: struct stat junk;
437:
438: if (f) {
439: printf("\"%s\" ", name);
440: fflush(stdout);
441: }
1.15 millert 442: if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
1.1 deraadt 443: if (!f)
444: fprintf(stderr, "%s: ", name);
1.6 millert 445: fputs("File exists\n", stderr);
1.1 deraadt 446: return(-1);
447: }
448: if ((of = Fopen(name, "w")) == NULL) {
1.8 millert 449: warn(NULL);
1.1 deraadt 450: return(-1);
451: }
452: lc = 0;
453: cc = 0;
454: while ((c = getc(fp)) != EOF) {
455: cc++;
456: if (c == '\n')
457: lc++;
1.7 millert 458: (void)putc(c, of);
1.1 deraadt 459: if (ferror(of)) {
1.19 millert 460: warn("%s", name);
1.6 millert 461: (void)Fclose(of);
1.1 deraadt 462: return(-1);
463: }
464: }
1.6 millert 465: (void)Fclose(of);
1.11 millert 466: printf("%d/%d\n", lc, cc);
1.1 deraadt 467: fflush(stdout);
468: return(0);
469: }
470:
471: /*
472: * Edit the message being collected on fp.
473: * On return, make the edit file the new temp file.
474: */
475: void
476: mesedit(fp, c)
477: FILE *fp;
478: int c;
479: {
480: sig_t sigint = signal(SIGINT, SIG_IGN);
481: FILE *nf = run_editor(fp, (off_t)-1, c, 0);
482:
483: if (nf != NULL) {
484: fseek(nf, 0L, 2);
485: collf = nf;
1.6 millert 486: (void)Fclose(fp);
1.1 deraadt 487: }
1.7 millert 488: (void)signal(SIGINT, sigint);
1.1 deraadt 489: }
490:
491: /*
492: * Pipe the message through the command.
493: * Old message is on stdin of command;
494: * New message collected from stdout.
495: * Sh -c must return 0 to accept the new message.
496: */
497: void
498: mespipe(fp, cmd)
499: FILE *fp;
500: char cmd[];
501: {
502: FILE *nf;
1.12 millert 503: int fd;
1.1 deraadt 504: sig_t sigint = signal(SIGINT, SIG_IGN);
1.12 millert 505: char *shell, tempname[PATHSIZE];
1.1 deraadt 506:
1.12 millert 507: (void)snprintf(tempname, sizeof(tempname),
508: "%s/mail.ReXXXXXXXXXX", tmpdir);
509: if ((fd = mkstemp(tempname)) == -1 ||
510: (nf = Fdopen(fd, "w+")) == NULL) {
1.19 millert 511: warn("%s", tempname);
1.1 deraadt 512: goto out;
513: }
1.12 millert 514: (void)rm(tempname);
1.1 deraadt 515: /*
516: * stdin = current message.
517: * stdout = new message.
518: */
1.20 millert 519: shell = value("SHELL");
1.1 deraadt 520: if (run_command(shell,
1.8 millert 521: 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
1.6 millert 522: (void)Fclose(nf);
1.1 deraadt 523: goto out;
524: }
525: if (fsize(nf) == 0) {
526: fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
1.6 millert 527: (void)Fclose(nf);
1.1 deraadt 528: goto out;
529: }
530: /*
531: * Take new files.
532: */
1.7 millert 533: (void)fseek(nf, 0L, 2);
1.1 deraadt 534: collf = nf;
1.6 millert 535: (void)Fclose(fp);
1.1 deraadt 536: out:
1.7 millert 537: (void)signal(SIGINT, sigint);
1.1 deraadt 538: }
539:
540: /*
541: * Interpolate the named messages into the current
542: * message, preceding each line with a tab.
543: * Return a count of the number of characters now in
544: * the message, or -1 if an error is encountered writing
545: * the message temporary. The flag argument is 'm' if we
546: * should shift over and 'f' if not.
547: */
548: int
1.12 millert 549: forward(ms, fp, fn, f)
1.1 deraadt 550: char ms[];
551: FILE *fp;
1.12 millert 552: char *fn;
1.1 deraadt 553: int f;
554: {
1.15 millert 555: int *msgvec;
1.1 deraadt 556: struct ignoretab *ig;
557: char *tabst;
558:
1.7 millert 559: msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
1.8 millert 560: if (msgvec == NULL)
1.1 deraadt 561: return(0);
562: if (getmsglist(ms, msgvec, 0) < 0)
563: return(0);
564: if (*msgvec == 0) {
565: *msgvec = first(0, MMNORM);
566: if (*msgvec == NULL) {
1.6 millert 567: puts("No appropriate messages");
1.1 deraadt 568: return(0);
569: }
570: msgvec[1] = NULL;
571: }
572: if (f == 'f' || f == 'F')
1.8 millert 573: tabst = NULL;
574: else if ((tabst = value("indentprefix")) == NULL)
1.1 deraadt 575: tabst = "\t";
576: ig = isupper(f) ? NULL : ignore;
1.6 millert 577: fputs("Interpolating:", stdout);
1.1 deraadt 578: for (; *msgvec != 0; msgvec++) {
579: struct message *mp = message + *msgvec - 1;
580:
581: touch(mp);
582: printf(" %d", *msgvec);
1.18 millert 583: if (sendmessage(mp, fp, ig, tabst) < 0) {
1.19 millert 584: warn("%s", fn);
1.1 deraadt 585: return(-1);
586: }
587: }
1.6 millert 588: putchar('\n');
1.1 deraadt 589: return(0);
590: }
591:
592: /*
593: * Print (continue) when continued after ^Z.
594: */
595: /*ARGSUSED*/
596: void
597: collstop(s)
598: int s;
599: {
600: sig_t old_action = signal(s, SIG_DFL);
1.2 deraadt 601: sigset_t nset;
1.1 deraadt 602:
1.13 millert 603: (void)sigemptyset(&nset);
604: (void)sigaddset(&nset, s);
605: (void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
606: (void)kill(0, s);
607: (void)sigprocmask(SIG_BLOCK, &nset, NULL);
608: (void)signal(s, old_action);
1.1 deraadt 609: if (colljmp_p) {
610: colljmp_p = 0;
611: hadintr = 0;
1.8 millert 612: siglongjmp(colljmp, 1);
1.1 deraadt 613: }
614: }
615:
616: /*
617: * On interrupt, come here to save the partial message in ~/dead.letter.
618: * Then jump out of the collection loop.
619: */
620: /*ARGSUSED*/
621: void
622: collint(s)
623: int s;
624: {
625: /*
626: * the control flow is subtle, because we can be called from ~q.
627: */
1.5 deraadt 628: if (hadintr == 0 && isatty(0)) {
1.8 millert 629: if (value("ignore") != NULL) {
1.1 deraadt 630: puts("@");
631: fflush(stdout);
632: clearerr(stdin);
633: return;
634: }
635: hadintr = 1;
1.8 millert 636: siglongjmp(colljmp, 1);
1.1 deraadt 637: }
638: rewind(collf);
1.8 millert 639: if (value("nosave") == NULL)
1.1 deraadt 640: savedeadletter(collf);
1.8 millert 641: siglongjmp(collabort, 1);
1.1 deraadt 642: }
643:
644: /*ARGSUSED*/
645: void
646: collhup(s)
647: int s;
648: {
649: rewind(collf);
650: savedeadletter(collf);
651: /*
652: * Let's pretend nobody else wants to clean up,
653: * a true statement at this time.
654: */
655: exit(1);
656: }
657:
658: void
659: savedeadletter(fp)
1.15 millert 660: FILE *fp;
1.1 deraadt 661: {
1.15 millert 662: FILE *dbuf;
663: int c;
1.1 deraadt 664: char *cp;
665:
666: if (fsize(fp) == 0)
667: return;
668: cp = getdeadletter();
669: c = umask(077);
670: dbuf = Fopen(cp, "a");
1.7 millert 671: (void)umask(c);
1.1 deraadt 672: if (dbuf == NULL)
673: return;
674: while ((c = getc(fp)) != EOF)
1.7 millert 675: (void)putc(c, dbuf);
1.6 millert 676: (void)Fclose(dbuf);
1.1 deraadt 677: rewind(fp);
1.11 millert 678: }
679:
680: void
681: gethfromtty(hp, gflags)
682: struct header *hp;
683: int gflags;
684: {
685: if (grabh(hp, gflags) == SIGINT) {
686: fflush(stdout);
687: fputs("\n(Interrupt -- one more to kill letter)\n",
688: stderr);
689: if (grabh(hp, gflags) == SIGINT) {
690: hadintr++;
691: collint(SIGINT);
692: exit(1);
693: }
694: }
1.1 deraadt 695: }