122448Sdist /*
262083Sbostic * Copyright (c) 1980, 1993
362083Sbostic * The Regents of the University of California. All rights reserved.
433499Sbostic *
542741Sbostic * %sccs.include.redist.c%
622448Sdist */
722448Sdist
834905Sbostic #ifndef lint
9*66999Sedward static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 04/19/94";
1034905Sbostic #endif /* not lint */
111227Skas
121227Skas /*
131227Skas * Mail -- a mail program
141227Skas *
151227Skas * Collect input from standard input, handling
161227Skas * ~ escapes.
171227Skas */
181227Skas
191227Skas #include "rcv.h"
2054505Sbostic #include "extern.h"
211227Skas
221227Skas /*
231227Skas * Read a message from standard output and return a read file to it
241227Skas * or NULL on error.
251227Skas */
261227Skas
271227Skas /*
281227Skas * The following hokiness with global variables is so that on
291227Skas * receipt of an interrupt signal, the partial message can be salted
3031142Sedward * away on dead.letter.
311227Skas */
321227Skas
3339162Sbostic static sig_t saveint; /* Previous SIGINT value */
3439162Sbostic static sig_t savehup; /* Previous SIGHUP value */
3539162Sbostic static sig_t savetstp; /* Previous SIGTSTP value */
3639162Sbostic static sig_t savettou; /* Previous SIGTTOU value */
3739162Sbostic static sig_t savettin; /* Previous SIGTTIN value */
3831142Sedward static FILE *collf; /* File for saving away */
391227Skas static int hadintr; /* Have seen one SIGINT so far */
401227Skas
4136554Sedward static jmp_buf colljmp; /* To get back to work */
4236554Sedward static int colljmp_p; /* whether to long jump */
4336554Sedward static jmp_buf collabort; /* To end collection with error */
441227Skas
451227Skas FILE *
collect(hp,printheaders)4634800Sedward collect(hp, printheaders)
471227Skas struct header *hp;
4854505Sbostic int printheaders;
491227Skas {
5034963Sedward FILE *fbuf;
5136554Sedward int lc, cc, escape, eofcount;
521227Skas register int c, t;
531227Skas char linebuf[LINESIZE], *cp;
541227Skas extern char tempMail[];
5516732Sralph char getsub;
5631142Sedward int omask;
5747706Sbostic void collint(), collhup(), collstop();
581227Skas
5931142Sedward collf = NULL;
6031142Sedward /*
6131142Sedward * Start catching signals from here, but we're still die on interrupts
6231142Sedward * until we're in the main loop.
6331142Sedward */
6431142Sedward omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
6531142Sedward if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
6636554Sedward signal(SIGINT, collint);
6731142Sedward if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
6836554Sedward signal(SIGHUP, collhup);
6936554Sedward savetstp = signal(SIGTSTP, collstop);
7036554Sedward savettou = signal(SIGTTOU, collstop);
7136554Sedward savettin = signal(SIGTTIN, collstop);
7236554Sedward if (setjmp(collabort) || setjmp(colljmp)) {
7346349Sedward rm(tempMail);
741227Skas goto err;
751227Skas }
7631142Sedward sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
7731142Sedward
7836554Sedward noreset++;
7943865Sedward if ((collf = Fopen(tempMail, "w+")) == NULL) {
801227Skas perror(tempMail);
811227Skas goto err;
821227Skas }
8334963Sedward unlink(tempMail);
841227Skas
851227Skas /*
861227Skas * If we are going to prompt for a subject,
871227Skas * refrain from printing a newline after
881227Skas * the headers (since some people mind).
891227Skas */
901227Skas t = GTO|GSUBJECT|GCC|GNL;
9116732Sralph getsub = 0;
9234750Sedward if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
9335375Sedward (value("ask") != NOSTR || value("asksub") != NOSTR))
9416732Sralph t &= ~GNL, getsub++;
9534800Sedward if (printheaders) {
961227Skas puthead(hp, stdout, t);
971227Skas fflush(stdout);
981227Skas }
991227Skas if ((cp = value("escape")) != NOSTR)
1001227Skas escape = *cp;
10134750Sedward else
10234750Sedward escape = ESCAPE;
10336554Sedward eofcount = 0;
10431142Sedward hadintr = 0;
10513039Ssam
10636554Sedward if (!setjmp(colljmp)) {
10731142Sedward if (getsub)
10816732Sralph grabh(hp, GSUBJECT);
10931142Sedward } else {
11031142Sedward /*
11131142Sedward * Come here for printing the after-signal message.
11231142Sedward * Duplicate messages won't be printed because
11331142Sedward * the write is aborted if we get a SIGTTOU.
11431142Sedward */
11531142Sedward cont:
11631142Sedward if (hadintr) {
11731142Sedward fflush(stdout);
11831142Sedward fprintf(stderr,
11931142Sedward "\n(Interrupt -- one more to kill letter)\n");
12031142Sedward } else {
12131142Sedward printf("(continue)\n");
12231142Sedward fflush(stdout);
12316732Sralph }
12431142Sedward }
12531142Sedward for (;;) {
12636554Sedward colljmp_p = 1;
12736554Sedward c = readline(stdin, linebuf, LINESIZE);
12836554Sedward colljmp_p = 0;
12936554Sedward if (c < 0) {
13034750Sedward if (value("interactive") != NOSTR &&
13136554Sedward value("ignoreeof") != NOSTR && ++eofcount < 25) {
13231142Sedward printf("Use \".\" to terminate letter\n");
1334388Skurt continue;
1344388Skurt }
1351227Skas break;
1364388Skurt }
13736554Sedward eofcount = 0;
1381227Skas hadintr = 0;
13934750Sedward if (linebuf[0] == '.' && linebuf[1] == '\0' &&
14034750Sedward value("interactive") != NOSTR &&
1414413Skurt (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
1421227Skas break;
14334750Sedward if (linebuf[0] != escape || value("interactive") == NOSTR) {
14434963Sedward if (putline(collf, linebuf) < 0)
1451227Skas goto err;
1461227Skas continue;
1471227Skas }
1481227Skas c = linebuf[1];
1491227Skas switch (c) {
1501227Skas default:
1511227Skas /*
1521227Skas * On double escape, just send the single one.
1531227Skas * Otherwise, it's an error.
1541227Skas */
1551227Skas if (c == escape) {
15634963Sedward if (putline(collf, &linebuf[1]) < 0)
1571227Skas goto err;
1581227Skas else
1591227Skas break;
1601227Skas }
1611227Skas printf("Unknown tilde escape.\n");
1621227Skas break;
1631227Skas case 'C':
1641227Skas /*
1651227Skas * Dump core.
1661227Skas */
1671227Skas core();
1681227Skas break;
1691227Skas case '!':
1701227Skas /*
1711227Skas * Shell escape, send the balance of the
1721227Skas * line to sh -c.
1731227Skas */
1741227Skas shell(&linebuf[2]);
1751227Skas break;
1761227Skas case ':':
1771227Skas /*
1781227Skas * Escape to command mode, but be nice!
1791227Skas */
1802117Skas execute(&linebuf[2], 1);
18131142Sedward goto cont;
1821227Skas case '.':
1831227Skas /*
1841227Skas * Simulate end of file on input.
1851227Skas */
18631142Sedward goto out;
1871227Skas case 'q':
1881227Skas /*
1891227Skas * Force a quit of sending mail.
1901227Skas * Act like an interrupt happened.
1911227Skas */
1921227Skas hadintr++;
19336554Sedward collint(SIGINT);
1941227Skas exit(1);
1951227Skas case 'h':
1961227Skas /*
1971227Skas * Grab a bunch of headers.
1981227Skas */
1991227Skas grabh(hp, GTO|GSUBJECT|GCC|GBCC);
20031142Sedward goto cont;
2011227Skas case 't':
2021227Skas /*
2031227Skas * Add to the To list.
2041227Skas */
20534800Sedward hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
2061227Skas break;
2071227Skas case 's':
2081227Skas /*
2091227Skas * Set the Subject list.
2101227Skas */
2111227Skas cp = &linebuf[2];
21231142Sedward while (isspace(*cp))
2131227Skas cp++;
2141227Skas hp->h_subject = savestr(cp);
2151227Skas break;
2161227Skas case 'c':
2171227Skas /*
2181227Skas * Add to the CC list.
2191227Skas */
22034800Sedward hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
2211227Skas break;
2221227Skas case 'b':
2231227Skas /*
2241227Skas * Add stuff to blind carbon copies list.
2251227Skas */
22634800Sedward hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
2271227Skas break;
2281227Skas case 'd':
22935351Sedward strcpy(linebuf + 2, getdeadletter());
2301227Skas /* fall into . . . */
2311227Skas case 'r':
2321227Skas /*
2331227Skas * Invoke a file:
2341227Skas * Search for the file name,
23534963Sedward * then open it and copy the contents to collf.
2361227Skas */
2371227Skas cp = &linebuf[2];
23831142Sedward while (isspace(*cp))
2391227Skas cp++;
2401227Skas if (*cp == '\0') {
2411227Skas printf("Interpolate what file?\n");
2421227Skas break;
2431227Skas }
2441227Skas cp = expand(cp);
2451227Skas if (cp == NOSTR)
2461227Skas break;
2471227Skas if (isdir(cp)) {
24831142Sedward printf("%s: Directory\n", cp);
2491227Skas break;
2501227Skas }
25143865Sedward if ((fbuf = Fopen(cp, "r")) == NULL) {
2521227Skas perror(cp);
2531227Skas break;
2541227Skas }
2551227Skas printf("\"%s\" ", cp);
25616858Sralph fflush(stdout);
2571227Skas lc = 0;
2581227Skas cc = 0;
25936554Sedward while (readline(fbuf, linebuf, LINESIZE) >= 0) {
2601227Skas lc++;
26134963Sedward if ((t = putline(collf, linebuf)) < 0) {
26243865Sedward Fclose(fbuf);
2631227Skas goto err;
2641227Skas }
2651227Skas cc += t;
2661227Skas }
26743865Sedward Fclose(fbuf);
2681227Skas printf("%d/%d\n", lc, cc);
2691227Skas break;
2701227Skas case 'w':
2711227Skas /*
2721227Skas * Write the message on a file.
2731227Skas */
2741227Skas cp = &linebuf[2];
27534987Sedward while (*cp == ' ' || *cp == '\t')
2761227Skas cp++;
2771227Skas if (*cp == '\0') {
2781227Skas fprintf(stderr, "Write what file!?\n");
2791227Skas break;
2801227Skas }
2811227Skas if ((cp = expand(cp)) == NOSTR)
2821227Skas break;
28334963Sedward rewind(collf);
28434963Sedward exwrite(cp, collf, 1);
2851227Skas break;
2861227Skas case 'm':
28734969Sedward case 'M':
2881227Skas case 'f':
28934969Sedward case 'F':
2901227Skas /*
2911227Skas * Interpolate the named messages, if we
2921227Skas * are in receiving mail mode. Does the
2931227Skas * standard list processing garbage.
2941227Skas * If ~f is given, we don't shift over.
2951227Skas */
29634987Sedward if (forward(linebuf + 2, collf, c) < 0)
2971227Skas goto err;
29831142Sedward goto cont;
2991227Skas case '?':
30043865Sedward if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
30137870Sbostic perror(_PATH_TILDE);
3021227Skas break;
3031227Skas }
30433412Sedward while ((t = getc(fbuf)) != EOF)
30536554Sedward (void) putchar(t);
30643865Sedward Fclose(fbuf);
3071227Skas break;
3081227Skas case 'p':
3091227Skas /*
3101227Skas * Print out the current state of the
3111227Skas * message without altering anything.
3121227Skas */
31334963Sedward rewind(collf);
3141227Skas printf("-------\nMessage contains:\n");
3151227Skas puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
31634963Sedward while ((t = getc(collf)) != EOF)
31736554Sedward (void) putchar(t);
31831142Sedward goto cont;
3191227Skas case '|':
3201227Skas /*
3211227Skas * Pipe message through command.
3221227Skas * Collect output as new message.
3231227Skas */
32434963Sedward rewind(collf);
32534963Sedward mespipe(collf, &linebuf[2]);
32631142Sedward goto cont;
3271227Skas case 'v':
3281227Skas case 'e':
3291227Skas /*
3301227Skas * Edit the current message.
3311227Skas * 'e' means to use EDITOR
3321227Skas * 'v' means to use VISUAL
3331227Skas */
33434963Sedward rewind(collf);
33534963Sedward mesedit(collf, c);
33631142Sedward goto cont;
3371227Skas }
3381227Skas }
33931142Sedward goto out;
3401227Skas err:
34134963Sedward if (collf != NULL) {
34243865Sedward Fclose(collf);
34334963Sedward collf = NULL;
34431142Sedward }
34531142Sedward out:
34634963Sedward if (collf != NULL)
34734963Sedward rewind(collf);
34836554Sedward noreset--;
34936554Sedward sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
35031142Sedward signal(SIGINT, saveint);
35131142Sedward signal(SIGHUP, savehup);
35236554Sedward signal(SIGTSTP, savetstp);
35336554Sedward signal(SIGTTOU, savettou);
35436554Sedward signal(SIGTTIN, savettin);
35531142Sedward sigsetmask(omask);
35634963Sedward return collf;
3571227Skas }
3581227Skas
3591227Skas /*
3601227Skas * Write a file, ex-like if f set.
3611227Skas */
36254505Sbostic int
exwrite(name,fp,f)36331142Sedward exwrite(name, fp, f)
3641227Skas char name[];
36531142Sedward FILE *fp;
36654505Sbostic int f;
3671227Skas {
3681227Skas register FILE *of;
3691227Skas register int c;
3701227Skas long cc;
3711227Skas int lc;
3721227Skas struct stat junk;
3731227Skas
3741227Skas if (f) {
3751227Skas printf("\"%s\" ", name);
3761227Skas fflush(stdout);
3771227Skas }
3784586Skurt if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
3791227Skas if (!f)
3801227Skas fprintf(stderr, "%s: ", name);
38131142Sedward fprintf(stderr, "File exists\n");
3821227Skas return(-1);
3831227Skas }
38443865Sedward if ((of = Fopen(name, "w")) == NULL) {
3851227Skas perror(NOSTR);
3861227Skas return(-1);
3871227Skas }
3881227Skas lc = 0;
3891227Skas cc = 0;
39031142Sedward while ((c = getc(fp)) != EOF) {
3911227Skas cc++;
3921227Skas if (c == '\n')
3931227Skas lc++;
39436554Sedward (void) putc(c, of);
3951227Skas if (ferror(of)) {
3961227Skas perror(name);
39743865Sedward Fclose(of);
3981227Skas return(-1);
3991227Skas }
4001227Skas }
40143865Sedward Fclose(of);
4021227Skas printf("%d/%ld\n", lc, cc);
4031227Skas fflush(stdout);
4041227Skas return(0);
4051227Skas }
4061227Skas
4071227Skas /*
40831142Sedward * Edit the message being collected on fp.
4091227Skas * On return, make the edit file the new temp file.
4101227Skas */
41154505Sbostic void
mesedit(fp,c)41231142Sedward mesedit(fp, c)
41331142Sedward FILE *fp;
41454505Sbostic int c;
4151227Skas {
41639162Sbostic sig_t sigint = signal(SIGINT, SIG_IGN);
41734963Sedward FILE *nf = run_editor(fp, (off_t)-1, c, 0);
4181227Skas
41934963Sedward if (nf != NULL) {
42054505Sbostic fseek(nf, 0L, 2);
42134963Sedward collf = nf;
42243865Sedward Fclose(fp);
4231227Skas }
42434963Sedward (void) signal(SIGINT, sigint);
4251227Skas }
4261227Skas
4271227Skas /*
4281227Skas * Pipe the message through the command.
4291227Skas * Old message is on stdin of command;
4301227Skas * New message collected from stdout.
4311227Skas * Sh -c must return 0 to accept the new message.
4321227Skas */
43354505Sbostic void
mespipe(fp,cmd)43431142Sedward mespipe(fp, cmd)
43531142Sedward FILE *fp;
4361227Skas char cmd[];
4371227Skas {
43834963Sedward FILE *nf;
43939162Sbostic sig_t sigint = signal(SIGINT, SIG_IGN);
44033218Sbostic extern char tempEdit[];
441*66999Sedward char *shell;
4421227Skas
44343865Sedward if ((nf = Fopen(tempEdit, "w+")) == NULL) {
4441227Skas perror(tempEdit);
44534963Sedward goto out;
4461227Skas }
44734963Sedward (void) unlink(tempEdit);
44834963Sedward /*
44934963Sedward * stdin = current message.
45034963Sedward * stdout = new message.
45134963Sedward */
452*66999Sedward if ((shell = value("SHELL")) == NOSTR)
453*66999Sedward shell = _PATH_CSHELL;
454*66999Sedward if (run_command(shell,
455*66999Sedward 0, fileno(fp), fileno(nf), "-c", cmd, NOSTR) < 0) {
45643865Sedward (void) Fclose(nf);
45734963Sedward goto out;
4581227Skas }
45931142Sedward if (fsize(nf) == 0) {
4601227Skas fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
46143865Sedward (void) Fclose(nf);
46234963Sedward goto out;
4631227Skas }
4641227Skas /*
4651227Skas * Take new files.
4661227Skas */
46734963Sedward (void) fseek(nf, 0L, 2);
46831142Sedward collf = nf;
46943865Sedward (void) Fclose(fp);
47034963Sedward out:
47134963Sedward (void) signal(SIGINT, sigint);
4721227Skas }
4731227Skas
4741227Skas /*
4751227Skas * Interpolate the named messages into the current
4761227Skas * message, preceding each line with a tab.
4771227Skas * Return a count of the number of characters now in
4781227Skas * the message, or -1 if an error is encountered writing
4791227Skas * the message temporary. The flag argument is 'm' if we
4801227Skas * should shift over and 'f' if not.
4811227Skas */
48254505Sbostic int
forward(ms,fp,f)48331142Sedward forward(ms, fp, f)
4841227Skas char ms[];
48531142Sedward FILE *fp;
48654505Sbostic int f;
4871227Skas {
48834987Sedward register int *msgvec;
4891227Skas extern char tempMail[];
49034969Sedward struct ignoretab *ig;
49134969Sedward char *tabst;
4921227Skas
4931227Skas msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
4941227Skas if (msgvec == (int *) NOSTR)
4951227Skas return(0);
4961227Skas if (getmsglist(ms, msgvec, 0) < 0)
4971227Skas return(0);
49834987Sedward if (*msgvec == 0) {
4991227Skas *msgvec = first(0, MMNORM);
5001227Skas if (*msgvec == NULL) {
5011227Skas printf("No appropriate messages\n");
5021227Skas return(0);
5031227Skas }
5041227Skas msgvec[1] = NULL;
5051227Skas }
50634969Sedward if (f == 'f' || f == 'F')
50734969Sedward tabst = NOSTR;
50839160Sedward else if ((tabst = value("indentprefix")) == NOSTR)
50934969Sedward tabst = "\t";
51034969Sedward ig = isupper(f) ? NULL : ignore;
5111227Skas printf("Interpolating:");
51234987Sedward for (; *msgvec != 0; msgvec++) {
51334987Sedward struct message *mp = message + *msgvec - 1;
51434987Sedward
51534987Sedward touch(mp);
51634987Sedward printf(" %d", *msgvec);
51734987Sedward if (send(mp, fp, ig, tabst) < 0) {
51834969Sedward perror(tempMail);
51934969Sedward return(-1);
52034969Sedward }
5211227Skas }
5221227Skas printf("\n");
5231227Skas return(0);
5241227Skas }
5251227Skas
5261227Skas /*
5273420Skurt * Print (continue) when continued after ^Z.
5283420Skurt */
52931142Sedward /*ARGSUSED*/
53047706Sbostic void
collstop(s)53136554Sedward collstop(s)
53254505Sbostic int s;
5333420Skurt {
53439162Sbostic sig_t old_action = signal(s, SIG_DFL);
5353420Skurt
53636554Sedward sigsetmask(sigblock(0) & ~sigmask(s));
53736554Sedward kill(0, s);
53836554Sedward sigblock(sigmask(s));
53936554Sedward signal(s, old_action);
54036554Sedward if (colljmp_p) {
54136554Sedward colljmp_p = 0;
54236554Sedward hadintr = 0;
54336554Sedward longjmp(colljmp, 1);
54436554Sedward }
5453420Skurt }
5463420Skurt
5473420Skurt /*
54836554Sedward * On interrupt, come here to save the partial message in ~/dead.letter.
54936554Sedward * Then jump out of the collection loop.
5501227Skas */
55136554Sedward /*ARGSUSED*/
55247706Sbostic void
collint(s)55336554Sedward collint(s)
55454505Sbostic int s;
5551227Skas {
55636554Sedward /*
55736554Sedward * the control flow is subtle, because we can be called from ~q.
55836554Sedward */
55936554Sedward if (!hadintr) {
56036554Sedward if (value("ignore") != NOSTR) {
56136554Sedward puts("@");
56236554Sedward fflush(stdout);
56336554Sedward clearerr(stdin);
56436554Sedward return;
56536554Sedward }
56631142Sedward hadintr = 1;
56736554Sedward longjmp(colljmp, 1);
5681227Skas }
56931142Sedward rewind(collf);
57036554Sedward if (value("nosave") == NOSTR)
57135351Sedward savedeadletter(collf);
57236554Sedward longjmp(collabort, 1);
5731227Skas }
5741227Skas
57536554Sedward /*ARGSUSED*/
57647706Sbostic void
collhup(s)57736554Sedward collhup(s)
57854505Sbostic int s;
57936554Sedward {
58036554Sedward rewind(collf);
58136554Sedward savedeadletter(collf);
58236554Sedward /*
58336554Sedward * Let's pretend nobody else wants to clean up,
58436554Sedward * a true statement at this time.
58536554Sedward */
58636554Sedward exit(1);
58736554Sedward }
58836554Sedward
58954505Sbostic void
savedeadletter(fp)59035351Sedward savedeadletter(fp)
59135351Sedward register FILE *fp;
59235351Sedward {
59335351Sedward register FILE *dbuf;
59435351Sedward register int c;
59535351Sedward char *cp;
59635351Sedward
59735351Sedward if (fsize(fp) == 0)
59835351Sedward return;
59935351Sedward cp = getdeadletter();
60035351Sedward c = umask(077);
60143865Sedward dbuf = Fopen(cp, "a");
60236554Sedward (void) umask(c);
60335351Sedward if (dbuf == NULL)
60435351Sedward return;
60535351Sedward while ((c = getc(fp)) != EOF)
60636554Sedward (void) putc(c, dbuf);
60743865Sedward Fclose(dbuf);
60835351Sedward rewind(fp);
60935351Sedward }
610