home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / mail / collect.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-18  |  13.3 KB  |  621 lines

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