Annotation of src/usr.bin/mail/collect.c, Revision 1.12
1.12 ! millert 1: /* $OpenBSD: collect.c,v 1.11 1997/07/22 18:54:36 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.12 ! millert 41: static char rcsid[] = "$OpenBSD: collect.c,v 1.11 1997/07/22 18:54:36 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.12 ! millert 84: int lc, cc, escape, eofcount, fd;
1.1 deraadt 85: register int c, t;
1.12 ! millert 86: char linebuf[LINESIZE], tempname[PATHSIZE], *cp;
1.1 deraadt 87: char getsub;
1.9 millert 88: sigset_t oset, nset;
1.6 millert 89: int longline, lastlong, rc; /* Can deal with lines > LINESIZE */
90:
1.2 deraadt 91: #if __GNUC__
1.8 millert 92: /* Avoid siglongjmp clobbering */
1.7 millert 93: (void)&escape;
94: (void)&eofcount;
95: (void)&getsub;
96: (void)&longline;
1.2 deraadt 97: #endif
1.1 deraadt 98:
99: collf = NULL;
100: /*
101: * Start catching signals from here, but we're still die on interrupts
102: * until we're in the main loop.
103: */
1.9 millert 104: sigemptyset(&nset);
105: sigaddset(&nset, SIGINT);
106: sigaddset(&nset, SIGHUP);
107: sigprocmask(SIG_BLOCK, &nset, &oset);
1.1 deraadt 108: if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
109: signal(SIGINT, collint);
110: if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
111: signal(SIGHUP, collhup);
112: savetstp = signal(SIGTSTP, collstop);
113: savettou = signal(SIGTTOU, collstop);
114: savettin = signal(SIGTTIN, collstop);
1.8 millert 115: if (sigsetjmp(collabort, 1) || sigsetjmp(colljmp, 1)) {
1.12 ! millert 116: (void)rm(tempname);
1.1 deraadt 117: goto err;
118: }
1.9 millert 119: sigdelset(&oset, SIGINT);
120: sigdelset(&oset, SIGHUP);
121: sigprocmask(SIG_SETMASK, &oset, NULL);
1.1 deraadt 122:
123: noreset++;
1.12 ! millert 124: (void)snprintf(tempname, sizeof(tempname),
! 125: "%s/mail.RsXXXXXXXXXX", tmpdir);
! 126: if ((fd = mkstemp(tempname)) == -1 ||
! 127: (collf = Fdopen(fd, "w+")) == NULL) {
! 128: warn(tempname);
1.1 deraadt 129: goto err;
130: }
1.12 ! millert 131: (void)rm(tempname);
1.1 deraadt 132:
133: /*
134: * If we are going to prompt for a subject,
135: * refrain from printing a newline after
136: * the headers (since some people mind).
137: */
138: t = GTO|GSUBJECT|GCC|GNL;
139: getsub = 0;
1.8 millert 140: if (hp->h_subject == NULL && value("interactive") != NULL &&
141: (value("ask") != NULL || value("asksub") != NULL))
1.1 deraadt 142: t &= ~GNL, getsub++;
143: if (printheaders) {
144: puthead(hp, stdout, t);
145: fflush(stdout);
146: }
1.8 millert 147: if ((cp = value("escape")) != NULL)
1.1 deraadt 148: escape = *cp;
149: else
150: escape = ESCAPE;
151: eofcount = 0;
152: hadintr = 0;
1.6 millert 153: lastlong = 0;
154: longline = 0;
1.1 deraadt 155:
1.8 millert 156: if (!sigsetjmp(colljmp, 1)) {
1.11 millert 157: if (getsub)
158: gethfromtty(hp, GSUBJECT);
1.1 deraadt 159: } else {
160: /*
161: * Come here for printing the after-signal message.
162: * Duplicate messages won't be printed because
163: * the write is aborted if we get a SIGTTOU.
164: */
165: cont:
166: if (hadintr) {
167: fflush(stdout);
1.6 millert 168: fputs("\n(Interrupt -- one more to kill letter)\n",
169: stderr);
1.1 deraadt 170: } else {
1.4 deraadt 171: if (isatty(0)) {
1.6 millert 172: puts("(continue)");
1.4 deraadt 173: fflush(stdout);
174: }
1.1 deraadt 175: }
176: }
177: for (;;) {
178: colljmp_p = 1;
179: c = readline(stdin, linebuf, LINESIZE);
180: colljmp_p = 0;
181: if (c < 0) {
1.8 millert 182: if (value("interactive") != NULL &&
183: value("ignoreeof") != NULL && ++eofcount < 25) {
1.6 millert 184: puts("Use \".\" to terminate letter");
1.1 deraadt 185: continue;
186: }
187: break;
188: }
1.6 millert 189: lastlong = longline;
190: longline = (c == LINESIZE - 1);
1.1 deraadt 191: eofcount = 0;
192: hadintr = 0;
193: if (linebuf[0] == '.' && linebuf[1] == '\0' &&
1.8 millert 194: value("interactive") != NULL && !lastlong &&
195: (value("dot") != NULL || value("ignoreeof") != NULL))
1.1 deraadt 196: break;
1.8 millert 197: if (linebuf[0] != escape || value("interactive") == NULL ||
1.6 millert 198: lastlong) {
199: if (putline(collf, linebuf, !longline) < 0)
1.1 deraadt 200: goto err;
201: continue;
202: }
203: c = linebuf[1];
204: switch (c) {
205: default:
206: /*
207: * On double escape, just send the single one.
208: * Otherwise, it's an error.
209: */
210: if (c == escape) {
1.6 millert 211: if (putline(collf, &linebuf[1], !longline) < 0)
1.1 deraadt 212: goto err;
213: else
214: break;
215: }
1.6 millert 216: puts("Unknown tilde escape.");
1.1 deraadt 217: break;
218: case 'C':
219: /*
220: * Dump core.
221: */
1.2 deraadt 222: core(NULL);
1.1 deraadt 223: break;
224: case '!':
225: /*
226: * Shell escape, send the balance of the
227: * line to sh -c.
228: */
229: shell(&linebuf[2]);
230: break;
231: case ':':
232: case '_':
233: /*
234: * Escape to command mode, but be nice!
235: */
236: execute(&linebuf[2], 1);
237: goto cont;
238: case '.':
239: /*
240: * Simulate end of file on input.
241: */
242: goto out;
243: case 'q':
244: /*
245: * Force a quit of sending mail.
246: * Act like an interrupt happened.
247: */
248: hadintr++;
249: collint(SIGINT);
250: exit(1);
251: case 'h':
252: /*
253: * Grab a bunch of headers.
254: */
255: grabh(hp, GTO|GSUBJECT|GCC|GBCC);
256: goto cont;
257: case 't':
258: /*
259: * Add to the To list.
260: */
261: hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
262: break;
263: case 's':
264: /*
265: * Set the Subject list.
266: */
267: cp = &linebuf[2];
268: while (isspace(*cp))
269: cp++;
270: hp->h_subject = savestr(cp);
271: break;
272: case 'c':
273: /*
274: * Add to the CC list.
275: */
276: hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
277: break;
278: case 'b':
279: /*
280: * Add stuff to blind carbon copies list.
281: */
282: hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
283: break;
284: case 'd':
285: strcpy(linebuf + 2, getdeadletter());
286: /* fall into . . . */
287: case 'r':
288: case '<':
289: /*
290: * Invoke a file:
291: * Search for the file name,
292: * then open it and copy the contents to collf.
293: */
294: cp = &linebuf[2];
295: while (isspace(*cp))
296: cp++;
297: if (*cp == '\0') {
1.6 millert 298: puts("Interpolate what file?");
1.1 deraadt 299: break;
300: }
301: cp = expand(cp);
1.8 millert 302: if (cp == NULL)
1.1 deraadt 303: break;
304: if (isdir(cp)) {
305: printf("%s: Directory\n", cp);
306: break;
307: }
308: if ((fbuf = Fopen(cp, "r")) == NULL) {
1.6 millert 309: warn(cp);
1.1 deraadt 310: break;
311: }
312: printf("\"%s\" ", cp);
313: fflush(stdout);
314: lc = 0;
315: cc = 0;
1.6 millert 316: while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) {
317: if (rc != LINESIZE - 1)
318: lc++;
319: if ((t = putline(collf, linebuf,
320: rc != LINESIZE-1)) < 0) {
321: (void)Fclose(fbuf);
1.1 deraadt 322: goto err;
323: }
324: cc += t;
325: }
1.6 millert 326: (void)Fclose(fbuf);
1.1 deraadt 327: printf("%d/%d\n", lc, cc);
328: break;
329: case 'w':
330: /*
331: * Write the message on a file.
332: */
333: cp = &linebuf[2];
334: while (*cp == ' ' || *cp == '\t')
335: cp++;
336: if (*cp == '\0') {
1.6 millert 337: fputs("Write what file!?\n", stderr);
1.1 deraadt 338: break;
339: }
1.8 millert 340: if ((cp = expand(cp)) == NULL)
1.1 deraadt 341: break;
342: rewind(collf);
343: exwrite(cp, collf, 1);
344: break;
345: case 'm':
346: case 'M':
347: case 'f':
348: case 'F':
349: /*
350: * Interpolate the named messages, if we
351: * are in receiving mail mode. Does the
352: * standard list processing garbage.
353: * If ~f is given, we don't shift over.
354: */
1.12 ! millert 355: if (forward(linebuf + 2, collf, tempname, c) < 0)
1.1 deraadt 356: goto err;
357: goto cont;
358: case '?':
359: if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
1.6 millert 360: warn(_PATH_TILDE);
1.1 deraadt 361: break;
362: }
363: while ((t = getc(fbuf)) != EOF)
1.7 millert 364: (void)putchar(t);
1.6 millert 365: (void)Fclose(fbuf);
1.1 deraadt 366: break;
367: case 'p':
368: /*
369: * Print out the current state of the
370: * message without altering anything.
371: */
372: rewind(collf);
1.6 millert 373: puts("-------\nMessage contains:");
1.1 deraadt 374: puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
375: while ((t = getc(collf)) != EOF)
1.7 millert 376: (void)putchar(t);
1.1 deraadt 377: goto cont;
378: case '|':
379: /*
380: * Pipe message through command.
381: * Collect output as new message.
382: */
383: rewind(collf);
384: mespipe(collf, &linebuf[2]);
385: goto cont;
386: case 'v':
387: case 'e':
388: /*
389: * Edit the current message.
390: * 'e' means to use EDITOR
391: * 'v' means to use VISUAL
392: */
393: rewind(collf);
394: mesedit(collf, c);
395: goto cont;
396: }
397: }
1.11 millert 398:
399: if (value("interactive") != NULL) {
400: if (value("askcc") != NULL || value("askbcc") != NULL) {
401: if (value("askcc") != NULL)
402: gethfromtty(hp, GCC);
403: if (value("askbcc") != NULL)
404: gethfromtty(hp, GBCC);
405: } else {
406: puts("EOT");
407: (void)fflush(stdout);
408: }
409: }
1.1 deraadt 410: goto out;
411: err:
412: if (collf != NULL) {
1.6 millert 413: (void)Fclose(collf);
1.1 deraadt 414: collf = NULL;
415: }
416: out:
417: if (collf != NULL)
418: rewind(collf);
419: noreset--;
1.9 millert 420: sigemptyset(&nset);
421: sigaddset(&nset, SIGINT);
422: sigaddset(&nset, SIGHUP);
423: sigprocmask(SIG_BLOCK, &nset, &oset);
1.1 deraadt 424: signal(SIGINT, saveint);
425: signal(SIGHUP, savehup);
426: signal(SIGTSTP, savetstp);
427: signal(SIGTTOU, savettou);
428: signal(SIGTTIN, savettin);
1.9 millert 429: sigprocmask(SIG_SETMASK, &oset, NULL);
1.6 millert 430: return(collf);
1.1 deraadt 431: }
432:
433: /*
434: * Write a file, ex-like if f set.
435: */
436: int
437: exwrite(name, fp, f)
438: char name[];
439: FILE *fp;
440: int f;
441: {
442: register FILE *of;
443: register int c;
1.11 millert 444: ssize_t cc, lc;
1.1 deraadt 445: struct stat junk;
446:
447: if (f) {
448: printf("\"%s\" ", name);
449: fflush(stdout);
450: }
451: if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
452: if (!f)
453: fprintf(stderr, "%s: ", name);
1.6 millert 454: fputs("File exists\n", stderr);
1.1 deraadt 455: return(-1);
456: }
457: if ((of = Fopen(name, "w")) == NULL) {
1.8 millert 458: warn(NULL);
1.1 deraadt 459: return(-1);
460: }
461: lc = 0;
462: cc = 0;
463: while ((c = getc(fp)) != EOF) {
464: cc++;
465: if (c == '\n')
466: lc++;
1.7 millert 467: (void)putc(c, of);
1.1 deraadt 468: if (ferror(of)) {
1.6 millert 469: warn(name);
470: (void)Fclose(of);
1.1 deraadt 471: return(-1);
472: }
473: }
1.6 millert 474: (void)Fclose(of);
1.11 millert 475: printf("%d/%d\n", lc, cc);
1.1 deraadt 476: fflush(stdout);
477: return(0);
478: }
479:
480: /*
481: * Edit the message being collected on fp.
482: * On return, make the edit file the new temp file.
483: */
484: void
485: mesedit(fp, c)
486: FILE *fp;
487: int c;
488: {
489: sig_t sigint = signal(SIGINT, SIG_IGN);
490: FILE *nf = run_editor(fp, (off_t)-1, c, 0);
491:
492: if (nf != NULL) {
493: fseek(nf, 0L, 2);
494: collf = nf;
1.6 millert 495: (void)Fclose(fp);
1.1 deraadt 496: }
1.7 millert 497: (void)signal(SIGINT, sigint);
1.1 deraadt 498: }
499:
500: /*
501: * Pipe the message through the command.
502: * Old message is on stdin of command;
503: * New message collected from stdout.
504: * Sh -c must return 0 to accept the new message.
505: */
506: void
507: mespipe(fp, cmd)
508: FILE *fp;
509: char cmd[];
510: {
511: FILE *nf;
1.12 ! millert 512: int fd;
1.1 deraadt 513: sig_t sigint = signal(SIGINT, SIG_IGN);
1.12 ! millert 514: char *shell, tempname[PATHSIZE];
1.1 deraadt 515:
1.12 ! millert 516: (void)snprintf(tempname, sizeof(tempname),
! 517: "%s/mail.ReXXXXXXXXXX", tmpdir);
! 518: if ((fd = mkstemp(tempname)) == -1 ||
! 519: (nf = Fdopen(fd, "w+")) == NULL) {
! 520: warn(tempname);
1.1 deraadt 521: goto out;
522: }
1.12 ! millert 523: (void)rm(tempname);
1.1 deraadt 524: /*
525: * stdin = current message.
526: * stdout = new message.
527: */
1.8 millert 528: if ((shell = value("SHELL")) == NULL)
1.1 deraadt 529: shell = _PATH_CSHELL;
530: if (run_command(shell,
1.8 millert 531: 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
1.6 millert 532: (void)Fclose(nf);
1.1 deraadt 533: goto out;
534: }
535: if (fsize(nf) == 0) {
536: fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
1.6 millert 537: (void)Fclose(nf);
1.1 deraadt 538: goto out;
539: }
540: /*
541: * Take new files.
542: */
1.7 millert 543: (void)fseek(nf, 0L, 2);
1.1 deraadt 544: collf = nf;
1.6 millert 545: (void)Fclose(fp);
1.1 deraadt 546: out:
1.7 millert 547: (void)signal(SIGINT, sigint);
1.1 deraadt 548: }
549:
550: /*
551: * Interpolate the named messages into the current
552: * message, preceding each line with a tab.
553: * Return a count of the number of characters now in
554: * the message, or -1 if an error is encountered writing
555: * the message temporary. The flag argument is 'm' if we
556: * should shift over and 'f' if not.
557: */
558: int
1.12 ! millert 559: forward(ms, fp, fn, f)
1.1 deraadt 560: char ms[];
561: FILE *fp;
1.12 ! millert 562: char *fn;
1.1 deraadt 563: int f;
564: {
565: register int *msgvec;
566: struct ignoretab *ig;
567: char *tabst;
568:
1.7 millert 569: msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
1.8 millert 570: if (msgvec == NULL)
1.1 deraadt 571: return(0);
572: if (getmsglist(ms, msgvec, 0) < 0)
573: return(0);
574: if (*msgvec == 0) {
575: *msgvec = first(0, MMNORM);
576: if (*msgvec == NULL) {
1.6 millert 577: puts("No appropriate messages");
1.1 deraadt 578: return(0);
579: }
580: msgvec[1] = NULL;
581: }
582: if (f == 'f' || f == 'F')
1.8 millert 583: tabst = NULL;
584: else if ((tabst = value("indentprefix")) == NULL)
1.1 deraadt 585: tabst = "\t";
586: ig = isupper(f) ? NULL : ignore;
1.6 millert 587: fputs("Interpolating:", stdout);
1.1 deraadt 588: for (; *msgvec != 0; msgvec++) {
589: struct message *mp = message + *msgvec - 1;
590:
591: touch(mp);
592: printf(" %d", *msgvec);
593: if (send(mp, fp, ig, tabst) < 0) {
1.12 ! millert 594: warn(fn);
1.1 deraadt 595: return(-1);
596: }
597: }
1.6 millert 598: putchar('\n');
1.1 deraadt 599: return(0);
600: }
601:
602: /*
603: * Print (continue) when continued after ^Z.
604: */
605: /*ARGSUSED*/
606: void
607: collstop(s)
608: int s;
609: {
610: sig_t old_action = signal(s, SIG_DFL);
1.2 deraadt 611: sigset_t nset;
1.1 deraadt 612:
1.2 deraadt 613: sigemptyset(&nset);
614: sigaddset(&nset, s);
615: sigprocmask(SIG_UNBLOCK, &nset, NULL);
1.1 deraadt 616: kill(0, s);
1.2 deraadt 617: sigprocmask(SIG_BLOCK, &nset, NULL);
1.1 deraadt 618: signal(s, old_action);
619: if (colljmp_p) {
620: colljmp_p = 0;
621: hadintr = 0;
1.8 millert 622: siglongjmp(colljmp, 1);
1.1 deraadt 623: }
624: }
625:
626: /*
627: * On interrupt, come here to save the partial message in ~/dead.letter.
628: * Then jump out of the collection loop.
629: */
630: /*ARGSUSED*/
631: void
632: collint(s)
633: int s;
634: {
635: /*
636: * the control flow is subtle, because we can be called from ~q.
637: */
1.5 deraadt 638: if (hadintr == 0 && isatty(0)) {
1.8 millert 639: if (value("ignore") != NULL) {
1.1 deraadt 640: puts("@");
641: fflush(stdout);
642: clearerr(stdin);
643: return;
644: }
645: hadintr = 1;
1.8 millert 646: siglongjmp(colljmp, 1);
1.1 deraadt 647: }
648: rewind(collf);
1.8 millert 649: if (value("nosave") == NULL)
1.1 deraadt 650: savedeadletter(collf);
1.8 millert 651: siglongjmp(collabort, 1);
1.1 deraadt 652: }
653:
654: /*ARGSUSED*/
655: void
656: collhup(s)
657: int s;
658: {
659: rewind(collf);
660: savedeadletter(collf);
661: /*
662: * Let's pretend nobody else wants to clean up,
663: * a true statement at this time.
664: */
665: exit(1);
666: }
667:
668: void
669: savedeadletter(fp)
670: register FILE *fp;
671: {
672: register FILE *dbuf;
673: register int c;
674: char *cp;
675:
676: if (fsize(fp) == 0)
677: return;
678: cp = getdeadletter();
679: c = umask(077);
680: dbuf = Fopen(cp, "a");
1.7 millert 681: (void)umask(c);
1.1 deraadt 682: if (dbuf == NULL)
683: return;
684: while ((c = getc(fp)) != EOF)
1.7 millert 685: (void)putc(c, dbuf);
1.6 millert 686: (void)Fclose(dbuf);
1.1 deraadt 687: rewind(fp);
1.11 millert 688: }
689:
690: void
691: gethfromtty(hp, gflags)
692: struct header *hp;
693: int gflags;
694: {
695: if (grabh(hp, gflags) == SIGINT) {
696: fflush(stdout);
697: fputs("\n(Interrupt -- one more to kill letter)\n",
698: stderr);
699: if (grabh(hp, gflags) == SIGINT) {
700: hadintr++;
701: collint(SIGINT);
702: exit(1);
703: }
704: }
1.1 deraadt 705: }