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