home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume18 / mush6.4 / part16 / mail.c
C/C++ Source or Header  |  1989-03-12  |  47KB  |  1,568 lines

  1. /* @(#)mail.c     (c) copyright 1986 (Dan Heller) */
  2.  
  3. #include "mush.h"
  4.  
  5. /*
  6.  * mail.c --
  7.  *    do_mail()     invoked from within mail.  see function for description.
  8.  *    abort_mail()    suntools specific: resets panel items and so forth.
  9.  *    start_file()      creates the editing file and reset signal catching.
  10.  *    mail_someone()    called from do_mail() or from the shell.
  11.  *    add_to_letter()    adds the next line to letter --determine ~ escapes.
  12.  *    finish_up_letter()  prompts for Cc:, verifies user really wants to send
  13.  *    send_it()        invokes mailer, sends to record file, expands aliases,
  14.  *            adds own_hdrs.
  15.  *    rm_edfile()    signals are directed here. remove letter, longjmp
  16.  *
  17.  * The flow of control in this file is NOT obvious to allow for both text
  18.  * and suntools _event driven_ attributes.  In text, the flow is far more
  19.  * obvious because it is sequential. In suntools, each function is called
  20.  * from outside this module.  Keystrokes are interpreted individually and
  21.  * queued up in "rite.c".  select.c calls add_to_letter when a \n is entered
  22.  * passing the string stored in rite.c.  If you're trying to follow the flow
  23.  * of control for suntools, keep the event drivers in mind and follow select.c
  24.  * and rite.c
  25.  */
  26. #define TO_FIELD    1
  27. #define SUBJECT        2
  28. #define CC_FIELD    3
  29. #define BC_FIELD    4
  30.  
  31. static char Subject[BUFSIZ],To[HDRSIZ],Cc[HDRSIZ],Bcc[HDRSIZ],in_reply_to[256];
  32. static int killme;
  33. static u_long flags;
  34. static SIGRET (*oldterm)(), (*oldint)(), (*oldquit)();
  35. static void send_it();
  36. static jmp_buf cntrl_c_buf;
  37. FILE *ed_fp;
  38. char *edfile;
  39.  
  40. /* argc, and argv could be null if coming from compose */
  41. do_mail(n, argv, list)
  42. register int n;   /* no need for "argc", so use the space for a variable */
  43. register char **argv, *list;
  44. {
  45.     char firstchar = (argv)? **argv: 'm';
  46.     char *to = NULL, *cc = NULL, *addcc = NULL, *bcc = NULL, *subj = NULL;
  47.     char *route = NULL;
  48.     char inc_list[MAXMSGS_BITS], buf[HDRSIZ];
  49.     u_long flgs = 0;
  50.  
  51.     if (ison(glob_flags, IS_GETTING)) {
  52.     print("You must finish the letter you are editing first.\n");
  53.     return -1;
  54.     }
  55.     if (ison(glob_flags, DO_PIPE)) {
  56.     print("You can't pipe through the mail command.\n");
  57.     return -1;
  58.     }
  59.     turnon(flgs, NO_IGNORE); /* if we include a message, include all hdrs */
  60.     clear_msg_list(inc_list);
  61.  
  62.     if (do_set(set_options, "autoedit"))
  63.     turnon(flgs, EDIT);
  64. #ifdef VERBOSE_ARG
  65.     if (do_set(set_options, "verbose"))
  66.     turnon(flgs, VERBOSE);
  67. #endif /* VERBOSE_ARG */
  68.     if (do_set(set_options, "autosign"))
  69.     turnon(flgs, SIGN);
  70.     /* If piped to mail, include the messages piped */
  71.     if (ison(glob_flags, IS_PIPE) ||
  72.     (lower(firstchar) == 'r' && do_set(set_options, "autoinclude"))) {
  73.     turnon(flgs, INCLUDE);
  74.     bitput(list, inc_list, msg_cnt, =);
  75.     }
  76.     while (argv && *argv && *++argv && **argv == '-') {
  77.     n = 1;
  78.     while (n && argv[0][n])
  79.         switch (argv[0][n]) {
  80. #ifdef VERBOSE_ARG
  81.         case 'v': turnon(flgs, VERBOSE); n++; break;
  82. #endif /* VERBOSE_ARG */
  83.         case 'e': turnon(flgs, EDIT); n++;
  84.         when 'F': turnon(flgs, DO_FORTUNE); n++;
  85.         when 'b':
  86.             if (argv[1]) {
  87.             n = 0, bcc = *++argv;
  88.             fix_up_addr(bcc);
  89.             } else {
  90.             print("Must specify blind-carbon list\n");
  91.             return -1;
  92.             }
  93.         when 'c':
  94.             if (argv[1]) {
  95.             n = 0, addcc = *++argv;
  96.             fix_up_addr(addcc);
  97.             } else {
  98.             print("Must specify carbon-copy list\n");
  99.             return -1;
  100.             }
  101.         when 's':
  102.             if (argv[1])
  103.             n = 0, subj = *++argv;
  104.             else
  105.             n++, turnon(flgs, NEW_SUBJECT);
  106.         when 'i': case 'h': case 'f': {
  107.             int m;
  108.             if (!msg_cnt) {
  109.             print("No message to include!\n");
  110.             return -1;
  111.             }
  112.             if (argv[0][n] == 'i') {
  113.             turnon(flgs, INCLUDE);
  114.             turnoff(flgs, INCLUDE_H);
  115.             turnoff(flgs, FORWARD);
  116.             } else if (argv[0][n] == 'h') {
  117.             turnon(flgs, INCLUDE_H);
  118.             turnoff(flgs, INCLUDE);
  119.             turnoff(flgs, FORWARD);
  120.             } else if (argv[0][n] == 'f') {
  121.             turnon(flgs, FORWARD);
  122.             turnoff(flgs, INCLUDE_H);
  123.             turnoff(flgs, INCLUDE);
  124.             }
  125.             /* "-i 3-5" or "-i3-5"  Consider the latter case first */
  126.             if (!argv[0][++n])
  127.             argv++, n = 0;
  128.             (*argv) += n;
  129.             m = get_msg_list(argv, inc_list);
  130.             (*argv) -= n;
  131.             if (m == -1)
  132.             return -1;
  133.             /* if there were args, then go back to the first char
  134.              * in the next argv
  135.              */
  136.             if (m)
  137.             n = 0;
  138.             if (!n) /* n may be 0 from above! */
  139.             argv += (m-1);
  140.         }
  141.         when 'r':
  142.             if (lower(firstchar) == 'r') {
  143.             route = *++argv;
  144.             n = 0;
  145.             break;
  146.             }
  147.             /* fall thru */
  148.         default:
  149.             if (argv[0][n] != '?')
  150.             wprint("%c: unknown option\n", argv[0][n]);
  151.             wprint("available options\n");
  152. #ifdef VERBOSE_ARG
  153.             wprint("v      verbose (passed to mail delivery system)\n");
  154. #endif /* VERBOSE_ARG */
  155.             wprint("e      immediately enter editor (autoedit)\n");
  156.             wprint("F      add fortune to the end of message.\n");
  157.             wprint("s [subject]  prompt for or set subject.\n");
  158.             wprint("c cc-addrs   set carbon-copy recipients.\n");
  159.             wprint("b bcc-addrs  set blind-carbon-copy recipients.\n");
  160.             wprint("i [msg#'s]   include msg_list into letter.\n");
  161.             wprint("h [msg#'s]   include msg_list with headers.\n");
  162.             wprint("f [msg#'s]   forward msg_list (not indented).\n");
  163.             return -1;
  164.         }
  165.     }
  166.     *in_reply_to = *To = *Subject = *Cc = *Bcc = 0;
  167.     if (lower(firstchar) == 'r') {
  168.     char *old_fmt = hdr_format, *pcc = NULL;
  169.     to = To, cc = Cc;
  170.     /*
  171.      * Generate a reply to all the messages passed to respond().  This
  172.      * list is different than the include-msg list above.  Get info about
  173.      * whom the messages were sent to for reply-all.
  174.      * BUG: currently, redundant addresses aren't pruned from Bcc list!
  175.      */
  176.     for (n = 0; n < msg_cnt; n++)
  177.         if (msg_bit(list, n)) {
  178.         if (to != To)
  179.             *to++ = ',', *to++ = ' ';
  180.         (void) reply_to(n, (firstchar == 'R'), buf);
  181.         if (strlen(buf) + (to - To) > sizeof(To) - 1) {
  182.             print("# recipients exceeded at msg %d\n", n);
  183.             break;
  184.         }
  185.         to += Strcpy(to, buf);
  186.         if (firstchar == 'R') {
  187.             if (pcc) /* if there was a previous cc, append ", " */
  188.             *cc++ = ',', *cc++ = ' ';
  189.             if (pcc = cc_to(n, buf))
  190.             if (strlen(pcc) + (cc - Cc) > sizeof(Cc) - 1)
  191.                 print("# Cc's exceeded at msg %d\n", n);
  192.             else
  193.                 cc += Strcpy(cc, pcc);
  194.         }
  195.         /* remove redundant addresses now, or headers could get too
  196.          * long before the list runs out (it still might)
  197.          */
  198.         rm_redundant_addrs(To, Cc);
  199.         to = To + strlen(To);
  200.         cc = Cc + strlen(Cc);
  201.         }
  202.     /* clean up end of Cc line for replyall's */
  203.     while (*cc == ' ' || *cc == ',')
  204.         *cc-- = '\0';
  205.     to = To, cc = Cc;
  206.     if (route || (route = do_set(set_options, "auto_route")))
  207.         /* careful! This routine could add lots-o-bytes and lose addresses
  208.          * to avoid writing out of segment.
  209.          */
  210.         route_addresses(To, Cc, route);
  211.     if (hdr_format = do_set(set_options, "in_reply_to"))
  212.         /* "9" here is a magic # --see compose_hdr() */
  213.         (void) strcpy(in_reply_to, compose_hdr(current_msg)+9);
  214.     hdr_format = old_fmt;
  215.     }
  216.     if (ison(flgs, FORWARD) && ison(flgs, EDIT) ||
  217.         lower(firstchar) == 'r' && isoff(flgs, NEW_SUBJECT)) {
  218.     turnoff(flgs, NEW_SUBJECT);
  219.     if (subj = subject_to(current_msg, buf))
  220.         subj = strcpy(Subject, buf + 4*(lower(firstchar) != 'r'));
  221.     } else if (isoff(flgs, NEW_SUBJECT) && isoff(flgs, FORWARD) &&
  222.     (do_set(set_options, "ask") || do_set(set_options, "asksub")))
  223.     turnon(flgs, NEW_SUBJECT);
  224.     if (argv && *argv) {
  225.     char buf[HDRSIZ];
  226.     (void) argv_to_string(buf, argv);
  227.     fix_up_addr(buf);
  228.     to = &To[strlen(To)];
  229.     if (*To)
  230.         *to++ = ',', *to++ = ' ';
  231.     (void) strcpy(to, buf);
  232.     to = To;
  233.     }
  234.     if (addcc && *addcc) {
  235.     cc = &Cc[strlen(Cc)];
  236.     if (*Cc)
  237.         *cc++ = ',', *cc++ = ' ';
  238.     (void) strcpy(cc, addcc); /* addcc has already been fixed up */
  239.     cc = Cc;
  240.     }
  241.     /* remove any redundant addresses that just got added */
  242.     rm_redundant_addrs(To, Cc);
  243.     if (bcc && *bcc)
  244.     (void) strncpy(Bcc, bcc, sizeof(Bcc)); /* bcc already fixed up */
  245.     bcc = Bcc;
  246.     if (do_set(set_options, "fortune"))
  247.     turnon(flgs, DO_FORTUNE);
  248. #ifdef SUNTOOL
  249.     if (istool) {
  250.     do_clear();
  251.     panel_set(abort_item,   PANEL_SHOW_ITEM, TRUE,  0);
  252.     panel_set(comp_item,    PANEL_SHOW_ITEM, FALSE, 0);
  253.     panel_set(read_item,    PANEL_SHOW_ITEM, FALSE, 0);
  254.     panel_set(respond_item, PANEL_SHOW_ITEM, FALSE, 0);
  255.     }
  256. #endif /* SUNTOOL */
  257.     return mail_someone(to, subj, cc, bcc, flgs, inc_list);
  258. }
  259.  
  260. #ifdef SUNTOOL
  261. /* panel item selection -- it's here because of local (static) variables */
  262. abort_mail(item, value)
  263. Panel_item item;
  264. {
  265.     get_hdr_field = 0;
  266.     if (item == abort_item && value != 2) {
  267.     print("Aborted letter.");
  268.     killme = 1, rm_edfile(SIGINT);
  269.     flags = 0;
  270.     }
  271.     panel_set(comp_item,    PANEL_SHOW_ITEM, TRUE,  0);
  272.     panel_set(send_item,    PANEL_SHOW_ITEM, FALSE, 0);
  273.     panel_set(edit_item,    PANEL_SHOW_ITEM, FALSE, 0);
  274.     panel_set(abort_item,   PANEL_SHOW_ITEM, FALSE, 0);
  275.     panel_set(read_item,    PANEL_SHOW_ITEM, TRUE,  0);
  276.     panel_set(respond_item, PANEL_SHOW_ITEM, TRUE,  0);
  277.     turnoff(glob_flags, IS_GETTING);
  278.     unlock_cursors();
  279. }
  280. #endif /* SUNTOOL */
  281.  
  282. mail_someone(to, subject, cc, bcc, flgs, list)
  283. register char *to, *subject, *cc, *bcc, *list;
  284. u_long flgs;
  285. {
  286.     register char *p;
  287.  
  288.     flags = flgs;
  289. #ifdef SUNTOOL
  290.     if (istool)
  291.     rite(_tty.sg_kill), do_clear();
  292. #endif /* SUNTOOL */
  293.     if (to && *to) {
  294.     if (!*To)
  295.         (void) strncpy(To, to, sizeof(To));
  296.     if (istool)
  297.         wprint("To: %s\n", To);
  298.     } else
  299. #ifdef SUNTOOL
  300.     if (istool)
  301.         turnon(get_hdr_field, TO_FIELD);
  302.     else
  303. #endif /* SUNTOOL */
  304.         to = NO_STRING;
  305.     if (subject && *subject) {
  306.     if (!*Subject)
  307.         (void) strncpy(Subject, subject, sizeof(Subject));
  308.     if (istool)
  309.         wprint("Subject: %s\n", Subject);
  310.     } else
  311. #ifdef SUNTOOL
  312.     if (istool && !*Subject && ison(flags, NEW_SUBJECT))
  313.         turnon(get_hdr_field, SUBJECT);
  314.     else
  315. #endif /* SUNTOOL */
  316.         subject = NO_STRING;
  317.     if (cc && *cc) {
  318.     if (!*Cc)
  319.         (void) strncpy(Cc, cc, sizeof(Cc));
  320.     if (istool)
  321.         wprint("Cc: %s\n", Cc);
  322.     } else
  323. #ifdef SUNTOOL  /* get_hdr_field -- prevents prompting on reply and forward */
  324.     if (istool && get_hdr_field && do_set(set_options, "askcc"))
  325.         turnon(get_hdr_field, CC_FIELD);
  326.     else
  327. #endif /* SUNTOOL */
  328.         Cc[0] = '\0';
  329.     if (bcc && *bcc) {
  330.     if (!*Bcc)
  331.         (void) strncpy(Bcc, bcc, sizeof(Bcc));
  332.     if (istool)
  333.         wprint("Bcc: %s\n", Bcc);
  334.     } else
  335. #ifdef SUNTOOL  /* get_hdr_field -- prevents prompting on reply and forward */
  336.     if (istool && get_hdr_field)
  337.         turnon(get_hdr_field, BC_FIELD);
  338.     else
  339. #endif /* SUNTOOL */
  340.         Bcc[0] = '\0';
  341.  
  342.     if (ison(glob_flags, REDIRECT)) {
  343.     send_it(); /* doesn't return */
  344.     return 0;
  345.     }
  346.     /* if (!*to) then prompting will be done */
  347.     if (!istool) {
  348.     if (!(p = set_header("To: ", to, !*to)) || !*p) {
  349.         puts("No recipients, can't mail.");
  350.         return -1;
  351.     }
  352.     if (!*to) /* if user typed To-line here, fix up the address line */
  353.         fix_up_addr(p);
  354.     (void) strcpy(To, p);
  355.     /* don't prompt for subject if forwarding mail */
  356.     if (isoff(flags, FORWARD) && 
  357.         (p = set_header("Subject: ", subject,
  358.             !*subject && ison(flags, NEW_SUBJECT))))
  359.         (void) strcpy(Subject, p);
  360.     if (*Cc)
  361.         printf("Cc: %s\n", Cc);
  362.     if (*Bcc)
  363.         printf("Bcc: %s\n", Bcc);
  364.     putchar('\n');
  365.     }
  366. #ifdef SUNTOOL
  367.     else if (!get_hdr_field) {
  368.     panel_set(send_item, PANEL_SHOW_ITEM, TRUE, 0);
  369.     panel_set(edit_item, PANEL_SHOW_ITEM, TRUE, 0);
  370.     } else if (ison(flags, FORWARD) && ison(flags, EDIT) &&
  371.         ison(get_hdr_field, TO_FIELD)) {
  372.     print("Specify recipient(s) to forward in tool mode.\n");
  373.     return -1;
  374.     }
  375. #endif /* SUNTOOL */
  376.     /* If forwarding w/o editing, start a new file for each */
  377.     if (ison(flags, FORWARD) && isoff(flags, EDIT)) {
  378.     char fwd[MAXMSGS_BITS];
  379.     register int i;
  380.     clear_msg_list(fwd);
  381.     for (i = 0; i < msg_cnt; i++)
  382.         if (msg_bit(list, i)) {
  383.         set_msg_bit(fwd, i);
  384.         if (start_file(fwd) < 0)
  385.             return -1;
  386.         clear_msg_list(fwd);
  387.         }
  388.     } else
  389.     return start_file(list);
  390. }
  391.  
  392. start_file(list)
  393. char *list;
  394. {
  395.     register char  *dir;
  396.     register int   i;
  397.     char         line[MAXPATHLEN];
  398.  
  399.     if (!(dir = do_set(set_options, "tmpdir")) &&
  400.     !(dir = do_set(set_options, "home")))
  401. alted:
  402.     dir = ALTERNATE_HOME;
  403.     (void) mktemp(sprintf(line, "%s/%s", dir, EDFILE));
  404.     strdup(edfile, line);
  405.     if (!(ed_fp = mask_fopen(edfile, "w+"))) {
  406.     if (strcmp(dir, ALTERNATE_HOME))
  407.         goto alted;
  408. #ifdef SUNTOOL
  409.     if (istool)
  410.         abort_mail(NO_ITEM, 0);
  411. #endif /* SUNTOOL */
  412.     error("can't create %s", edfile);
  413.     return -1;
  414.     }
  415.     if (!istool) {
  416.     oldint = signal(SIGINT, rm_edfile);
  417.     oldquit = signal(SIGQUIT, rm_edfile);
  418.     oldterm = signal(SIGTERM, rm_edfile);
  419.     }
  420.  
  421.     /* if flags call for it, include current message (with header?) */
  422.     if (ison(flags, INCLUDE) || ison(flags,FORWARD) || ison(flags,INCLUDE_H)) {
  423.     long copy_flgs = 0, is_forw = ison(flags, FORWARD);
  424.     char buf[sizeof(To)];
  425.     if (is_forw)
  426.         turnon(copy_flgs, FORWARD);
  427.     else
  428.         turnon(copy_flgs, INDENT);
  429.     if (ison(flags, INCLUDE))
  430.         turnon(copy_flgs, NO_HEADER);
  431.     if (ison(flags, INCLUDE) || is_forw)
  432.         turnon(copy_flgs, NO_IGNORE);
  433. #ifdef MMDF
  434.     turnon(copy_flgs, NO_SEPARATOR);
  435. #endif /* MMDF */
  436. #ifdef SUNTOOL
  437.     if (istool)
  438.         lock_cursors();
  439. #endif /* SUNTOOL */
  440.     for (i = 0; i < msg_cnt; i++)
  441.         if (msg_bit(list, i)) {
  442.         if (is_forw && ison(flags, EDIT)) {
  443.             (void) reply_to(i, FALSE, buf);
  444.             fprintf(ed_fp, "--- Forwarded mail from %s\n\n", buf);
  445.         }
  446.         wprint("%sing message %d ...",
  447.             is_forw? "forward" : "includ", i+1);
  448.         wprint("(%d lines)\n", copy_msg(i, ed_fp, copy_flgs));
  449.         set_isread(i); /* if we included it, we read it, right? */
  450.         if (is_forw && ison(flags, EDIT))
  451.             fprintf(ed_fp,
  452.             "\n--- End of forwarded message from %s\n", buf);
  453.         }
  454.     fflush(ed_fp);
  455. #ifdef SUNTOOL
  456.     if (istool)
  457.         unlock_cursors();
  458. #endif /* SUNTOOL */
  459.     }
  460.     if (ison(glob_flags, WARNING)) {
  461.     if (escape && !strncmp(escape, DEF_ESCAPE, 1))
  462.         wprint("(escape character is set to `%c')\n", *escape);
  463.     if (wrapcolumn && wrapcolumn < 20)
  464.         wprint("(warning: wrapping only %d columns from the left!)\n",
  465.             wrapcolumn);
  466.     }
  467.     turnon(glob_flags, IS_GETTING);
  468.     /* enter editor if autoedit and not toolmode */
  469.     if (
  470. #ifdef SUNTOOL
  471.         (!istool || !get_hdr_field) &&
  472. #endif /* SUNTOOL */
  473.     /* do an "if" again in case editor not found and EDIT turned off */
  474.                     ison(flags, EDIT)) {
  475.     char *argv[3];
  476.     if (!(argv[0] = do_set(set_options, "visual")) || !*argv[0])
  477.         argv[0] = DEF_EDITOR;
  478.     argv[1] = edfile;
  479.     argv[2] = NULL;
  480.     print("Starting \"%s\"...\n", argv[0]);
  481.     fclose(ed_fp);
  482.     ed_fp = NULL_FILE;
  483.     execute(argv);
  484.     turnoff(flags, EDIT);
  485.     turnoff(flags, FORWARD); /* forwarded messages must be unedited */
  486.     /* upon exit of editor, user must now type ^D or "." to send */
  487.     if (istool)
  488.         return 0;
  489.     if (!(ed_fp = fopen(edfile, "r+"))) {
  490.         error("can't reopen %s", edfile);
  491.         return -1;
  492.     }
  493.     (void) fseek(ed_fp, 0L, 2);
  494.     puts("continue editing letter or ^D to send");
  495.     } else if (ison(flags, FORWARD) &&
  496. #ifdef SUNTOOL
  497.            (!istool || !get_hdr_field) &&
  498. #endif /* SUNTOOL */
  499.            finish_up_letter())
  500.     return 0; /* if forwarding mail, check to see if tool requires To: */
  501. #ifdef SUNTOOL
  502.     if (istool) {
  503.     /* If toolmode, we're ready for IO. Give first prompt if not given */
  504.     if (ison(get_hdr_field, TO_FIELD))
  505.         wprint("To: ");
  506.     else if (ison(get_hdr_field, SUBJECT))
  507.         wprint("Subject: ");
  508.     else if (ison(get_hdr_field, CC_FIELD))
  509.         wprint("Cc: ");
  510.     type_cursor(PIX_SRC);
  511.     win_setcursor(msg_sw->ts_windowfd, &write_cursor);
  512.     return 0;
  513.     }
  514. #endif /* SUNTOOL */
  515.     i = 0;
  516.     do  {
  517.     /* If the user hits ^C in cbreak mode, mush will return to
  518.      * Getstr and not clear the buffer. whatever is typed next will
  519.      * be appended to the line.  jumping here will force the line to
  520.      * be cleared cuz it's a new call.
  521.      */
  522.     (void) setjmp(cntrl_c_buf);
  523.     while (Getstr(line, sizeof(line), 0) > -1) {
  524.         if (!istool) /* toolmode checks on a timer -- don't do it here */
  525.         (void) check_new_mail(); /* if new mail comes in, get it */
  526.         if ((i = add_to_letter(line)) <= 0)
  527.         break;
  528.     }
  529.     } while (i >= 0 && !finish_up_letter());
  530.     return i; /* return -1 if ~x or ~q to terminate letter */
  531. }
  532.  
  533. char *tilde_commands[] = {
  534.     "commands: [OPTIONAL argument]",
  535.     "e [editor]\tEnter editor. Editor used: \"set editor\", env EDITOR, vi",
  536.     "v [editor]\tEnter visual editor. \"set visual\", env VISUAL, vi",
  537.     "p [pager]\tPage message; pager used: \"set pager\", env. PAGER, more",
  538.     "i [msg#'s]\tInclude current msg body [msg#'s] indented by \"indent_str\"",
  539.     "H [msg#'s]\tSame, but include the message headers from included messages",
  540.     "f [msg#'s]\tForward mail. Not indented, but marked as \"forwarded mail\"",
  541.     "t [list]\tChange list of recipients",
  542.     "s [subject]\tModify [set] subject header",
  543.     "c [cc list]\tModify [set] carbon copy recipients",
  544.     "b [bcc list]\tModify [set] blind carbon recipients",
  545.     "h\t\tModify all message headers",
  546.     "S[!]\t\tInclude Signature file [suppress file]",
  547.     "F[!]\t\tAdd a fortune at end of letter [don't add]",
  548.     "w file\t\tWrite msg buffer to file name",
  549.     "a file\t\tAppend msg buffer to file name",
  550.     "r file\t\tRead filename into message buffer",
  551.     "q \t\tQuit message; save in dead.letter (unless \"nosave\" is set).",
  552.     "x \t\tQuit message; don't save in dead.letter.",
  553.     "$variable\tInsert the string value for \"variable\" into message.",
  554.     ":cmd\t\tRun the mail command \"cmd\".",
  555.     "u\t\tedit previous line in file.",
  556.     "E[!]\t\tClear contents of letter after saving to dead.letter [unless !].",
  557.     0
  558. };
  559.  
  560. /*
  561.  * Add the line (char *) parameter to the letter.  Determine tilde
  562.  * escapes and determine what to do.  This function returns 0 to
  563.  * indicate user wants to end the letter, -1 if the letter cannot
  564.  * be sent (~q, ~x no buffer after editor, etc...) or 1 to indicate
  565.  * successful addition of the line to the letter.
  566.  */
  567. add_to_letter(line)
  568. char line[];
  569. {
  570.     register char *p;
  571.     char buf[BUFSIZ];
  572.  
  573.     killme = 0;
  574.     (void) fseek(ed_fp, 0L, 2);
  575. #ifdef SUNTOOL
  576.     if (get_hdr_field) {
  577.     /* These are received in order by design! */
  578.     if (ison(get_hdr_field, TO_FIELD)) {
  579.         if (!line[0]) {
  580.             wprint("There must be a recipient!\nTo: ");
  581.         return 1;
  582.         }
  583.         fix_up_addr(line);
  584.         (void) strcpy(To, line), turnoff(get_hdr_field, TO_FIELD);
  585.     } else if (ison(get_hdr_field, SUBJECT)) {
  586.         (void) strcpy(Subject, line);
  587.         turnoff(get_hdr_field, SUBJECT);
  588.     } else if (ison(get_hdr_field, CC_FIELD)) {
  589.         fix_up_addr(line);
  590.         (void) strcpy(Cc, line);
  591.         turnoff(get_hdr_field, CC_FIELD);
  592.     } else if (ison(get_hdr_field, BC_FIELD)) {
  593.         fix_up_addr(line);
  594.         (void) strcpy(Bcc, line);
  595.         turnoff(get_hdr_field, BC_FIELD);
  596.     }
  597.  
  598.         if (ison(get_hdr_field, SUBJECT))
  599.         (void) set_header("Subject: ", Subject, 1);
  600.         else if (ison(get_hdr_field, CC_FIELD))
  601.         (void) set_header("Cc: ", Cc, 1);
  602.         else if (ison(get_hdr_field, BC_FIELD))
  603.         (void) set_header("Bcc: ", Bcc, 1);
  604.     panel_set(send_item, PANEL_SHOW_ITEM, (get_hdr_field==0), 0);
  605.     panel_set(edit_item, PANEL_SHOW_ITEM, (get_hdr_field==0), 0);
  606.     if (!get_hdr_field) {
  607.         wprint("\n");
  608.         if (ison(flags, EDIT)) {
  609.         (void) add_to_letter(sprintf(line, "%cv", *escape));
  610.         turnoff(flags, EDIT);
  611.         }
  612.     }
  613.     return 1;
  614.     }
  615. #endif /* SUNTOOL */
  616.     if (!strcmp(line, ".") && (istool || do_set(set_options, "dot")))
  617.     return 0;
  618.     if (line[0] != *escape) {
  619.     fputs(line, ed_fp), fputc('\n', ed_fp), fflush(ed_fp);
  620.     return 1;
  621.     }
  622.     /* all commands are "~c" (where 'c' is the command). set p = first
  623.      * character after 'c' and skip whitespace
  624.      */
  625.     p = &line[2];
  626.     skipspaces(0);
  627.     switch (line[1]) {
  628.     case 'v' : case 'p': case 'e': {
  629.         if (!*p || *p == 'i' && !p[1])
  630.         switch (line[1]) {
  631.             case 'p' :
  632.             if (!*p && !(p = do_set(set_options, "pager")))
  633.                 p = DEF_PAGER;
  634.             if (!*p || !strcmp(p, "internal"))
  635.                 p = NULL;
  636.             when 'v' :
  637.             if (p = do_set(set_options, "visual"))
  638.                 break;
  639.             default :
  640.             if (!(p = do_set(set_options, "editor")) || !*p)
  641.                 p = DEF_EDITOR;
  642.         }
  643.         if (line[1] == 'p') {
  644.         rewind(ed_fp);
  645.         (void) do_pager(p, TRUE); /* start the pager "p" */
  646.         do_pager(sprintf(buf, "To: %s\n", To), FALSE);
  647.         if (Subject[0])
  648.             do_pager(sprintf(buf, "Subject: %s\n", Subject), FALSE);
  649.         if (Cc[0])
  650.             do_pager(sprintf(buf, "Cc: %s\n", Cc), FALSE);
  651.         if (Bcc[0])
  652.             do_pager(sprintf(buf, "Bcc: %s\n", Bcc), FALSE);
  653.         do_pager(strcpy(buf, "--------\nMessage contains:\n"), FALSE);
  654.         while (fgets(buf, sizeof(buf), ed_fp))
  655.             if (do_pager(buf, FALSE) == EOF)
  656.             break;
  657.         (void) do_pager(NULL, FALSE); /* end pager */
  658.         } else {
  659.         char *argv[3];
  660.         argv[0] = p;
  661.         argv[1] = edfile;
  662.         argv[2] = NULL;
  663.         fclose(ed_fp);
  664.         ed_fp = NULL_FILE;
  665.         execute(argv); /* tool will return even tho editor isn't done */
  666.         if (istool)
  667.             return 1;
  668.         if (!(ed_fp = fopen(edfile, "r+"))) {
  669.             error("can't reopen %s", edfile);
  670.             return -1;
  671.         }
  672.         }
  673.     }
  674.     when '$': {
  675.         register char *p2;
  676.         if (!(p2 = do_set(set_options, p)))
  677.         wprint("(%s isn't set)\n", p);
  678.         else
  679.         putstring(p2, ed_fp);
  680.     }
  681.     when ':': {
  682.         char new[MAXMSGS_BITS];
  683.         u_long save_flags = glob_flags;
  684.  
  685.         turnon(glob_flags, IGN_SIGS);
  686.         turnon(glob_flags, IGN_BANG);
  687.         turnoff(glob_flags, DO_PIPE);
  688.         turnoff(glob_flags, IS_PIPE);
  689.         (void) cmd_line(p, new);
  690.         glob_flags = save_flags;
  691. #ifdef SUNTOOL
  692.         if (istool && msg_pix) /* the command was to read a message */
  693.         return 1;
  694. #endif /* SUNTOOL */
  695.     }
  696.     when 'i': case 'f': case 'H': case 'm': {
  697.         int  n;
  698.         long copy_flgs = 0;
  699.         char list[MAXMSGS_BITS];
  700.  
  701.         if (!msg_cnt) {
  702.         print("No messages.\n");
  703.         break;
  704.         }
  705.         clear_msg_list(list);
  706.         if (line[1] != 'f')
  707.         turnon(copy_flgs, INDENT);
  708.         if (line[1] == 'i')
  709.         turnon(copy_flgs, NO_HEADER);
  710. #ifdef MMDF
  711.         turnon(copy_flgs, NO_SEPARATOR);
  712. #endif /* MMDF */
  713.         if (!*p)
  714.         set_msg_bit(list, current_msg);
  715.         else if (!do_range(p, list))
  716.         return 1;
  717. #ifdef SUNTOOL
  718.         if (istool)
  719.         lock_cursors();
  720. #endif /* SUNTOOL */
  721.         for (n = 0; n < msg_cnt; n++)
  722.         if (msg_bit(list, n)) {
  723.             if (line[1] == 'f') {
  724.             (void) reply_to(n, FALSE, buf);
  725.             fprintf(ed_fp, "--- Forwarded mail from %s\n\n", buf);
  726.             }
  727.             wprint("Including message %d ... ", n+1);
  728.             wprint("(%d lines)\n", copy_msg(n, ed_fp, copy_flgs));
  729.             set_isread(n);
  730.             if (line[1] == 'f')
  731.         fprintf(ed_fp,"\n--- End of forwarded message from %s\n\n",buf);
  732.         }
  733. #ifdef SUNTOOL
  734.         if (istool)
  735.         unlock_cursors();
  736. #endif /* SUNTOOL */
  737.     }
  738.     /* To: Cc: and Bcc: headers */
  739.     when 'b':
  740.     case 't':
  741.     case 'c': {
  742.         char *h = (line[1] == 't')? To : (line[1] == 'c')? Cc : Bcc;
  743.         char *Prompt = line[1] == 't'? "To: " :
  744.                line[1] == 'c'? "Cc: " : "Bcc: ";
  745. #ifdef SUNTOOL
  746.         if (!*p && istool) {
  747.         turnon(get_hdr_field, CC_FIELD);
  748.         (void) set_header(Prompt, h, 1);
  749.         panel_set(send_item, PANEL_SHOW_ITEM, FALSE, 0);
  750.         panel_set(edit_item, PANEL_SHOW_ITEM, FALSE, 0);
  751.         return 1;
  752.         }
  753. #endif /* SUNTOOL */
  754.         if (*p) {
  755.         fix_up_addr(p);
  756.         if (*h)
  757.             (void) sprintf(h+strlen(h), ", %s", p);
  758.         else
  759.             (void) strcpy(h, p);
  760.         } else if (!(p = set_header(Prompt, h, TRUE)) || !*p)
  761.         if (line[1] == 't') {
  762.             wprint("There must be a recipient!\n");
  763. #ifdef SUNTOOL
  764.             turnoff(get_hdr_field, TO_FIELD);
  765.             panel_set(send_item, PANEL_SHOW_ITEM, TRUE, 0);
  766.             panel_set(edit_item, PANEL_SHOW_ITEM, TRUE, 0);
  767. #endif /* SUNTOOL */
  768.         } else
  769.             *h = 0;
  770.         else {
  771.         fix_up_addr(p);
  772.         (void) strcpy(h, p);
  773.         }
  774.     }
  775.     when 's':
  776. #ifdef SUNTOOL
  777.         if (!*p && istool) {
  778.         turnon(get_hdr_field, SUBJECT);
  779.         panel_set(send_item, PANEL_SHOW_ITEM, FALSE, 0);
  780.         panel_set(edit_item, PANEL_SHOW_ITEM, FALSE, 0);
  781.         (void) set_header("Subject: ", Subject, 1);
  782.         return 1;
  783.         }
  784. #endif /* SUNTOOL */
  785.         if (*p || (p = set_header("Subject: ", Subject, 1)))
  786.         if (!*p)
  787.             Subject[0] = 0;
  788.         else
  789.             (void) strcpy(Subject, p);
  790.     when 'h':
  791. #ifdef SUNTOOL
  792.         if (istool) {
  793.         turnon(get_hdr_field, TO_FIELD);
  794.         turnon(get_hdr_field, SUBJECT);
  795.         turnon(get_hdr_field, CC_FIELD);
  796.         turnon(get_hdr_field, BC_FIELD);
  797.         (void) set_header("To: ", To, 1);
  798.         panel_set(send_item, PANEL_SHOW_ITEM, FALSE, 0);
  799.         panel_set(edit_item, PANEL_SHOW_ITEM, FALSE, 0);
  800.         return 1;
  801.         }
  802. #endif /* SUNTOOL */
  803.         while ((p = set_header("To: ", To, 1)) && !*p)
  804.         wprint("(There must be a recipient.)\n");
  805.         (void) strcpy(To, p);
  806.         if (p = set_header("Subject: ", Subject, 1))
  807.         if (!*p)
  808.             Subject[0] = 0;
  809.         else
  810.             (void) strcpy(Subject, p);
  811.         if (p = set_header("Cc: ", Cc, 1))
  812.         if (!*p)
  813.             Cc[0] = 0;
  814.         else {
  815.             fix_up_addr(p);
  816.             (void) strcpy(Cc, p);
  817.         }
  818.         if (p = set_header("Bcc: ", Bcc, 1))
  819.         if (!*p)
  820.             Bcc[0] = 0;
  821.         else {
  822.             fix_up_addr(p);
  823.             (void) strcpy(Bcc, p);
  824.         }
  825.     when 'S':
  826.         if (*p == '!')
  827.         turnoff(flags, SIGN), wprint("not ");
  828.         else
  829.         turnon(flags, SIGN);
  830.         wprint("adding signature file at end of message.\n");
  831.     when 'F':
  832.         if (*p == '!')
  833.         turnoff(flags, DO_FORTUNE), wprint("not ");
  834.         else
  835.         turnon(flags, DO_FORTUNE);
  836.         wprint("adding fortune at end of message.\n");
  837.     when 'w': case 'a': case 'r':
  838.         if (!*p) {
  839.         wprint("(you must specify a filename)\n");
  840.         return 1;
  841.         }
  842.         (void) fseek(ed_fp, 0L, 2); /* append */
  843.         file_to_fp(p, ed_fp, (line[1] == 'r')? "r":
  844.                   (line[1] == 'w')? "w": "a");
  845.     /* go up one line in the message file and allow the user to edit it */
  846.     when 'u': {
  847.         long newpos, pos = ftell(ed_fp);
  848.         char oldline[256];
  849.         if (istool) {
  850.         wprint("(Not available in tool mode.)\n");
  851.         return 1;
  852.         }
  853.         if (pos <= 0L) { /* pos could be -1 if ftell() failed */
  854.         wprint("(No previous line in file.)\n");
  855.         return 1;
  856.         }
  857.         /* get the last 256 bytes written and read backwards from the
  858.          * current place until '\n' is found. Start by moving past the
  859.          * first \n which is at the end of the line we want to edit
  860.          */
  861.         newpos = max(0, pos - 256L);
  862.         (void) fseek(ed_fp, newpos, L_SET);
  863.         /* don't fgets -- it'll stop at a \n */
  864.         (void) fread(line, sizeof(char), (int)(pos-newpos), ed_fp);
  865.         pos--;
  866.         /* the last char in line should be a \n cuz it was last input */
  867.         if (line[(int)(pos-newpos)] != '\n')
  868.         wprint("I don't know how, but your last line ended with %c.\n",
  869.             line[(int)(pos-newpos)]);
  870.         else
  871.         line[(int)(pos-newpos)] = 0; /* null terminate \n for ^H-ing */
  872.         for (pos--; pos > newpos && line[(int)(pos-newpos)] != '\n'; pos--)
  873.         ;
  874.         /* we've gone back to the end of the second previous line. Check
  875.          * to see if the char we're pointing to is a \n.  It should be, but
  876.          * if it's not, we moved back to the first line of the file.
  877.          */
  878.         if (line[(int)(pos-newpos)] == '\n')
  879.         ++pos;
  880.         /* save the old line that's there in case the user boo-boos */
  881.         (void) strcpy(oldline, &line[(int)(pos-newpos)]);
  882.         /* let set header print out the line and get the input */
  883.         if (!(p = set_header("", &line[(int)(pos-newpos)], TRUE))) {
  884.         wprint("Something bad happened and I don't know what it is.\n");
  885.         p = oldline;
  886.         } else if (*p == *escape)
  887.         wprint("(Warning: %c escapes ignored on %cu lines.)\n",
  888.                 *escape, *escape);
  889.         /* seek to to the position where the new line will go */
  890.         (void) fseek(ed_fp, pos, L_SET);
  891.         /* put the newly typed line */
  892.         (void) fputs(p, ed_fp); /* don't add \n. padding may be necessary */
  893.         /* if the new line is less than the old line, we're going to do
  894.          * one of two things.  The best thing to do is to truncate the
  895.          * file to the end of the new line.  Sys-v can't do that, so we
  896.          * pad the line with blanks.  May be messy in some cases, but...
  897.          */
  898.         if ((pos = strlen(p) - strlen(oldline)) < 0) {
  899. #ifndef SYSV
  900.         /* add the \n, flush the file, truncate to the current pos */
  901.         fputc('\n', ed_fp), fflush(ed_fp);
  902.         (void) ftruncate(fileno(ed_fp), (int)ftell(ed_fp));
  903. #else
  904.         /* pad with blanks to the length of the old line. add \n */
  905.         while (pos++ < 0)
  906.             fputc(' ', ed_fp);
  907.         fputc('\n', ed_fp), fflush(ed_fp);
  908. #endif /* SYSV */
  909.         } else
  910.         /* the new line is >= the old line, add \n -- no trunc req. */
  911.             fputc('\n', ed_fp);
  912.         return 1;
  913.      }
  914.     /* break;  not here cuz of "return" (lint). */
  915.     case 'E':
  916.         if (*p != '!' && !do_set(set_options, "nosave"))
  917.         dead_letter();
  918.         if (emptyfile(&ed_fp, edfile) == -1)
  919.         error(edfile);
  920.         else
  921.         wprint("Message buffer empty\n");
  922.     when 'q':
  923.         /* save in dead.letter if nosave not set -- rm_edfile(-2). */
  924.         rm_edfile(-2); /* doesn't return out of tool mode */
  925.         return -1;
  926.         /* break; not stated cuz of "return" (lint) */
  927.     case 'x':
  928.         /* don't save dead.letter -- simulate normal rm_edfile() call */
  929.         rm_edfile(0);
  930. #ifdef SUNTOOL
  931.         if (istool) {
  932.         wprint("*Letter aborted*");
  933.         type_cursor(PIX_CLR);
  934.         }
  935. #endif /* SUNTOOL */
  936.         return -1;
  937.         /* break; (not specified for lint) */
  938.     default:
  939.         if (line[1] == *escape) {
  940.         fputs(&line[1], ed_fp), fputc('\n', ed_fp), fflush(ed_fp);
  941.         return 1;
  942.         } else if (line[1] == '?') {
  943.         register int x;
  944.         if (!istool)
  945.             (void) do_pager(NULL, TRUE); /* start pager */
  946.         for (x = 0; tilde_commands[x]; x++) {
  947.             (void) sprintf(buf, "%s%s\n", escape, tilde_commands[x]);
  948.             if (!istool) {
  949.             if (do_pager(buf, FALSE))
  950.                 break;
  951.             } else
  952.             wprint(buf);
  953.         }
  954.         (void) sprintf(buf, "%s%s\t\tbegin a line with a single %s\n",
  955.             escape, escape, escape);
  956.         if (istool)
  957.             wprint(buf);
  958.         else {
  959.             if (tilde_commands[x] == NULL)
  960.             (void) do_pager(buf, FALSE);
  961.             (void) do_pager(NULL, FALSE); /* end pager */
  962.         }
  963. #ifdef SUNTOOL
  964.         if (istool)
  965.             (void) help(0, "compose", tool_help);
  966. #endif /* SUNTOOL */
  967.         } else
  968.         wprint("`%c': unknown %c escape. Use %c? for help.\n",
  969.             line[1], *escape, *escape);
  970.     }
  971.     (void) fseek(ed_fp, 0L, 2); /* seek to end of file in case there's more */
  972.     wprint("(continue editing letter)\n");
  973.     return 1;
  974. }
  975.  
  976. /*
  977.  * finish up the letter. ask for the cc line, if verify is set, ask to
  978.  * verify sending, continue editing, or to dump the whole idea.
  979.  * Then check for signature and fortune.  Finally, pass it to send_it()
  980.  * to actually send it off.
  981.  */
  982. finish_up_letter()
  983. {
  984.     register char *p;
  985.     int c;
  986.     char buf[MAXPATHLEN];
  987.  
  988.     /* forwarded mail has no additional personalized text */
  989.     if (ison(flags, FORWARD)) {
  990.     send_it();
  991.     turnoff(glob_flags, IS_GETTING);
  992.     return 1;
  993.     }
  994.  
  995.     if (isoff(glob_flags, REDIRECT)) {
  996.     if (!istool) {
  997.         if (do_set(set_options, "askcc")) {
  998.         if (p = set_header("Cc: ", Cc, 1))
  999.             (void) strcpy(Cc, p);
  1000.         }
  1001. #ifdef MMDF
  1002.         /* Give some sort of indication that the end was seen */
  1003.         else
  1004.         wprint("EOT\n");
  1005. #endif /* MMDF */
  1006.     }
  1007.     /* ~v on the Cc line asks for verification, first initialize p! */
  1008.     p = NULL;
  1009.     if (!strncmp(Cc, "~v", 2) || (p = do_set(set_options, "verify"))) {
  1010.         if (!p) /* so we don't Cc to ~v! */
  1011.         *Cc = 0;
  1012.         for (;;) {
  1013. #ifdef SUNTOOL
  1014.         if (istool) {
  1015.             type_cursor(PIX_CLR);
  1016.             print("Send, Continue, Discard [Left, Middle, Right]?");
  1017.             c = confirm(msg_sw->ts_windowfd);
  1018.             clr_bot_line(); /* really: clears print window */
  1019.             if (isascii(c))
  1020.             Lower(c);
  1021.             else switch(c) {
  1022.             when MS_LEFT : c = 's';
  1023.             when MS_MIDDLE : c = 'c';
  1024.             when MS_RIGHT : c = 'd';
  1025.             otherwise: c = 0;
  1026.             }
  1027.         } else
  1028. #endif /* SUNTOOL */
  1029.         {
  1030.             print("send, continue editing, discard [s,c,d]? ");
  1031.             c = Getstr(buf, sizeof(buf), 0);
  1032.         }
  1033.         if (c < 0)
  1034.             putchar('\n');
  1035.         else if (!istool)
  1036.             c = lower(*buf);
  1037.         if (c == 'd') {
  1038.             rm_edfile(-2);
  1039.             return 1;
  1040.         } else if (c == 'c') {
  1041.             wprint("(continue editing letter)\n");
  1042. #ifdef SUNTOOL
  1043.             if (istool)
  1044.             type_cursor(PIX_SRC);
  1045. #endif /* SUNTOOL */
  1046.             return 0;
  1047.         } else if (c == 's')
  1048.             break;
  1049.         }
  1050.     }
  1051.     }
  1052.  
  1053. #ifdef SUNTOOL
  1054.     if (istool)
  1055.     lock_cursors();
  1056. #endif /* SUNTOOL */
  1057.     send_it();
  1058.     turnoff(glob_flags, IS_GETTING);
  1059.     return 1;
  1060. }
  1061.  
  1062. /*
  1063.  * actually send the letter.
  1064.  * 1. reset all the signals because of fork.
  1065.  * 2. determine recipients (users, address, files, programs)
  1066.  * 3. determine mailer, fork and return (if not verbose).
  1067.  * 4. popen mailer, $record, and other files specified in step 1.
  1068.  * 5. make the headers; this includes To: line, and user set headers, etc...
  1069.  * 6. copy the letter right into the array of file pointers (step 1).
  1070.  * 7. close the mailer and other files (step 1) and remove the edit-file.
  1071.  */
  1072. static void
  1073. send_it()
  1074. {
  1075.     register char *p, *b, *addr_list;
  1076. #ifdef MAXFILES
  1077.     register int size = MAXFILES - 1;
  1078.     FILE *files[MAXFILES];
  1079. #else
  1080.     register int size = getdtablesize() - 1;
  1081.     FILE *files[30];  /* 30 should be sufficiently large enough */
  1082. #endif /* MAXFILES */
  1083.     int next_file = 1; /* reserve files[0] for the mail delivery program */
  1084.     int log_file = -1; /* the index into the files array for mail logging */
  1085.     char buf[3*HDRSIZ];
  1086.     char expand = !do_set(set_options, "no_expand");
  1087.     int fork_err = 0;
  1088.  
  1089.     if (!istool) {
  1090.     (void) signal(SIGINT, oldint);
  1091.     (void) signal(SIGQUIT, oldquit);
  1092.     (void) signal(SIGTERM, oldterm);
  1093.     }
  1094.  
  1095.     if (!(p = do_set(set_options, "sendmail")))
  1096.     p = MAIL_DELIVERY;
  1097.  
  1098. #ifdef VERBOSE_ARG
  1099.     if (ison(flags, VERBOSE) || do_set(set_options, "verbose"))
  1100. #ifdef MMDF
  1101.     b = &buf[strlen(sprintf(buf, "%s%s", p, VERBOSE_ARG))];
  1102. #else /* MMDF */
  1103.     b = &buf[strlen(sprintf(buf, "%s %s", p, VERBOSE_ARG))];
  1104. #endif /* MMDF */
  1105.     else
  1106. #endif /* VERBOSE_ARG */
  1107.     b = buf + Strcpy(buf, p);
  1108. #ifdef METOO_ARG
  1109.     if (!strcmp(p, MAIL_DELIVERY) && do_set(set_options, "metoo"))
  1110.     b += strlen(sprintf(b, " %s", METOO_ARG));
  1111. #endif /* METOO_ARG */
  1112.     *b++ = ' ', *b = 0; /* strcat(b, " "); */
  1113.     addr_list = b; /* save this position to check for addresses later */
  1114.  
  1115.     /*
  1116.      * Build the address lines to give to the mail transfer system.  This
  1117.      * address line cannot contain comment fields!  First, expand aliases
  1118.      * since they may contain comment fields within addresses. Copy this
  1119.      * result back into the Buffer since this will go into the header ...
  1120.      * Next, remove all comments so the buffer contains ONLY valid addresses.
  1121.      * Next, strip off any filenames/programs which might occur in the list.
  1122.      * Finally, add this information to the command line buffer (buf).
  1123.      * Remove commas if necessary (see ifdefs).  In the event of errors,
  1124.      * force a dead letter by rm_edfile(-1).
  1125.      */
  1126.     if (!(p = alias_to_address(To))) {
  1127.     print("address expansion failed for To: line.\n");
  1128.     rm_edfile(-1);
  1129.     } else {
  1130.     next_file += find_files(p, files+next_file, size - next_file);
  1131.     if (expand)
  1132.         (void) strcpy(To, p);
  1133.     rm_cmts_in_addr(p);
  1134.     skipspaces(0);
  1135.     if (!*p) {
  1136.         print("There must be at least 1 legal recipient on the To line\n");
  1137.         while (--next_file > 0)
  1138.         fclose(files[next_file]);
  1139.         rm_edfile(-2);
  1140.         return;
  1141.     }
  1142.     b += Strcpy(b, p);
  1143.     }
  1144.     if (*Cc) {
  1145.     if (!(p = alias_to_address(Cc))) {
  1146.         print("address expansion failed for Cc: line.\n");
  1147.         while (--next_file > 0)
  1148.         fclose(files[next_file]);
  1149.         rm_edfile(-1);
  1150.     } else {
  1151.         next_file += find_files(p, files+next_file, size - next_file);
  1152.         if (expand)
  1153.         (void) strcpy(Cc, p);
  1154.         rm_cmts_in_addr(p);
  1155.         skipspaces(0);
  1156.         if (*p) {
  1157.         *b++ = ',', *b++ = ' ';
  1158.         b += Strcpy(b, p);
  1159.         }
  1160.     }
  1161.     }
  1162.  
  1163.     /* Sign the letter before adding the Bcc list since they aren't
  1164.      * considered when adding a signature.
  1165.      */
  1166.     if ((ison(flags, SIGN) || ison(flags, DO_FORTUNE)) &&
  1167.     isoff(glob_flags, REDIRECT) && isoff(flags, FORWARD))
  1168.     sign_letter(addr_list, flags, ed_fp);
  1169.  
  1170.     if (*Bcc) {
  1171.     if (!(p = alias_to_address(Bcc))) {
  1172.         print("address expansion failed for Bcc: line.\n");
  1173.         while (--next_file > 0)
  1174.         fclose(files[next_file]);
  1175.         rm_edfile(-1);
  1176.     } else {
  1177.         next_file += find_files(p, files+next_file, size - next_file);
  1178.         (void) strcpy(Bcc, p);
  1179.         rm_cmts_in_addr(p);
  1180.         skipspaces(0);
  1181.         if (*p) {
  1182.         *b++ = ',', *b++ = ' ';
  1183.         b += Strcpy(b, p);
  1184.         }
  1185.     }
  1186.     }
  1187.  
  1188. #ifdef NO_COMMAS
  1189.     for (p = buf; p = index(p, ','); p++)
  1190.     *p = ' ';
  1191. #endif /* NO_COMMAS */
  1192.  
  1193.     Debug("mail command: %s\n", buf);
  1194.  
  1195. #ifdef SUNTOOL
  1196.     if (istool)
  1197.     abort_mail(NO_ITEM, 0);
  1198. #endif /* SUNTOOL */
  1199.  
  1200.     if (isoff(flags, VERBOSE) && debug < 3)
  1201.     switch (fork()) {
  1202.         case  0:  /* the child will send the letter. ignore signals */
  1203. #ifdef SYSV
  1204.         if (setpgrp() == -1)
  1205.             error("setpgrp");
  1206. #endif /* SYSV */
  1207. #ifdef MMDF
  1208.         (void) signal(SIGCHLD, SIG_DFL);
  1209. #endif /* MMDF */
  1210.         (void) signal(SIGINT, SIG_IGN);
  1211.         (void) signal(SIGHUP, SIG_IGN);
  1212.         (void) signal(SIGQUIT, SIG_IGN);
  1213.         (void) signal(SIGTERM, SIG_IGN);
  1214. #ifdef SIGTTIN
  1215.         (void) signal(SIGTTOU, SIG_IGN);
  1216.         (void) signal(SIGTTIN, SIG_IGN);
  1217. #endif /* SIGTTIN */
  1218. #ifdef SIGCONT
  1219.         (void) signal(SIGCONT, SIG_IGN);
  1220.         (void) signal(SIGTSTP, SIG_IGN);
  1221. #endif /* SIGCONT */
  1222.         turnon(glob_flags, IGN_SIGS);
  1223.         when -1:
  1224.         error("fork failed trying to send mail");
  1225.         fork_err++;
  1226.         rm_edfile(-1);
  1227.         /* fall thru */
  1228.         default:
  1229.         if (!fork_err && isoff(glob_flags, REDIRECT))
  1230.             fclose(ed_fp);
  1231. #ifdef SUNTOOL
  1232.                 if (istool) {
  1233.             if (!fork_err) {
  1234.             wprint("Letter sent.");
  1235.             print("Letter sent.");
  1236.             }
  1237.             type_cursor(PIX_CLR);
  1238.         }
  1239. #endif /* SUNTOOL */
  1240.         while (--next_file > 0)
  1241.             fclose(files[next_file]);
  1242. #ifdef MMDF
  1243.         if (!fork_err)
  1244.             (void) wait((int *) 0);
  1245. #endif /* MMDF */
  1246.         return;
  1247.     }
  1248.  
  1249. #ifdef MMDF
  1250.     *(addr_list-1) = '\0';
  1251. #endif /* MMDF */
  1252.     if (debug > 2)
  1253.     files[0] = stdout;
  1254.     else if (!(files[0] = open_file(buf, TRUE))) {
  1255.     rm_edfile(-1); /* force saving of undeliverable mail */
  1256.     if (isoff(flags, VERBOSE) && debug < 3)
  1257.         exit(-1);
  1258.     else
  1259.         return;
  1260.     }
  1261.  
  1262.     if (ison(flags, VERBOSE))
  1263.     wprint("Sending letter ... "), fflush(stdout);
  1264. #ifdef MMDF
  1265.     /* give address list to submit */
  1266.     for (p = addr_list; p && (p = any(p, ",<")); p++)
  1267.     if (*p == ',')
  1268.         *p = '\n';
  1269.     else
  1270.         p = index(p, '>');
  1271.     fprintf(files[0], "%s\n\n", addr_list);
  1272. #endif /* MMDF */
  1273.  
  1274.     /* see if log is set.  This is just to add message headers. No msg body. */
  1275.     if (p = do_set(set_options, "logfile")) {
  1276.     if (!*p)
  1277.         p = "~/mail.log";
  1278.     (void) strcpy(buf, p);
  1279.     log_file = next_file;
  1280.     next_file += find_files(buf, files+next_file, size - next_file);
  1281.     if (log_file == next_file)
  1282.         log_file = -1;
  1283.     }
  1284.  
  1285.     /* see if record is set.  If so, open that file for appending and add
  1286.      * the letter in a format such that mail can be read from it
  1287.      */
  1288.     if (p = do_set(set_options, "record")) {
  1289.     if (!*p)
  1290.         p = "~/record";
  1291.     (void) strcpy(buf, p);
  1292.     next_file += find_files(buf, files+next_file, size - next_file);
  1293.     }
  1294.  
  1295.     /* Make folders conform to RFC-822 by adding From: and Date: headers.
  1296.      * Some older mailers (binmail, execmail, delivermail), don't add
  1297.      * these headers, so add them for #define OLD_MAILER.  Also add them
  1298.      * with the "Resent-" prefix when forwarding.
  1299.      */
  1300.     {
  1301.     time_t t;
  1302.     char From_buf[256], *pF = From_buf, date_str[64];
  1303.     char *host = NULL;
  1304.     
  1305.     if (ourname)
  1306.         host = ourname[0];
  1307.  
  1308.     if (ison(flags, FORWARD))
  1309.         pF += Strcpy(From_buf, "Resent-");
  1310.     pF += Strcpy(pF, "From: ");
  1311. #ifdef UUCP
  1312.     if (host && *host)
  1313.         pF += strlen(sprintf(pF, "%s!", host));
  1314. #endif /* UUCP */
  1315.     pF += Strcpy(pF, login);
  1316. #ifndef UUCP
  1317.     if (host && *host)
  1318.         pF += strlen(sprintf(pF, "@%s", host));
  1319. #endif /* UUCP */
  1320.     if (p = do_set(set_options, "realname"))
  1321.         pF += strlen(sprintf(pF, " (%s)", p));
  1322.     *pF++ = '\n', *pF++ = 0;
  1323.     (void) time(&t);
  1324.     for (size = 0; size < next_file; size++) {
  1325. #ifndef OLD_MAILER
  1326.         if (size == 0 && isoff(flags, FORWARD))
  1327.         continue;
  1328. #endif /* OLD_MAILER */
  1329.         if (size > 0) {
  1330. #ifndef MSG_SEPARATOR
  1331.         fprintf(files[size], "From %s %s", login, ctime(&t));
  1332. #else /* MSG_SEPARATOR */
  1333. #ifdef MMDF
  1334.         fputs(MSG_SEPARATOR, files[size]);
  1335. #else /* MMDF */
  1336.         fprintf(files[size], "%s\n", MSG_SEPARATOR);
  1337. #endif /* MMDF */
  1338. #endif /* MSG_SEPARATOR */
  1339.         }
  1340.         fputs(From_buf, files[size]);
  1341.         fprintf(files[size], "%sDate: %s\n",
  1342.         ison(flags, FORWARD) ? "Resent-" : "", rfc_date(date_str));
  1343.     }
  1344.     }
  1345.  
  1346.     /* first print users own message headers */
  1347.     if (own_hdrs && !do_set(set_options, "no_hdrs")) {
  1348.     struct options *opts;
  1349.     for (opts = own_hdrs; opts; opts = opts->next)
  1350.         for (size = 0; size < next_file; size++)
  1351.         fprintf(files[size], "%s %s\n", opts->option, opts->value);
  1352.     }
  1353.  
  1354.     wrap_addrs(To, 80);
  1355.     wrap_addrs(Cc, 80);
  1356.     wrap_addrs(Bcc, 80);
  1357.     /* send the header stuff to sendmail and end header with a blank line */
  1358.     for (size = 0; size < next_file; size++) {
  1359.     if (*in_reply_to)
  1360.         fprintf(files[size], "In-Reply-To: %s\n", in_reply_to);
  1361.     fprintf(files[size], "X-Mailer: %s\n", VERSION);
  1362.     fprintf(files[size], "%sTo: %s\n",
  1363.         ison(flags, FORWARD) ? "Resent-" : "", To);
  1364.     if (*Subject && isoff(flags, FORWARD))
  1365.         fprintf(files[size], "Subject: %s\n", Subject);
  1366.     if (*Cc)
  1367.         fprintf(files[size], "%sCc: %s\n",
  1368.         ison(flags, FORWARD) ? "Resent-" : "", Cc);
  1369.     if (size > 0) {
  1370.         /* Do not send these to mail transfer agent */
  1371.         if (*Bcc)
  1372.         fprintf(files[size], "%sBcc: %s\n",
  1373.         ison(flags, FORWARD) ? "Resent-" : "", Bcc);
  1374.         fprintf(files[size], "Status: OR\n");
  1375.     }
  1376.     if (isoff(flags, FORWARD))
  1377.         fputc('\n', files[size]);
  1378.     }
  1379.  
  1380.     /* if redirection, ed_fp = stdin, else rewind the file just made */
  1381.     if (isoff(glob_flags, REDIRECT))
  1382.     rewind(ed_fp);
  1383.     else
  1384.     ed_fp = stdin;
  1385.  
  1386.     /* Read from stdin or the edfile till EOF and send it all to the mailer
  1387.      * and other open files/folders/programs. Check for "From " at the
  1388.      * beginnings of these lines to prevent creating new messages in folders.
  1389.      * If forwarding, skip the leading From_ line of the forwarded message.
  1390.      */
  1391. #ifndef MSG_SEPARATOR
  1392.     if (ison(flags, FORWARD) && fgets(buf, sizeof buf, ed_fp)) {
  1393.     if (strncmp(buf, "From ", 5) != 0)
  1394.         rewind(ed_fp); /* No From_ line (should never happen) */
  1395.     }
  1396. #endif /* !MSG_SEPARATOR */
  1397.     while (fgets(buf, sizeof buf, ed_fp))
  1398.     for (size = 0; size < next_file; size++) {
  1399.         if (size == log_file)
  1400.         continue;
  1401. #ifndef MSG_SEPARATOR
  1402.         if (!strncmp(buf, "From ", 5))
  1403.         fputc('>', files[size]);
  1404. #endif /* MSG_SEPARATOR */
  1405.         fputs(buf, files[size]);
  1406.     }
  1407.  
  1408.     /* loop thru the open files (except for the first: the mail delivery agent)
  1409.      * and append a blank line so that ucb-mail can read these folders.
  1410.      * Then close the files.  If the file was a popened program, the sigchld
  1411.      * that the program generates will close the file.
  1412.      */
  1413. #ifdef END_MSG_SEP
  1414.     for (size = 1; size < next_file; size++) {
  1415.     fputs(END_MSG_SEP, files[size]);
  1416.     if (files[size])
  1417.         fclose(files[size]);
  1418.     }
  1419. #else /* !END_MSG_SEP */
  1420.     for (size = 1; size < next_file; size++)
  1421.     if (files[size]) {
  1422.         fputc('\n', files[size]);
  1423.         fclose(files[size]);
  1424.     }
  1425. #endif /* END_MSG_SEP */
  1426.  
  1427. #ifdef MMDF
  1428.     if (debug < 3) {
  1429.     int reply_code = pclose(files[0]);
  1430.     Debug("pclose reply_code = %d\n", reply_code);
  1431.     rm_edfile((reply_code >> 8) == 9 ? 0 : -1);
  1432.     } else
  1433.     rm_edfile(0);
  1434. #else /* MMDF */
  1435.     rm_edfile(0);
  1436.     if (debug < 3)
  1437.     (void) pclose(files[0]);
  1438. #endif /* MMDF */
  1439.  
  1440.     if ((ison(flags, VERBOSE) || debug > 2) && isoff(glob_flags, REDIRECT))
  1441.     wprint("sent.\n");
  1442.     else
  1443.     exit(0); /* not a user exit -- a child exit */
  1444. }
  1445.  
  1446. /* ARGSUSED */
  1447. SIGRET
  1448. rm_edfile(sig)
  1449. {
  1450.     if (sig > 0) {
  1451.     /* wrapcolumn may have been trashed -- restore it */
  1452.     char *fix = do_set(set_options, "wrapcolumn");
  1453.     if (fix && *fix)
  1454.         wrapcolumn = atoi(fix);
  1455.     mac_flush(); /* abort pending macros */
  1456.     }
  1457.     /* now check whether we should abort the letter */
  1458.     if (sig > 0 && !killme) {
  1459.     if (!istool)
  1460.         (void) signal(sig, rm_edfile);
  1461.     killme = 1;
  1462.     wprint("\n** interrupt -- one more to kill letter **\n");
  1463. #ifdef SUNTOOL
  1464.     if (istool) {
  1465.         type_cursor(PIX_SRC);
  1466.         return;
  1467.     }
  1468. #endif /* SUNTOOL */
  1469.     longjmp(cntrl_c_buf, 1);
  1470.     }
  1471.     killme = 0;
  1472.     /* if sig == -1, force a save into dead.letter.
  1473.      * else, check for nosave not being set and save anyway if it's not set
  1474.      * sig == 0 indicates normal exit (or ~x), so don't save a dead letter.
  1475.      */
  1476.     if (sig == -1 || sig != 0 && !do_set(set_options, "nosave"))
  1477.     dead_letter();
  1478.     if (isoff(glob_flags, REDIRECT))
  1479.     fclose(ed_fp);
  1480.     (void) unlink(edfile);
  1481.  
  1482.     turnoff(glob_flags, IS_GETTING);
  1483.     if (sig == -1)
  1484.     return;
  1485. #ifdef SUNTOOL
  1486.     if (sig && istool > 1) {
  1487.     wprint("*Letter aborted*");
  1488.     abort_mail(abort_item, 2);
  1489.     }
  1490. #endif /* SUNTOOL */
  1491.  
  1492.     if (sig == SIGHUP)
  1493.     cleanup(0);
  1494.     if (!istool) {
  1495.     (void) signal(SIGINT, oldint);
  1496.     (void) signal(SIGQUIT, oldquit);
  1497.     (void) signal(SIGTERM, oldterm);
  1498.     }
  1499.  
  1500.     if (sig == 0)
  1501.     return;
  1502.     if (istool || sig == -2) /* make sure sigchld is reset first */
  1503.     return;
  1504.  
  1505.     if (isoff(glob_flags, DO_SHELL)) {  /* If we're not in a shell, exit */
  1506.     puts("exiting");
  1507.     echo_on();
  1508.     exit(1);
  1509.     }
  1510.     longjmp(jmpbuf, 1);
  1511. }
  1512.  
  1513. /* save letter into dead letter */
  1514. dead_letter()
  1515. {
  1516.     char     *p, buf[BUFSIZ];
  1517.     long     t;
  1518.     FILE     *dead;
  1519.  
  1520.     if (ison(glob_flags, REDIRECT)) {
  1521.     print("input redirected -- can't save dead letter.\n");
  1522.     return;
  1523.     }
  1524.     /* If the file doesn't exist, get outta here. File may not exist if
  1525.      * user generated a ^C from a promptable header and catch sent us here.
  1526.      */
  1527.     if (Access(edfile, R_OK))
  1528.     return;
  1529.     /* User may have killed mush via a signal while he was in an editor.
  1530.      * ed_fp will be NULL in this case.  Since the file does exist (above),
  1531.      * open it so we can copy it to dead letter.
  1532.      */
  1533.     if (!ed_fp && !(ed_fp = fopen(edfile, "r"))) {
  1534.     error("can't save dead letter from %s", edfile);
  1535.     return;
  1536.     }
  1537.     /* don't save a dead letter if there's nothing to save. */
  1538.     if (fseek(ed_fp, 0L, 2) || ftell(ed_fp) <= 1L)
  1539.     return;
  1540.     if (!(p = do_set(set_options, "dead")))
  1541.     p = "~/dead.letter";
  1542.     if (!(dead = open_file(p, FALSE)))
  1543.     return;
  1544.     (void) time (&t);
  1545.     fflush(ed_fp);
  1546.     rewind(ed_fp);
  1547. #ifdef MSG_SEPARATOR
  1548.     fputs(MSG_SEPARATOR, dead);
  1549. #else /* MSG_SEPARATOR */
  1550.     fprintf(dead, "From %s %s", login, ctime(&t));
  1551. #endif /* MSG_SEPARATOR */
  1552.     fprintf(dead, "To: %s\nSubject: %s\n", To, Subject);
  1553.     fprintf(dead, "Date: %s\n", rfc_date(buf));
  1554.     if (*Cc)
  1555.     fprintf(dead, "Cc: %s\n", Cc);
  1556.     if (*Bcc)
  1557.     fprintf(dead, "Bcc: %s\n", Bcc);
  1558.     (void) fputc('\n', dead);
  1559.     while (fgets(buf, sizeof(buf), ed_fp))
  1560.     (void) fputs(buf, dead);
  1561.     (void) fputc('\n', dead);
  1562. #ifdef END_MSG_SEP
  1563.     fputs(END_MSG_SEP, dead);
  1564. #endif /* END_MSG_SEP */
  1565.     (void) fclose(dead);
  1566.     print("Saved unfinished letter in %s.\n", p);
  1567. }
  1568.