1*085f113bSokan /* $OpenBSD: collect.c,v 1.34 2014/01/17 18:42:30 okan Exp $ */
2db59c1a6Smillert /* $NetBSD: collect.c,v 1.9 1997/07/09 05:25:45 mikel Exp $ */
37eb34045Sderaadt
4df930be7Sderaadt /*
5df930be7Sderaadt * Copyright (c) 1980, 1993
6df930be7Sderaadt * The Regents of the University of California. All rights reserved.
7df930be7Sderaadt *
8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt * modification, are permitted provided that the following conditions
10df930be7Sderaadt * are met:
11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt * notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt * documentation and/or other materials provided with the distribution.
16f75387cbSmillert * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt * may be used to endorse or promote products derived from this software
18df930be7Sderaadt * without specific prior written permission.
19df930be7Sderaadt *
20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt * SUCH DAMAGE.
31df930be7Sderaadt */
32df930be7Sderaadt
33df930be7Sderaadt /*
34df930be7Sderaadt * Mail -- a mail program
35df930be7Sderaadt *
36df930be7Sderaadt * Collect input from standard input, handling
37df930be7Sderaadt * ~ escapes.
38df930be7Sderaadt */
39df930be7Sderaadt
40df930be7Sderaadt #include "rcv.h"
41df930be7Sderaadt #include "extern.h"
42df930be7Sderaadt
43df930be7Sderaadt /*
44df930be7Sderaadt * Read a message from standard output and return a read file to it
45df930be7Sderaadt * or NULL on error.
46df930be7Sderaadt */
47df930be7Sderaadt
48df930be7Sderaadt /*
49df930be7Sderaadt * The following hokiness with global variables is so that on
50df930be7Sderaadt * receipt of an interrupt signal, the partial message can be salted
51df930be7Sderaadt * away on dead.letter.
52df930be7Sderaadt */
53df930be7Sderaadt static FILE *collf; /* File for saving away */
54df930be7Sderaadt static int hadintr; /* Have seen one SIGINT so far */
55df930be7Sderaadt
56df930be7Sderaadt FILE *
collect(struct header * hp,int printheaders)574a9caef2Smillert collect(struct header *hp, int printheaders)
58df930be7Sderaadt {
59df930be7Sderaadt FILE *fbuf;
607f56ba93Smillert int lc, cc, fd, c, t, lastlong, rc, sig;
617f56ba93Smillert int escape, eofcount, longline;
627f56ba93Smillert char getsub;
63e6aad408Smillert char linebuf[LINESIZE], tempname[PATHSIZE], *cp;
64df930be7Sderaadt
65df930be7Sderaadt collf = NULL;
667f56ba93Smillert eofcount = 0;
677f56ba93Smillert hadintr = 0;
687f56ba93Smillert lastlong = 0;
697f56ba93Smillert longline = 0;
707f56ba93Smillert if ((cp = value("escape")) != NULL)
717f56ba93Smillert escape = *cp;
727f56ba93Smillert else
737f56ba93Smillert escape = ESCAPE;
74df930be7Sderaadt noreset++;
757f56ba93Smillert
760ecc72fcSmillert (void)snprintf(tempname, sizeof(tempname),
770ecc72fcSmillert "%s/mail.RsXXXXXXXXXX", tmpdir);
780ecc72fcSmillert if ((fd = mkstemp(tempname)) == -1 ||
790ecc72fcSmillert (collf = Fdopen(fd, "w+")) == NULL) {
80b638aa94Smillert warn("%s", tempname);
81df930be7Sderaadt goto err;
82df930be7Sderaadt }
830ecc72fcSmillert (void)rm(tempname);
84df930be7Sderaadt
85df930be7Sderaadt /*
86df930be7Sderaadt * If we are going to prompt for a subject,
87df930be7Sderaadt * refrain from printing a newline after
88df930be7Sderaadt * the headers (since some people mind).
89df930be7Sderaadt */
90df930be7Sderaadt t = GTO|GSUBJECT|GCC|GNL;
91df930be7Sderaadt getsub = 0;
92c318c72bSmillert if (hp->h_subject == NULL && value("interactive") != NULL &&
93c318c72bSmillert (value("ask") != NULL || value("asksub") != NULL))
94df930be7Sderaadt t &= ~GNL, getsub++;
95df930be7Sderaadt if (printheaders) {
96df930be7Sderaadt puthead(hp, stdout, t);
97df930be7Sderaadt fflush(stdout);
98df930be7Sderaadt }
997f56ba93Smillert if (getsub && gethfromtty(hp, GSUBJECT) == -1)
1007f56ba93Smillert goto err;
101df930be7Sderaadt
1027f56ba93Smillert if (0) {
103df930be7Sderaadt cont:
1047f56ba93Smillert /* Come here for printing the after-suspend message. */
1051c39f73cSderaadt if (isatty(0)) {
106db59c1a6Smillert puts("(continue)");
107df930be7Sderaadt fflush(stdout);
108df930be7Sderaadt }
109df930be7Sderaadt }
110df930be7Sderaadt for (;;) {
1117f56ba93Smillert c = readline(stdin, linebuf, LINESIZE, &sig);
1127f56ba93Smillert
1137f56ba93Smillert /* Act on any signal caught during readline() ignoring 'c' */
1147f56ba93Smillert switch (sig) {
1157f56ba93Smillert case 0:
1167f56ba93Smillert break;
1177f56ba93Smillert case SIGINT:
1187f56ba93Smillert if (collabort())
1197f56ba93Smillert goto err;
1207f56ba93Smillert continue;
1217f56ba93Smillert case SIGHUP:
1227f56ba93Smillert rewind(collf);
1237f56ba93Smillert savedeadletter(collf);
1247f56ba93Smillert /*
1257f56ba93Smillert * Let's pretend nobody else wants to clean up,
1267f56ba93Smillert * a true statement at this time.
1277f56ba93Smillert */
1287f56ba93Smillert exit(1);
1297f56ba93Smillert default:
1307f56ba93Smillert /* Stopped due to job control */
1317f56ba93Smillert (void)kill(0, sig);
1327f56ba93Smillert goto cont;
1337f56ba93Smillert }
1347f56ba93Smillert
1357f56ba93Smillert /* No signal, check for error */
136df930be7Sderaadt if (c < 0) {
137c318c72bSmillert if (value("interactive") != NULL &&
138c318c72bSmillert value("ignoreeof") != NULL && ++eofcount < 25) {
139db59c1a6Smillert puts("Use \".\" to terminate letter");
140df930be7Sderaadt continue;
141df930be7Sderaadt }
142df930be7Sderaadt break;
143df930be7Sderaadt }
144db59c1a6Smillert lastlong = longline;
145db59c1a6Smillert longline = (c == LINESIZE - 1);
146df930be7Sderaadt eofcount = 0;
147df930be7Sderaadt hadintr = 0;
148df930be7Sderaadt if (linebuf[0] == '.' && linebuf[1] == '\0' &&
149c318c72bSmillert value("interactive") != NULL && !lastlong &&
150c318c72bSmillert (value("dot") != NULL || value("ignoreeof") != NULL))
151df930be7Sderaadt break;
15237fc7e6fSmillert if (linebuf[0] != escape || value("interactive") == NULL ||
15337fc7e6fSmillert lastlong) {
154db59c1a6Smillert if (putline(collf, linebuf, !longline) < 0)
155df930be7Sderaadt goto err;
156df930be7Sderaadt continue;
157df930be7Sderaadt }
158*085f113bSokan c = (unsigned char)linebuf[1];
159df930be7Sderaadt switch (c) {
160df930be7Sderaadt default:
161df930be7Sderaadt /*
162df930be7Sderaadt * On double escape, just send the single one.
163df930be7Sderaadt * Otherwise, it's an error.
164df930be7Sderaadt */
165df930be7Sderaadt if (c == escape) {
166db59c1a6Smillert if (putline(collf, &linebuf[1], !longline) < 0)
167df930be7Sderaadt goto err;
168df930be7Sderaadt else
169df930be7Sderaadt break;
170df930be7Sderaadt }
171db59c1a6Smillert puts("Unknown tilde escape.");
172df930be7Sderaadt break;
173df930be7Sderaadt case '!':
174df930be7Sderaadt /*
175df930be7Sderaadt * Shell escape, send the balance of the
176df930be7Sderaadt * line to sh -c.
177df930be7Sderaadt */
178df930be7Sderaadt shell(&linebuf[2]);
179df930be7Sderaadt break;
180df930be7Sderaadt case ':':
181df930be7Sderaadt case '_':
182df930be7Sderaadt /*
183df930be7Sderaadt * Escape to command mode, but be nice!
184df930be7Sderaadt */
185df930be7Sderaadt execute(&linebuf[2], 1);
186df930be7Sderaadt goto cont;
187df930be7Sderaadt case '.':
188df930be7Sderaadt /*
189df930be7Sderaadt * Simulate end of file on input.
190df930be7Sderaadt */
191df930be7Sderaadt goto out;
192df930be7Sderaadt case 'q':
193df930be7Sderaadt /*
194df930be7Sderaadt * Force a quit of sending mail.
195df930be7Sderaadt * Act like an interrupt happened.
196df930be7Sderaadt */
197df930be7Sderaadt hadintr++;
1987f56ba93Smillert collabort();
1997f56ba93Smillert fputs("Interrupt\n", stderr);
2007f56ba93Smillert goto err;
201c09de76fSmartynas case 'x':
202c09de76fSmartynas /*
203c09de76fSmartynas * Force a quit of sending mail.
204c09de76fSmartynas * Do not save the message.
205c09de76fSmartynas */
206c09de76fSmartynas goto err;
207df930be7Sderaadt case 'h':
208df930be7Sderaadt /*
209df930be7Sderaadt * Grab a bunch of headers.
210df930be7Sderaadt */
211df930be7Sderaadt grabh(hp, GTO|GSUBJECT|GCC|GBCC);
212df930be7Sderaadt goto cont;
213df930be7Sderaadt case 't':
214df930be7Sderaadt /*
215df930be7Sderaadt * Add to the To list.
216df930be7Sderaadt */
217df930be7Sderaadt hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
218df930be7Sderaadt break;
219df930be7Sderaadt case 's':
220df930be7Sderaadt /*
221df930be7Sderaadt * Set the Subject list.
222df930be7Sderaadt */
223df930be7Sderaadt cp = &linebuf[2];
224*085f113bSokan while (isspace((unsigned char)*cp))
225df930be7Sderaadt cp++;
226df930be7Sderaadt hp->h_subject = savestr(cp);
227df930be7Sderaadt break;
228df930be7Sderaadt case 'c':
229df930be7Sderaadt /*
230df930be7Sderaadt * Add to the CC list.
231df930be7Sderaadt */
232df930be7Sderaadt hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
233df930be7Sderaadt break;
234df930be7Sderaadt case 'b':
235df930be7Sderaadt /*
236df930be7Sderaadt * Add stuff to blind carbon copies list.
237df930be7Sderaadt */
238df930be7Sderaadt hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
239df930be7Sderaadt break;
240df930be7Sderaadt case 'd':
2414a9caef2Smillert linebuf[2] = '\0';
2424a9caef2Smillert strlcat(linebuf, getdeadletter(), sizeof(linebuf));
243df930be7Sderaadt /* fall into . . . */
244df930be7Sderaadt case 'r':
245df930be7Sderaadt case '<':
246df930be7Sderaadt /*
247df930be7Sderaadt * Invoke a file:
248df930be7Sderaadt * Search for the file name,
249df930be7Sderaadt * then open it and copy the contents to collf.
250df930be7Sderaadt */
251df930be7Sderaadt cp = &linebuf[2];
252*085f113bSokan while (isspace((unsigned char)*cp))
253df930be7Sderaadt cp++;
254df930be7Sderaadt if (*cp == '\0') {
255db59c1a6Smillert puts("Interpolate what file?");
256df930be7Sderaadt break;
257df930be7Sderaadt }
258df930be7Sderaadt cp = expand(cp);
259c318c72bSmillert if (cp == NULL)
260df930be7Sderaadt break;
261df930be7Sderaadt if (isdir(cp)) {
262df930be7Sderaadt printf("%s: Directory\n", cp);
263df930be7Sderaadt break;
264df930be7Sderaadt }
265df930be7Sderaadt if ((fbuf = Fopen(cp, "r")) == NULL) {
266b638aa94Smillert warn("%s", cp);
267df930be7Sderaadt break;
268df930be7Sderaadt }
269df930be7Sderaadt printf("\"%s\" ", cp);
270df930be7Sderaadt fflush(stdout);
271df930be7Sderaadt lc = 0;
272df930be7Sderaadt cc = 0;
2737f56ba93Smillert while ((rc = readline(fbuf, linebuf, LINESIZE, NULL)) >= 0) {
274db59c1a6Smillert if (rc != LINESIZE - 1)
275df930be7Sderaadt lc++;
276db59c1a6Smillert if ((t = putline(collf, linebuf,
277db59c1a6Smillert rc != LINESIZE-1)) < 0) {
278db59c1a6Smillert (void)Fclose(fbuf);
279df930be7Sderaadt goto err;
280df930be7Sderaadt }
281df930be7Sderaadt cc += t;
282df930be7Sderaadt }
283db59c1a6Smillert (void)Fclose(fbuf);
284df930be7Sderaadt printf("%d/%d\n", lc, cc);
285df930be7Sderaadt break;
286df930be7Sderaadt case 'w':
287df930be7Sderaadt /*
288df930be7Sderaadt * Write the message on a file.
289df930be7Sderaadt */
290df930be7Sderaadt cp = &linebuf[2];
291df930be7Sderaadt while (*cp == ' ' || *cp == '\t')
292df930be7Sderaadt cp++;
293df930be7Sderaadt if (*cp == '\0') {
294db59c1a6Smillert fputs("Write what file!?\n", stderr);
295df930be7Sderaadt break;
296df930be7Sderaadt }
297c318c72bSmillert if ((cp = expand(cp)) == NULL)
298df930be7Sderaadt break;
299df930be7Sderaadt rewind(collf);
300df930be7Sderaadt exwrite(cp, collf, 1);
301df930be7Sderaadt break;
302df930be7Sderaadt case 'm':
303df930be7Sderaadt case 'M':
304df930be7Sderaadt case 'f':
305df930be7Sderaadt case 'F':
306df930be7Sderaadt /*
307df930be7Sderaadt * Interpolate the named messages, if we
308df930be7Sderaadt * are in receiving mail mode. Does the
309df930be7Sderaadt * standard list processing garbage.
310df930be7Sderaadt * If ~f is given, we don't shift over.
311df930be7Sderaadt */
3120ecc72fcSmillert if (forward(linebuf + 2, collf, tempname, c) < 0)
313df930be7Sderaadt goto err;
314df930be7Sderaadt goto cont;
315df930be7Sderaadt case '?':
316df930be7Sderaadt if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
317db59c1a6Smillert warn(_PATH_TILDE);
318df930be7Sderaadt break;
319df930be7Sderaadt }
320df930be7Sderaadt while ((t = getc(fbuf)) != EOF)
321df930be7Sderaadt (void)putchar(t);
322db59c1a6Smillert (void)Fclose(fbuf);
323df930be7Sderaadt break;
324df930be7Sderaadt case 'p':
325df930be7Sderaadt /*
326df930be7Sderaadt * Print out the current state of the
327df930be7Sderaadt * message without altering anything.
328df930be7Sderaadt */
329df930be7Sderaadt rewind(collf);
330db59c1a6Smillert puts("-------\nMessage contains:");
331df930be7Sderaadt puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
332df930be7Sderaadt while ((t = getc(collf)) != EOF)
333df930be7Sderaadt (void)putchar(t);
334df930be7Sderaadt goto cont;
335df930be7Sderaadt case '|':
336df930be7Sderaadt /*
337df930be7Sderaadt * Pipe message through command.
338df930be7Sderaadt * Collect output as new message.
339df930be7Sderaadt */
340df930be7Sderaadt rewind(collf);
341df930be7Sderaadt mespipe(collf, &linebuf[2]);
342df930be7Sderaadt goto cont;
343df930be7Sderaadt case 'v':
344df930be7Sderaadt case 'e':
345df930be7Sderaadt /*
346df930be7Sderaadt * Edit the current message.
347df930be7Sderaadt * 'e' means to use EDITOR
348df930be7Sderaadt * 'v' means to use VISUAL
349df930be7Sderaadt */
350df930be7Sderaadt rewind(collf);
351df930be7Sderaadt mesedit(collf, c);
352df930be7Sderaadt goto cont;
353df930be7Sderaadt }
354df930be7Sderaadt }
355db7aedf8Smillert
356db7aedf8Smillert if (value("interactive") != NULL) {
357db7aedf8Smillert if (value("askcc") != NULL || value("askbcc") != NULL) {
3587f56ba93Smillert if (value("askcc") != NULL) {
3597f56ba93Smillert if (gethfromtty(hp, GCC) == -1)
3607f56ba93Smillert goto err;
3617f56ba93Smillert }
3627f56ba93Smillert if (value("askbcc") != NULL) {
3637f56ba93Smillert if (gethfromtty(hp, GBCC) == -1)
3647f56ba93Smillert goto err;
3657f56ba93Smillert }
366db7aedf8Smillert } else {
367db7aedf8Smillert puts("EOT");
368db7aedf8Smillert (void)fflush(stdout);
369db7aedf8Smillert }
370db7aedf8Smillert }
371df930be7Sderaadt goto out;
372df930be7Sderaadt err:
373df930be7Sderaadt if (collf != NULL) {
374db59c1a6Smillert (void)Fclose(collf);
375df930be7Sderaadt collf = NULL;
376df930be7Sderaadt }
377df930be7Sderaadt out:
378df930be7Sderaadt if (collf != NULL)
379df930be7Sderaadt rewind(collf);
380df930be7Sderaadt noreset--;
381db59c1a6Smillert return(collf);
382df930be7Sderaadt }
383df930be7Sderaadt
384df930be7Sderaadt /*
385df930be7Sderaadt * Write a file, ex-like if f set.
386df930be7Sderaadt */
387df930be7Sderaadt int
exwrite(char * name,FILE * fp,int f)3884a9caef2Smillert exwrite(char *name, FILE *fp, int f)
389df930be7Sderaadt {
39036999bedSmillert FILE *of;
39136999bedSmillert int c;
392db7aedf8Smillert ssize_t cc, lc;
393df930be7Sderaadt struct stat junk;
394df930be7Sderaadt
395df930be7Sderaadt if (f) {
396df930be7Sderaadt printf("\"%s\" ", name);
397df930be7Sderaadt fflush(stdout);
398df930be7Sderaadt }
39936999bedSmillert if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
400df930be7Sderaadt if (!f)
401df930be7Sderaadt fprintf(stderr, "%s: ", name);
402db59c1a6Smillert fputs("File exists\n", stderr);
403df930be7Sderaadt return(-1);
404df930be7Sderaadt }
405df930be7Sderaadt if ((of = Fopen(name, "w")) == NULL) {
406c318c72bSmillert warn(NULL);
407df930be7Sderaadt return(-1);
408df930be7Sderaadt }
409df930be7Sderaadt lc = 0;
410df930be7Sderaadt cc = 0;
411df930be7Sderaadt while ((c = getc(fp)) != EOF) {
412df930be7Sderaadt cc++;
413df930be7Sderaadt if (c == '\n')
414df930be7Sderaadt lc++;
415df930be7Sderaadt (void)putc(c, of);
416df930be7Sderaadt if (ferror(of)) {
417b638aa94Smillert warn("%s", name);
418db59c1a6Smillert (void)Fclose(of);
419df930be7Sderaadt return(-1);
420df930be7Sderaadt }
421df930be7Sderaadt }
422db59c1a6Smillert (void)Fclose(of);
4232eba0a68Smillert printf("%lld/%lld\n", (long long)lc, (long long)cc);
424df930be7Sderaadt fflush(stdout);
425df930be7Sderaadt return(0);
426df930be7Sderaadt }
427df930be7Sderaadt
428df930be7Sderaadt /*
429df930be7Sderaadt * Edit the message being collected on fp.
430df930be7Sderaadt * On return, make the edit file the new temp file.
431df930be7Sderaadt */
432df930be7Sderaadt void
mesedit(FILE * fp,int c)4334a9caef2Smillert mesedit(FILE *fp, int c)
434df930be7Sderaadt {
4357f56ba93Smillert FILE *nf;
4367f56ba93Smillert struct sigaction oact;
4377f56ba93Smillert sigset_t oset;
438df930be7Sderaadt
4397f56ba93Smillert (void)ignoresig(SIGINT, &oact, &oset);
4407f56ba93Smillert nf = run_editor(fp, (off_t)-1, c, 0);
441df930be7Sderaadt if (nf != NULL) {
442a52c79cdStobias fseek(nf, 0L, SEEK_END);
443df930be7Sderaadt collf = nf;
444db59c1a6Smillert (void)Fclose(fp);
445df930be7Sderaadt }
4467f56ba93Smillert (void)sigprocmask(SIG_SETMASK, &oset, NULL);
4477f56ba93Smillert (void)sigaction(SIGINT, &oact, NULL);
448df930be7Sderaadt }
449df930be7Sderaadt
450df930be7Sderaadt /*
451df930be7Sderaadt * Pipe the message through the command.
452df930be7Sderaadt * Old message is on stdin of command;
453df930be7Sderaadt * New message collected from stdout.
454df930be7Sderaadt * Sh -c must return 0 to accept the new message.
455df930be7Sderaadt */
456df930be7Sderaadt void
mespipe(FILE * fp,char * cmd)4574a9caef2Smillert mespipe(FILE *fp, char *cmd)
458df930be7Sderaadt {
459df930be7Sderaadt FILE *nf;
4600ecc72fcSmillert int fd;
4610ecc72fcSmillert char *shell, tempname[PATHSIZE];
4627f56ba93Smillert struct sigaction oact;
4637f56ba93Smillert sigset_t oset;
464df930be7Sderaadt
4657f56ba93Smillert (void)ignoresig(SIGINT, &oact, &oset);
4660ecc72fcSmillert (void)snprintf(tempname, sizeof(tempname),
4670ecc72fcSmillert "%s/mail.ReXXXXXXXXXX", tmpdir);
4680ecc72fcSmillert if ((fd = mkstemp(tempname)) == -1 ||
4690ecc72fcSmillert (nf = Fdopen(fd, "w+")) == NULL) {
470b638aa94Smillert warn("%s", tempname);
471df930be7Sderaadt goto out;
472df930be7Sderaadt }
4730ecc72fcSmillert (void)rm(tempname);
474df930be7Sderaadt /*
475df930be7Sderaadt * stdin = current message.
476df930be7Sderaadt * stdout = new message.
477df930be7Sderaadt */
478ca8b07b0Smillert shell = value("SHELL");
479df930be7Sderaadt if (run_command(shell,
480c318c72bSmillert 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
481df930be7Sderaadt (void)Fclose(nf);
482df930be7Sderaadt goto out;
483df930be7Sderaadt }
484df930be7Sderaadt if (fsize(nf) == 0) {
485df930be7Sderaadt fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
486df930be7Sderaadt (void)Fclose(nf);
487df930be7Sderaadt goto out;
488df930be7Sderaadt }
489df930be7Sderaadt /*
490df930be7Sderaadt * Take new files.
491df930be7Sderaadt */
492a52c79cdStobias (void)fseek(nf, 0L, SEEK_END);
493df930be7Sderaadt collf = nf;
494df930be7Sderaadt (void)Fclose(fp);
495df930be7Sderaadt out:
4967f56ba93Smillert (void)sigprocmask(SIG_SETMASK, &oset, NULL);
4977f56ba93Smillert (void)sigaction(SIGINT, &oact, NULL);
498df930be7Sderaadt }
499df930be7Sderaadt
500df930be7Sderaadt /*
501df930be7Sderaadt * Interpolate the named messages into the current
502df930be7Sderaadt * message, preceding each line with a tab.
503df930be7Sderaadt * Return a count of the number of characters now in
504df930be7Sderaadt * the message, or -1 if an error is encountered writing
505df930be7Sderaadt * the message temporary. The flag argument is 'm' if we
506df930be7Sderaadt * should shift over and 'f' if not.
507df930be7Sderaadt */
508df930be7Sderaadt int
forward(char * ms,FILE * fp,char * fn,int f)5094a9caef2Smillert forward(char *ms, FILE *fp, char *fn, int f)
510df930be7Sderaadt {
51136999bedSmillert int *msgvec;
512df930be7Sderaadt struct ignoretab *ig;
513df930be7Sderaadt char *tabst;
514df930be7Sderaadt
515db59c1a6Smillert msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
516c318c72bSmillert if (msgvec == NULL)
517df930be7Sderaadt return(0);
518df930be7Sderaadt if (getmsglist(ms, msgvec, 0) < 0)
519df930be7Sderaadt return(0);
520df930be7Sderaadt if (*msgvec == 0) {
521df930be7Sderaadt *msgvec = first(0, MMNORM);
522ca9f5f35Savsm if (*msgvec == 0) {
523db59c1a6Smillert puts("No appropriate messages");
524df930be7Sderaadt return(0);
525df930be7Sderaadt }
526691235adSmiod msgvec[1] = 0;
527df930be7Sderaadt }
5280ab8ef84Smartynas if (tolower(f) == 'f')
529c318c72bSmillert tabst = NULL;
530c318c72bSmillert else if ((tabst = value("indentprefix")) == NULL)
531df930be7Sderaadt tabst = "\t";
532df930be7Sderaadt ig = isupper(f) ? NULL : ignore;
533db59c1a6Smillert fputs("Interpolating:", stdout);
534df930be7Sderaadt for (; *msgvec != 0; msgvec++) {
535df930be7Sderaadt struct message *mp = message + *msgvec - 1;
536df930be7Sderaadt
537df930be7Sderaadt touch(mp);
538df930be7Sderaadt printf(" %d", *msgvec);
53980d25fdaSmillert if (sendmessage(mp, fp, ig, tabst) < 0) {
540b638aa94Smillert warn("%s", fn);
541df930be7Sderaadt return(-1);
542df930be7Sderaadt }
543df930be7Sderaadt }
544db59c1a6Smillert putchar('\n');
545df930be7Sderaadt return(0);
546df930be7Sderaadt }
547df930be7Sderaadt
548df930be7Sderaadt /*
5497f56ba93Smillert * User aborted during message composition.
5507f56ba93Smillert * Save the partial message in ~/dead.letter.
551df930be7Sderaadt */
5527f56ba93Smillert int
collabort(void)5534a9caef2Smillert collabort(void)
554df930be7Sderaadt {
555df930be7Sderaadt /*
556df930be7Sderaadt * the control flow is subtle, because we can be called from ~q.
557df930be7Sderaadt */
5583d45000cSderaadt if (hadintr == 0 && isatty(0)) {
559c318c72bSmillert if (value("ignore") != NULL) {
560df930be7Sderaadt puts("@");
561df930be7Sderaadt fflush(stdout);
562df930be7Sderaadt clearerr(stdin);
5637f56ba93Smillert } else {
5647f56ba93Smillert fflush(stdout);
5657f56ba93Smillert fputs("\n(Interrupt -- one more to kill letter)\n",
5667f56ba93Smillert stderr);
5677f56ba93Smillert hadintr++;
568df930be7Sderaadt }
5697f56ba93Smillert return(0);
570df930be7Sderaadt }
5717f56ba93Smillert fflush(stdout);
572df930be7Sderaadt rewind(collf);
573c318c72bSmillert if (value("nosave") == NULL)
574df930be7Sderaadt savedeadletter(collf);
5757f56ba93Smillert return(1);
576df930be7Sderaadt }
577df930be7Sderaadt
578df930be7Sderaadt void
savedeadletter(FILE * fp)5794a9caef2Smillert savedeadletter(FILE *fp)
580df930be7Sderaadt {
58136999bedSmillert FILE *dbuf;
58236999bedSmillert int c;
583df930be7Sderaadt char *cp;
584df930be7Sderaadt
585df930be7Sderaadt if (fsize(fp) == 0)
586df930be7Sderaadt return;
587df930be7Sderaadt cp = getdeadletter();
588df930be7Sderaadt c = umask(077);
589df930be7Sderaadt dbuf = Fopen(cp, "a");
590df930be7Sderaadt (void)umask(c);
591df930be7Sderaadt if (dbuf == NULL)
592df930be7Sderaadt return;
593df930be7Sderaadt while ((c = getc(fp)) != EOF)
594df930be7Sderaadt (void)putc(c, dbuf);
595db59c1a6Smillert (void)Fclose(dbuf);
596df930be7Sderaadt rewind(fp);
597df930be7Sderaadt }
598db7aedf8Smillert
5997f56ba93Smillert int
gethfromtty(struct header * hp,int gflags)6004a9caef2Smillert gethfromtty(struct header *hp, int gflags)
601db7aedf8Smillert {
6027f56ba93Smillert
6037f56ba93Smillert hadintr = 0;
6047f56ba93Smillert while (grabh(hp, gflags) != 0) {
6057f56ba93Smillert if (collabort())
6067f56ba93Smillert return(-1);
607db7aedf8Smillert }
6087f56ba93Smillert return(0);
609db7aedf8Smillert }
610