home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / clients / xmh / msg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-24  |  23.8 KB  |  976 lines

  1. /*
  2.  * $XConsortium: msg.c,v 2.49 91/07/24 20:28:31 converse Exp $
  3.  *
  4.  *
  5.  *               COPYRIGHT 1987, 1989
  6.  *           DIGITAL EQUIPMENT CORPORATION
  7.  *               MAYNARD, MASSACHUSETTS
  8.  *            ALL RIGHTS RESERVED.
  9.  *
  10.  * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
  11.  * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
  12.  * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
  13.  * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
  14.  *
  15.  * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
  16.  * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
  17.  * ADDITION TO THAT SET FORTH ABOVE.
  18.  *
  19.  * Permission to use, copy, modify, and distribute this software and its
  20.  * documentation for any purpose and without fee is hereby granted, provided
  21.  * that the above copyright notice appear in all copies and that both that
  22.  * copyright notice and this permission notice appear in supporting
  23.  * documentation, and that the name of Digital Equipment Corporation not be
  24.  * used in advertising or publicity pertaining to distribution of the software
  25.  * without specific, written prior permission.
  26.  */
  27.  
  28. /* msgs.c -- handle operations on messages. */
  29.  
  30. #include <X11/Xaw/Cardinals.h>
  31.  
  32. #include "xmh.h"
  33. #include "tocintrnl.h"
  34.  
  35. static int SetScrn();
  36.  
  37. /*    Function Name: SetEditable
  38.  *    Description: Sets the editable flag for this message.
  39.  *    Arguments: msg - the message.
  40.  *                 edit - set editable to this.
  41.  *    Returns: none
  42.  */
  43.  
  44. static void
  45. SetEditable(msg, edit)
  46. Msg msg;
  47. Boolean edit;
  48. {
  49.   Arg args[1];
  50.  
  51.   if (edit)
  52.     XtSetArg(args[0], XtNeditType, XawtextEdit);
  53.   else
  54.     XtSetArg(args[0], XtNeditType, XawtextRead);
  55.  
  56.   XtSetValues(msg->source, args, ONE);
  57. }
  58.  
  59. /*    Function Name: IsEditable
  60.  *    Description: Returns true if this is an editable message.
  61.  *    Arguments: msg - the message to edit.
  62.  *    Returns: TRUE if editable.
  63.  */
  64.  
  65. static Boolean
  66. IsEditable(msg)
  67. Msg msg;
  68. {
  69.   Arg args[1];
  70.   XawTextEditType type;
  71.  
  72.   XtSetArg(args[0], XtNeditType, &type);
  73.   XtGetValues(msg->source, args, ONE);
  74.  
  75.   return(type == XawtextEdit);
  76. }
  77.  
  78. /* Return the user-viewable name of the given message. */
  79.  
  80. static char *NameOfMsg(msg)
  81. Msg msg;
  82. {
  83.     static char result[100];
  84.     (void) sprintf(result, "%s:%d", msg->toc->foldername, msg->msgid);
  85.     return result;
  86. }
  87.  
  88.  
  89. /* Update the message titlebar in the given scrn. */
  90.  
  91. static void ResetMsgLabel(scrn)
  92. Scrn scrn;
  93. {
  94.     Msg msg;
  95.     char str[200];
  96.     if (scrn) {
  97.      msg = scrn->msg;
  98.     if (msg == NULL) (void) strcpy(str, app_resources.banner);
  99.     else {
  100.         (void) strcpy(str, NameOfMsg(msg));
  101.         switch (msg->fate) {
  102.           case Fdelete:
  103.         (void) strcat(str, " -> *Delete*");
  104.          break;
  105.           case Fcopy:
  106.           case Fmove:
  107.         (void) strcat(str, " -> ");
  108.         (void) strcat(str, msg->desttoc->foldername);
  109.         if (msg->fate == Fcopy)
  110.             (void) strcat(str, " (Copy)");
  111.         break;
  112.         }
  113.         if (msg->temporary) (void)strcat(str, " [Temporary]");
  114.     }
  115.     ChangeLabel((Widget) scrn->viewlabel, str);
  116.     }
  117. }
  118.  
  119.  
  120. /* A major msg change has occured; redisplay it.  (This also should
  121. work even if we now have a new source to display stuff from.)  This
  122. routine arranges to hide boring headers, and also will set the text
  123. insertion point to the proper place if this is a composition and we're
  124. viewing it for the first time. */
  125.  
  126. static void RedisplayMsg(scrn)
  127. Scrn scrn;
  128. {
  129.     Msg msg;
  130.     XawTextPosition startPos, lastPos, nextPos;
  131.     int length; char str[100];
  132.     XawTextBlock block;
  133.     if (scrn) {
  134.     msg = scrn->msg;
  135.     if (msg) {
  136.         startPos = 0;
  137.         if (app_resources.hide_boring_headers && scrn->kind != STcomp) {
  138.         lastPos = XawTextSourceScan(msg->source, (XawTextPosition) 0,
  139.                         XawstAll, XawsdRight, 1, FALSE);
  140.          while (startPos < lastPos) {
  141.             nextPos = startPos;
  142.             length = 0;
  143.             while (length < 8 && nextPos < lastPos) {
  144.                 nextPos = XawTextSourceRead(msg->source, nextPos,
  145.                             &block, 8 - length);
  146.             (void) strncpy(str + length, block.ptr, block.length);
  147.              length += block.length;
  148.             }
  149.             if (length == 8) {
  150.             if (strncmp(str, "From:", 5) == 0 ||
  151.                 strncmp(str, "To:", 3) == 0 ||
  152.                 strncmp(str, "Date:", 5) == 0 ||
  153.                 strncmp(str, "Subject:", 8) == 0) break;
  154.             }
  155.             startPos = XawTextSourceScan(msg->source, startPos,
  156.                             XawstEOL, XawsdRight, 1, TRUE);
  157.          }
  158.         if (startPos >= lastPos) startPos = 0;
  159.         }
  160.         XawTextSetSource(scrn->viewwidget, msg->source, startPos);
  161.         if (msg->startPos > 0) {
  162.         XawTextSetInsertionPoint(scrn->viewwidget, msg->startPos);
  163.         msg->startPos = 0; /* Start in magic place only once. */
  164.         }
  165.     } else {
  166.         XawTextSetSource(scrn->viewwidget, PNullSource,
  167.                  (XawTextPosition)0);
  168.      }
  169.     }
  170. }
  171.  
  172.  
  173.  
  174. static char tempDraftFile[100] = "";
  175.  
  176. /* Temporarily move the draftfile somewhere else, so we can exec an mh
  177.    command that affects it. */
  178.  
  179. static void TempMoveDraft()
  180. {
  181.     char *ptr;
  182.     if (FileExists(draftFile)) {
  183.     do {
  184.         ptr = MakeNewTempFileName();
  185.         (void) strcpy(tempDraftFile, draftFile);
  186.         (void) strcpy(rindex(tempDraftFile, '/'), rindex(ptr, '/'));
  187.     } while (FileExists(tempDraftFile));
  188.     RenameAndCheck(draftFile, tempDraftFile);
  189.     }
  190. }
  191.  
  192.  
  193.  
  194. /* Restore the draftfile from its temporary hiding place. */
  195.  
  196. static void RestoreDraft()
  197. {
  198.     if (*tempDraftFile) {
  199.     RenameAndCheck(tempDraftFile, draftFile);
  200.     *tempDraftFile = 0;
  201.     }
  202. }
  203.  
  204.  
  205.  
  206. /* Public routines */
  207.  
  208.  
  209. /* Given a message, return the corresponding filename. */
  210.  
  211. char *MsgFileName(msg)
  212. Msg msg;
  213. {
  214.     static char result[500];
  215.     (void) sprintf(result, "%s/%d", msg->toc->path, msg->msgid);
  216.     return result;
  217. }
  218.  
  219.  
  220.  
  221. /* Save any changes to a message.  Also calls the toc routine to update the
  222.    scanline for this msg.  Returns True if saved, false otherwise. */
  223.  
  224. MsgSaveChanges(msg)
  225. Msg msg;
  226. {
  227.     int i;
  228.     if (msg->source) {
  229.     if (XawAsciiSave(msg->source)) {
  230.         for (i=0; i < (int) msg->num_scrns; i++)
  231.         EnableProperButtons(msg->scrn[i]);
  232.         if (!msg->temporary)
  233.         TocMsgChanged(msg->toc, msg);
  234.         return True;
  235.     }
  236.     else {
  237.         char str[256];
  238.         (void) sprintf(str, "Cannot save changes to \"%s/%d\"!",
  239.                msg->toc->foldername, msg->msgid);
  240.         PopupError((Widget)NULL, str);
  241.         return False;
  242.     }
  243.     }
  244.     Feep();
  245.     return False;
  246. }
  247.  
  248.  
  249. /*
  250.  * Show the given message in the given scrn.  If a message is changed, and we
  251.  * are removing it from any scrn, then ask for confirmation first.  If the
  252.  * scrn was showing a temporary msg that is not being shown in any other scrn,
  253.  * it is deleted.  If scrn is NULL, then remove the message from every scrn
  254.  * that's showing it.
  255.  */
  256.  
  257.  
  258. /*ARGSUSED*/
  259. static void ConfirmedNoScrn(widget, client_data, call_data)
  260.     Widget    widget;        /* unused */
  261.     XtPointer    client_data;
  262.     XtPointer    call_data;    /* unused */
  263. {
  264.     Msg        msg = (Msg) client_data;
  265.     register int i;
  266.  
  267.     for (i=msg->num_scrns - 1 ; i >= 0 ; i--)
  268.     SetScrn((Msg)NULL, msg->scrn[i], TRUE, (XtCallbackList) NULL,
  269.         (XtCallbackList) NULL);
  270. }
  271.  
  272.  
  273. static void RemoveMsgConfirmed(scrn)
  274.     Scrn    scrn;
  275. {
  276.     if (scrn->kind == STtocAndView && MsgChanged(scrn->msg)) {
  277.     Arg    args[1];
  278.     XtSetArg(args[0], XtNtranslations, scrn->read_translations);
  279.     XtSetValues(scrn->viewwidget, args, (Cardinal) 1);
  280.     }
  281.     scrn->msg->scrn[0] = NULL;
  282.     scrn->msg->num_scrns = 0;
  283.     XawTextSetSource(scrn->viewwidget, PNullSource, (XawTextPosition) 0);
  284.     XtDestroyWidget(scrn->msg->source);
  285.     scrn->msg->source = NULL;
  286.     if (scrn->msg->temporary) {
  287.     (void) unlink(MsgFileName(scrn->msg));
  288.     TocRemoveMsg(scrn->msg->toc, scrn->msg);
  289.     MsgFree(scrn->msg);
  290.     }        
  291. }
  292.  
  293.  
  294. static void SetScrnNewMsg(msg, scrn)
  295.     Msg        msg;
  296.     Scrn    scrn;
  297. {
  298.    scrn->msg = msg;
  299.    if (msg == NULL) {
  300.     XawTextSetSource(scrn->viewwidget, PNullSource, (XawTextPosition) 0);
  301.     ResetMsgLabel(scrn);
  302.     EnableProperButtons(scrn);
  303.     if (scrn->kind != STtocAndView)
  304.         StoreWindowName(scrn, progName);
  305.     } else {
  306.     msg->num_scrns++;
  307.     msg->scrn = (Scrn *) XtRealloc((char *)msg->scrn,
  308.                        (unsigned) sizeof(Scrn)*msg->num_scrns);
  309.     msg->scrn[msg->num_scrns - 1] = scrn;
  310.     if (msg->source == NULL)
  311.         msg->source = CreateFileSource(scrn->viewwidget, MsgFileName(msg),
  312.                        scrn->kind == STcomp);
  313.     ResetMsgLabel(scrn);
  314.     RedisplayMsg(scrn);
  315.     EnableProperButtons(scrn);
  316.     if (scrn->kind != STtocAndView)
  317.         StoreWindowName(scrn, NameOfMsg(msg));
  318.     }
  319. }
  320.  
  321. typedef struct _MsgAndScrn {
  322.     Msg        msg;
  323.     Scrn    scrn;
  324. } MsgAndScrnRec, *MsgAndScrn;
  325.  
  326. /*ARGSUSED*/
  327. static void ConfirmedWithScrn(widget, client_data, call_data)
  328.     Widget    widget;        /* unused */
  329.     XtPointer    client_data;
  330.     XtPointer    call_data;    /* unused */
  331. {
  332.     MsgAndScrn    mas = (MsgAndScrn) client_data;
  333.     RemoveMsgConfirmed(mas->scrn);
  334.     SetScrnNewMsg(mas->msg, mas->scrn);
  335.     XtFree((char *) mas);
  336. }
  337.     
  338.     
  339. static int SetScrn(msg, scrn, force, confirms, cancels)
  340.     Msg        msg;
  341.     Scrn    scrn;
  342.     Boolean    force;            /* if true, force msg set scrn */
  343.     XtCallbackList    confirms;    /* callbacks upon confirmation */
  344.     XtCallbackList    cancels;    /* callbacks upon cancellation */
  345. {
  346.     register int i, num_scrns;
  347.     static XtCallbackRec yes_callbacks[] = {
  348.     {(XtCallbackProc) NULL,    (XtPointer) NULL},
  349.     {(XtCallbackProc) NULL,    (XtPointer) NULL},
  350.     {(XtCallbackProc) NULL,    (XtPointer) NULL}
  351.     };
  352.  
  353.     if (scrn == NULL) {
  354.     if (msg == NULL || msg->num_scrns == 0) return 0;
  355.     if (!force && XawAsciiSourceChanged(msg->source)) {
  356.         char str[100];
  357.         (void) sprintf(str,
  358.                "Are you sure you want to remove changes to %s?",
  359.                NameOfMsg(msg));
  360.  
  361.         yes_callbacks[0].callback = ConfirmedNoScrn;
  362.         yes_callbacks[0].closure = (XtPointer) msg;
  363.         yes_callbacks[1].callback = confirms[0].callback;
  364.         yes_callbacks[1].closure = confirms[0].closure;
  365.  
  366.         PopupConfirm((Widget) NULL, str, yes_callbacks, cancels);
  367.         return NEEDS_CONFIRMATION;
  368.     }
  369.     ConfirmedNoScrn((Widget)NULL, (XtPointer) msg, (XtPointer) NULL);
  370.     return 0;
  371.     }
  372.  
  373.     if (scrn->msg == msg) return 0;
  374.  
  375.     if (scrn->msg) {
  376.     num_scrns = scrn->msg->num_scrns;
  377.     for (i=0 ; i<num_scrns ; i++)
  378.         if (scrn->msg->scrn[i] == scrn) break;
  379.     if (i >= num_scrns) Punt("Couldn't find scrn in SetScrn!");
  380.     if (num_scrns > 1)
  381.         scrn->msg->scrn[i] = scrn->msg->scrn[--(scrn->msg->num_scrns)];
  382.     else {
  383.         if (!force && XawAsciiSourceChanged(scrn->msg->source)) {
  384.         char        str[100];
  385.         MsgAndScrn    cb_data;
  386.  
  387.         cb_data = XtNew(MsgAndScrnRec);
  388.         cb_data->msg = msg;
  389.         cb_data->scrn = scrn;
  390.         (void)sprintf(str,
  391.                   "Are you sure you want to remove changes to %s?",
  392.                   NameOfMsg(scrn->msg));
  393.         yes_callbacks[0].callback = ConfirmedWithScrn;
  394.         yes_callbacks[0].closure = (XtPointer) cb_data;
  395.         yes_callbacks[1].callback = confirms[0].callback;
  396.         yes_callbacks[1].closure = confirms[0].closure;
  397.         PopupConfirm(scrn->viewwidget, str, yes_callbacks, cancels);
  398.         return NEEDS_CONFIRMATION;
  399.         }
  400.         RemoveMsgConfirmed(scrn);
  401.     }
  402.     }
  403.     SetScrnNewMsg(msg, scrn);
  404.     return 0;
  405. }
  406.  
  407.  
  408.  
  409. /* Associate the given msg and scrn, asking for confirmation if necessary. */
  410.  
  411. int MsgSetScrn(msg, scrn, confirms, cancels)
  412. Msg msg;
  413. Scrn scrn;
  414. XtCallbackList    confirms;
  415. XtCallbackList    cancels;
  416. {
  417.     return SetScrn(msg, scrn, FALSE, confirms, cancels);
  418. }
  419.  
  420.  
  421. /* Same as above, but with the extra information that the message is actually
  422.    a composition.  (Nothing currently takes advantage of that extra fact.) */
  423.  
  424. void MsgSetScrnForComp(msg, scrn)
  425. Msg msg;
  426. Scrn scrn;
  427. {
  428.     (void) SetScrn(msg, scrn, FALSE, (XtCallbackList) NULL, 
  429.            (XtCallbackList) NULL);
  430. }
  431.  
  432.  
  433. /* Associate the given msg and scrn, even if it means losing some unsaved
  434.    changes. */
  435.  
  436. void MsgSetScrnForce(msg, scrn)
  437. Msg msg;
  438. Scrn scrn;
  439. {
  440.     (void) SetScrn(msg, scrn, TRUE, (XtCallbackList) NULL,
  441.            (XtCallbackList) NULL);
  442. }
  443.  
  444.  
  445.  
  446. /* Set the fate of the given message. */
  447.  
  448. void MsgSetFate(msg, fate, desttoc)
  449.   Msg msg;
  450.   FateType fate;
  451.   Toc desttoc;
  452. {
  453.     Toc toc = msg->toc;
  454.     XawTextBlock block;
  455.     int i;
  456.     msg->fate = fate;
  457.     msg->desttoc = desttoc;
  458.     if (fate == Fignore && msg == msg->toc->curmsg)
  459.     block.ptr = "+";
  460.     else {
  461.     switch (fate) {
  462.         case Fignore:    block.ptr = " "; break;
  463.         case Fcopy:        block.ptr = "C"; break;
  464.         case Fmove:        block.ptr = "^"; break;
  465.         case Fdelete:    block.ptr = "D"; break;
  466.     }
  467.     }
  468.     block.firstPos = 0;
  469.     block.format = FMT8BIT;
  470.     block.length = 1;
  471.     if (toc->stopupdate)
  472.     toc->needsrepaint = TRUE;
  473.     if (toc->num_scrns && msg->visible && !toc->needsrepaint &&
  474.         *block.ptr != msg->buf[MARKPOS])
  475.     (void)XawTextReplace(msg->toc->scrn[0]->tocwidget, /*%%%SourceReplace*/
  476.                 msg->position + MARKPOS,
  477.                 msg->position + MARKPOS + 1, &block);
  478.     else
  479.     msg->buf[MARKPOS] = *block.ptr;
  480.     for (i=0; i < (int) msg->num_scrns; i++)
  481.     ResetMsgLabel(msg->scrn[i]);
  482. }
  483.  
  484.  
  485.  
  486. /* Get the fate of this message. */
  487.  
  488. FateType MsgGetFate(msg, toc)
  489. Msg msg;
  490. Toc *toc;            /* RETURN */
  491. {
  492.     if (toc) *toc = msg->desttoc;
  493.     return msg->fate;
  494. }
  495.  
  496.  
  497. /* Make this a temporary message. */
  498.  
  499. void MsgSetTemporary(msg)
  500. Msg msg;
  501. {
  502.     int i;
  503.     msg->temporary = TRUE;
  504.     for (i=0; i < (int) msg->num_scrns; i++)
  505.     ResetMsgLabel(msg->scrn[i]);
  506. }
  507.  
  508.  
  509. /* Make this a permanent message. */
  510.  
  511. void MsgSetPermanent(msg)
  512. Msg msg;
  513. {
  514.     int i;
  515.     msg->temporary = FALSE;
  516.     for (i=0; i < (int) msg->num_scrns; i++)
  517.     ResetMsgLabel(msg->scrn[i]);
  518. }
  519.  
  520.  
  521.  
  522. /* Return the id# of this message. */
  523.  
  524. int MsgGetId(msg)
  525. Msg msg;
  526. {
  527.     return msg->msgid;
  528. }
  529.  
  530.  
  531. /* Return the scanline for this message. */
  532.  
  533. char *MsgGetScanLine(msg)
  534. Msg msg;
  535. {
  536.     return msg->buf;
  537. }
  538.  
  539.  
  540.  
  541. /* Return the toc this message is in. */
  542.  
  543. Toc MsgGetToc(msg)
  544. Msg msg;
  545. {
  546.     return msg->toc;
  547. }
  548.  
  549.  
  550. /* Set the reapable flag for this msg. */
  551.  
  552. void MsgSetReapable(msg)
  553. Msg msg;
  554. {
  555.     int i;
  556.     msg->reapable = TRUE;
  557.     for (i=0; i < (int) msg->num_scrns; i++)
  558.     EnableProperButtons(msg->scrn[i]);
  559. }
  560.  
  561.  
  562.  
  563. /* Clear the reapable flag for this msg. */
  564.  
  565. void MsgClearReapable(msg)
  566. Msg msg;
  567. {
  568.     int i;
  569.     msg->reapable = FALSE;
  570.     for (i=0; i < (int) msg->num_scrns; i++)
  571.     EnableProperButtons(msg->scrn[i]);
  572. }
  573.  
  574.  
  575. /* Get the reapable value for this msg.  Returns TRUE iff the reapable flag
  576.    is set AND no changes have been made. */
  577.  
  578. int MsgGetReapable(msg)
  579. Msg msg;
  580. {
  581.     return msg == NULL || (msg->reapable &&
  582.                (msg->source == NULL ||
  583.                 !XawAsciiSourceChanged(msg->source)));
  584. }
  585.  
  586.  
  587. /* Make it possible to edit the given msg. */
  588. void MsgSetEditable(msg)
  589. Msg msg;
  590. {
  591.     int i;
  592.     if (msg && msg->source) {
  593.     SetEditable(msg, TRUE);
  594.     for (i=0; i < (int) msg->num_scrns; i++)
  595.         EnableProperButtons(msg->scrn[i]);
  596.     }
  597. }
  598.  
  599.  
  600.  
  601. /* Turn off editing for the given msg. */
  602.  
  603. void MsgClearEditable(msg)
  604. Msg msg;
  605. {
  606.     int i;
  607.     if (msg && msg->source) {
  608.     SetEditable(msg, FALSE);
  609.     for (i=0; i < (int) msg->num_scrns; i++)
  610.         EnableProperButtons(msg->scrn[i]);
  611.     }
  612. }
  613.  
  614.  
  615.  
  616. /* Get whether the msg is editable. */
  617.  
  618. int MsgGetEditable(msg)
  619. Msg msg;
  620. {
  621.     return msg && msg->source && IsEditable(msg);
  622. }
  623.  
  624.  
  625. /* Get whether the msg has changed since last saved. */
  626.  
  627. int MsgChanged(msg)
  628. Msg msg;
  629. {
  630.     return msg && msg->source && XawAsciiSourceChanged(msg->source);
  631. }
  632.  
  633. /* Call the given function when the msg changes. */
  634.  
  635. void 
  636. MsgSetCallOnChange(msg, func, param)
  637. Msg msg;
  638. void (*func)();
  639. XtPointer param;
  640. {
  641.   Arg args[1];
  642.   static XtCallbackRec cb[] = { {NULL, NULL}, {NULL, NULL} };
  643.  
  644.   if (func != NULL) {
  645.     cb[0].callback = func;
  646.     cb[0].closure = param;
  647.     XtSetArg(args[0], XtNcallback, cb);
  648.   }
  649.   else
  650.     XtSetArg(args[0], XtNcallback, NULL);
  651.  
  652.   XtSetValues(msg->source, args, (Cardinal) 1);
  653.  
  654. }
  655.  
  656. /* Send (i.e., mail) the given message as is.  First break it up into lines,
  657.    and copy it to a new file in the process.  The new file is one of 10
  658.    possible draft files; we rotate amoung the 10 so that the user can have up
  659.    to 10 messages being sent at once.  (Using a file in /tmp is a bad idea
  660.    because these files never actually get deleted, but renamed with some
  661.    prefix.  Also, these should stay in an area private to the user for
  662.    security.) */
  663.  
  664. void MsgSend(msg)
  665. Msg msg;
  666. {
  667.     FILEPTR from;
  668.     FILEPTR to;
  669.     int     p, c, l, inheader, sendwidth, sendbreakwidth;
  670.     char   *ptr, *ptr2, **argv, str[100];
  671.     static sendcount = -1;
  672.     (void) MsgSaveChanges(msg);
  673.     from = FOpenAndCheck(MsgFileName(msg), "r");
  674.     sendcount = (sendcount + 1) % 10;
  675.     (void) sprintf(str, "%s%d", xmhDraftFile, sendcount);
  676.     to = FOpenAndCheck(str, "w");
  677.     sendwidth = app_resources.send_line_width;
  678.     sendbreakwidth = app_resources.break_send_line_width;
  679.     inheader = TRUE;
  680.     while (ptr = ReadLine(from)) {
  681.     if (inheader) {
  682.         if (strncmpIgnoringCase(ptr, "sendwidth:", 10) == 0) {
  683.         if (atoi(ptr+10) > 0) sendwidth = atoi(ptr+10);
  684.         continue;
  685.         }
  686.         if (strncmpIgnoringCase(ptr, "sendbreakwidth:", 15) == 0) {
  687.         if (atoi(ptr+15) > 0) sendbreakwidth = atoi(ptr+15);
  688.         continue;
  689.         }
  690.         for (l = 0, ptr2 = ptr ; *ptr2 && !l ; ptr2++)
  691.         l = (*ptr2 != ' ' && *ptr2 != '\t' && *ptr != '-');
  692.         if (l) {
  693.         (void) fprintf(to, "%s\n", ptr);
  694.         continue;
  695.         }
  696.         inheader = FALSE;
  697.         if (sendbreakwidth < sendwidth) sendbreakwidth = sendwidth;
  698.     }
  699.     do {
  700.         for (p = c = l = 0, ptr2 = ptr;
  701.          *ptr2 && c < sendbreakwidth;
  702.          p++, ptr2++) {
  703.          if (*ptr2 == ' ' && c < sendwidth)
  704.              l = p;
  705.          if (*ptr2 == '\t') {
  706.              if (c < sendwidth) l = p;
  707.              c += 8 - (c % 8);
  708.          }
  709.          else
  710.          c++;
  711.          }
  712.         if (c < sendbreakwidth) {
  713.         (void) fprintf(to, "%s\n", ptr);
  714.         *ptr = 0;
  715.         }
  716.         else
  717.         if (l) {
  718.             ptr[l] = 0;
  719.             (void) fprintf(to, "%s\n", ptr);
  720.             ptr += l + 1;
  721.         }
  722.         else {
  723.             for (c = 0; c < sendwidth; ) {
  724.             if (*ptr == '\t') c += 8 - (c % 8);
  725.             else c++;
  726.             (void) fputc(*ptr++, to);
  727.             }
  728.             (void) fputc('\n', to);
  729.         }
  730.     } while (*ptr);
  731.     }
  732.     (void) myfclose(from);
  733.     (void) myfclose(to);
  734.     argv = MakeArgv(3);
  735.     argv[0] = "send";
  736.     argv[1] = "-push";
  737.     argv[2] = str;
  738.     DoCommand(argv, (char *) NULL, (char *) NULL);
  739.     XtFree((char *) argv);
  740. }
  741.  
  742.  
  743. /* Make the msg into the form for a generic composition.  Set msg->startPos
  744.    so that the text insertion point will be placed at the end of the first
  745.    line (which is usually the "To:" field). */
  746.  
  747. void MsgLoadComposition(msg)
  748. Msg msg;
  749. {
  750.     static char *blankcomp = NULL; /* Array containing comp template */
  751.     static int compsize = 0;
  752.     static XawTextPosition startPos;
  753.     char *file, **argv;
  754.     int fid;
  755.     if (blankcomp == NULL) {
  756.     file = MakeNewTempFileName();
  757.     argv = MakeArgv(5);
  758.     argv[0] = "comp";
  759.     argv[1] = "-file";
  760.     argv[2] = file;
  761.     argv[3] = "-nowhatnowproc";
  762.     argv[4] = "-nodraftfolder";
  763.     DoCommand(argv, (char *) NULL, (char *) NULL);
  764.     XtFree((char *) argv);
  765.     compsize = GetFileLength(file);
  766.     if (compsize > 0) {
  767.         blankcomp = XtMalloc((Cardinal) compsize);
  768.         fid = myopen(file, O_RDONLY, 0666);
  769.         if (compsize != read(fid, blankcomp, compsize))
  770.         Punt("Error reading in MsgLoadComposition!");
  771.         (void) myclose(fid);
  772.         DeleteFileAndCheck(file);
  773.     } else {
  774.          blankcomp = "To: \n--------\n";
  775.          compsize = strlen(blankcomp);
  776.      }
  777.     startPos = index(blankcomp, '\n') - blankcomp;
  778.     }
  779.     fid = myopen(MsgFileName(msg), O_WRONLY | O_TRUNC | O_CREAT, 0666);
  780.     if (compsize != write(fid, blankcomp, compsize))
  781.     Punt("Error writing in MsgLoadComposition!");
  782.     (void) myclose(fid);
  783.     TocSetCacheValid(msg->toc);
  784.     msg->startPos = startPos;
  785. }
  786.  
  787.  
  788.  
  789. /* Load a msg with a template of a reply to frommsg.  Set msg->startPos so
  790.    that the text insertion point will be placed at the beginning of the
  791.    message body. */
  792.  
  793. void MsgLoadReply(msg, frommsg)
  794. Msg msg, frommsg;
  795. {
  796.     char **argv;
  797.     char str[100];
  798.     int status;
  799.  
  800.     TempMoveDraft();
  801.     argv = MakeArgv(5);
  802.     argv[0] = "repl";
  803.     argv[1] = TocMakeFolderName(frommsg->toc);
  804.     (void) sprintf(str, "%d", frommsg->msgid);
  805.     argv[2] = str;
  806.     argv[3] = "-nowhatnowproc";
  807.     argv[4] = "-nodraftfolder";
  808.     status = DoCommand(argv, (char *) NULL, (char *) NULL);
  809.     XtFree(argv[1]);
  810.     XtFree((char*)argv);
  811.     if (!status) {
  812.     RenameAndCheck(draftFile, MsgFileName(msg));
  813.     RestoreDraft();
  814.     TocSetCacheValid(frommsg->toc); /* If -anno is set, this keeps us from
  815.                        rescanning folder. */
  816.     TocSetCacheValid(msg->toc);
  817.     msg->startPos = GetFileLength(MsgFileName(msg));
  818.     }
  819. }
  820.  
  821.  
  822.  
  823. /* Load a msg with a template of forwarding a list of messages.  Set 
  824.    msg->startPos so that the text insertion point will be placed at the end
  825.    of the first line (which is usually a "To:" field). */
  826.  
  827. void MsgLoadForward(scrn, msg, mlist)
  828.   Scrn scrn;
  829.   Msg msg;
  830.   MsgList mlist;
  831. {
  832.     char  **argv, str[100];
  833.     int     i;
  834.     TempMoveDraft();
  835.     argv = MakeArgv(4 + mlist->nummsgs);
  836.     argv[0] = "forw";
  837.     argv[1] = TocMakeFolderName(mlist->msglist[0]->toc);
  838.     for (i = 0; i < mlist->nummsgs; i++) {
  839.         (void) sprintf(str, "%d", mlist->msglist[i]->msgid);
  840.         argv[2 + i] = XtNewString(str);
  841.     }
  842.     argv[2 + i] = "-nowhatnowproc";
  843.     argv[3 + i] = "-nodraftfolder";
  844.     DoCommand(argv, (char *) NULL, (char *) NULL);
  845.     for (i = 1; i < 2 + mlist->nummsgs; i++)
  846.         XtFree((char *) argv[i]);
  847.     XtFree((char *) argv);
  848.     RenameAndCheck(draftFile, MsgFileName(msg));
  849.     RestoreDraft();
  850.     TocSetCacheValid(msg->toc);
  851.     msg->source = CreateFileSource(scrn->viewlabel, MsgFileName(msg), True);
  852.     msg->startPos = XawTextSourceScan(msg->source, (XawTextPosition) 0, 
  853.                       XawstEOL, XawsdRight, 1, False);
  854. }
  855.  
  856.  
  857. /* Load msg with a copy of frommsg. */
  858.  
  859. void MsgLoadCopy(msg, frommsg)
  860. Msg msg, frommsg;
  861. {
  862.     char str[500];
  863.     (void)strcpy(str, MsgFileName(msg));
  864.     CopyFileAndCheck(MsgFileName(frommsg), str);
  865.     TocSetCacheValid(msg->toc);
  866. }
  867.  
  868. /* Checkpoint the given message if it contains unsaved edits. */
  869.  
  870. void MsgCheckPoint(msg)
  871. Msg msg;
  872. {
  873.     int len;
  874.     char file[500];
  875.  
  876.     if (!msg || !msg->source || !IsEditable(msg) ||
  877.     !XawAsciiSourceChanged(msg->source))
  878.     return;
  879.  
  880.     if (*app_resources.checkpoint_name_format == '/') {
  881.     (void) sprintf(file, app_resources.checkpoint_name_format, msg->msgid);
  882.     } else {
  883.     (void) sprintf(file, "%s/", msg->toc->path);
  884.     len = strlen(file);
  885.     (void) sprintf(file + len, app_resources.checkpoint_name_format,
  886.                msg->msgid);
  887.     }
  888.     if (!XawAsciiSaveAsFile(msg->source, file)) {
  889.     char str[256];
  890.     (void) sprintf(str, "Unsaved edits cannot be checkpointed to %s.",
  891.                file);
  892.     PopupError((Widget)NULL, str);
  893.     }
  894.     TocSetCacheValid(msg->toc);
  895. }
  896.  
  897. /* Free the storage being used by the given msg. */
  898.  
  899. void MsgFree(msg)
  900. Msg msg;
  901. {
  902.     XtFree(msg->buf);
  903.     XtFree((char *)msg);
  904. }
  905.  
  906. /* Insert the associated message, if any, filtering it first */
  907.  
  908. /*ARGSUSED*/
  909. void XmhInsert(w, event, params, num_params)
  910.     Widget    w;
  911.     XEvent    *event;
  912.     String    *params;
  913.     Cardinal    *num_params;
  914. {
  915.     Scrn scrn = ScrnFromWidget(w);
  916.     Msg msg = scrn->msg;
  917.     XawTextPosition pos;
  918.     XawTextBlock block;
  919.  
  920.     if (msg == NULL || scrn->assocmsg == NULL) return;
  921.  
  922.     if (app_resources.insert_filter && *app_resources.insert_filter) {
  923.     char command[1024];
  924.     char *argv[4];
  925.     argv[0] = "/bin/sh";
  926.     argv[1] = "-c";
  927.     sprintf(command, "%s %s", app_resources.insert_filter,
  928.         MsgFileName(scrn->assocmsg));
  929.     argv[2] = command;
  930.     argv[3] = 0;
  931.     block.ptr = DoCommandToString(argv);
  932.         block.length = strlen(block.ptr);
  933.     }
  934.     else {
  935.     /* default filter is equivalent to 'echo "<filename>"' */
  936.     block.ptr = XtNewString(MsgFileName(scrn->assocmsg));
  937.     block.length = strlen(block.ptr);
  938.     }
  939.     block.firstPos = 0;
  940.     block.format = FMT8BIT;
  941.     pos = XawTextGetInsertionPoint(scrn->viewwidget);
  942.     if (XawTextReplace(scrn->viewwidget, pos, pos, &block) != XawEditDone)
  943.     PopupError(scrn->parent, "Insertion failed!");
  944.     XtFree(block.ptr);
  945. }
  946.  
  947. /*    Function Name: CreateFileSource
  948.  *    Description: Creates an AsciiSource for a file. 
  949.  *    Arguments: w - the widget to create the source for.
  950.  *                 filename - the file to assign to this source.
  951.  *                 edit - if TRUE then this disk source is editable.
  952.  *    Returns: the source.
  953.  */
  954.  
  955. Widget
  956. CreateFileSource(w, filename, edit)
  957. Widget w;
  958. String filename;
  959. Boolean edit;
  960. {
  961.   Arg arglist[10];
  962.   Cardinal num_args = 0;
  963.  
  964.   XtSetArg(arglist[num_args], XtNtype, XawAsciiFile);  num_args++;
  965.   XtSetArg(arglist[num_args], XtNstring, filename);    num_args++;
  966.   if (edit) 
  967.       XtSetArg(arglist[num_args], XtNeditType, XawtextEdit);
  968.   else
  969.       XtSetArg(arglist[num_args], XtNeditType, XawtextRead);
  970.   num_args++;
  971.  
  972.   return(XtCreateWidget("textSource", asciiSrcObjectClass, w, 
  973.             arglist, num_args));
  974. }
  975.  
  976.