home *** CD-ROM | disk | FTP | other *** search
/ ftp.wwiv.com / ftp.wwiv.com.zip / ftp.wwiv.com / pub / MISC / SQDEV200.ZIP / SRC / SQ_MSG.C < prev    next >
C/C++ Source or Header  |  1994-05-23  |  18KB  |  688 lines

  1. /***************************************************************************
  2.  *                                                                         *
  3.  *  Squish Developers Kit Source, Version 2.00                             *
  4.  *  Copyright 1989-1994 by SCI Communications.  All rights reserved.       *
  5.  *                                                                         *
  6.  *  USE OF THIS FILE IS SUBJECT TO THE RESTRICTIONS CONTAINED IN THE       *
  7.  *  SQUISH DEVELOPERS KIT LICENSING AGREEMENT IN SQDEV.PRN.  IF YOU DO NOT *
  8.  *  FIND THE TEXT OF THIS AGREEMENT IN THE AFOREMENTIONED FILE, OR IF YOU  *
  9.  *  DO NOT HAVE THIS FILE, YOU SHOULD IMMEDIATELY CONTACT THE AUTHOR AT    *
  10.  *  ONE OF THE ADDRESSES LISTED BELOW.  IN NO EVENT SHOULD YOU PROCEED TO  *
  11.  *  USE THIS FILE WITHOUT HAVING ACCEPTED THE TERMS OF THE SQUISH          *
  12.  *  DEVELOPERS KIT LICENSING AGREEMENT, OR SUCH OTHER AGREEMENT AS YOU ARE *
  13.  *  ABLE TO REACH WITH THE AUTHOR.                                         *
  14.  *                                                                         *
  15.  *  You can contact the author at one of the address listed below:         *
  16.  *                                                                         *
  17.  *  Scott Dudley       FidoNet     1:249/106                               *
  18.  *  777 Downing St.    Internet    sjd@f106.n249.z1.fidonet.org            *
  19.  *  Kingston, Ont.     CompuServe  >INTERNET:sjd@f106.n249.z1.fidonet.org  *
  20.  *  Canada  K7M 5N3    BBS         1-613-634-3058, V.32bis                 *
  21.  *                                                                         *
  22.  ***************************************************************************/
  23.  
  24. #pragma off(unreferenced)
  25. static char rcs_id[]="$Id: sq_msg.c 1.6 1993/12/30 00:49:27 sjd Exp sjd $";
  26. #pragma on(unreferenced)
  27.  
  28. #define MSGAPI_HANDLERS
  29. #define MSGAPI_NO_OLD_TYPES
  30.  
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <io.h>
  34. #include <fcntl.h>
  35. #include <assert.h>
  36. #include "prog.h"
  37. #include "msgapi.h"
  38. #include "api_sq.h"
  39. #include "apidebug.h"
  40.  
  41.  
  42. /* Allocate a new message handle */
  43.  
  44. static HMSG near NewHmsg(HAREA ha, word wMode)
  45. {
  46.   HMSG hmsg;
  47.  
  48.   /* Allocate memory for the message handle */
  49.  
  50.   if ((hmsg=palloc(sizeof *hmsg))==NULL)
  51.     return NULL;
  52.  
  53.   (void)memset(hmsg, 0, sizeof *hmsg);
  54.  
  55.   /* Initialize the handle to the standard defaults */
  56.  
  57.   hmsg->ha=ha;
  58.   hmsg->id=MSGH_ID;
  59.   hmsg->bytes_written=0;
  60.   hmsg->cur_pos=0;
  61.   hmsg->foRead=NULL_FRAME;
  62.   hmsg->foWrite=NULL_FRAME;
  63.   hmsg->wMode=wMode;
  64.   hmsg->fDiskErr=FALSE;
  65.   hmsg->dwMsg=0L;
  66.   hmsg->uidUs=(UMSGID)0L;
  67.   hmsg->hmsgNext=NULL;
  68.  
  69.   return hmsg;
  70. }
  71.  
  72.  
  73.  
  74.  
  75.  
  76. /* Returns TRUE if this is a valid message which can be read */
  77.  
  78. static unsigned near _SquishHeaderValidRead(HAREA ha, SQHDR *psqh)
  79. {
  80.   if (psqh->next_frame > Sqd->foEnd ||
  81.       psqh->prev_frame > Sqd->foEnd)
  82.   {
  83.     msgapierr=MERR_BADF;
  84.     return FALSE;
  85.   }
  86.  
  87.   /* Now make sure that it's okay to read from this frame type */
  88.  
  89.   if (psqh->frame_type==FRAME_NORMAL)
  90.     return TRUE;
  91.   else if (psqh->frame_type==FRAME_FREE)
  92.   {
  93.     msgapierr=MERR_BADMSG;
  94.     return FALSE;
  95.   }
  96.   else if (psqh->frame_type==FRAME_UPDATE)
  97.   {
  98.     msgapierr=MERR_SHARE;
  99.     return FALSE;
  100.   }
  101.   else
  102.   {
  103.     msgapierr=MERR_BADF;
  104.     return FALSE;
  105.   }
  106. }
  107.  
  108.  
  109.  
  110. /* Translate an absolute message number into a relative one */
  111.  
  112. static dword near _SquishTranslateNum(HAREA ha, dword dwMsg)
  113. {
  114.   if (dwMsg==MSGNUM_CUR)
  115.     return ha->cur_msg;
  116.   else if (dwMsg==MSGNUM_PREV)
  117.     return ha->cur_msg-1;
  118.   else if (dwMsg==MSGNUM_NEXT)
  119.     return ha->cur_msg+1;
  120.   else return dwMsg;
  121. }
  122.  
  123.  
  124. /* Set the HAREA struct to show this as the current message being read */
  125.  
  126. static unsigned near _SquishSetCurFrameRead(HMSG hmsg, dword dwMsg, FOFS foCur, SQHDR *psqh)
  127. {
  128.   hmsg->ha->cur_msg=dwMsg;
  129.   HSqd->foCur=foCur;
  130.   HSqd->foPrev=psqh->prev_frame;
  131.   HSqd->foNext=psqh->next_frame;
  132.  
  133.   hmsg->foRead=foCur;
  134.   hmsg->sqhRead=*psqh;
  135.  
  136.   return TRUE;
  137. }
  138.  
  139.  
  140.  
  141. /* Open an existing message */
  142.  
  143. static unsigned near _SquishOpenMsgExisting(HMSG hmsg, dword dwMsg)
  144. {
  145.   SQHDR sqh;
  146.   FOFS foMsg;
  147.  
  148.   /* If the message number is invalid, return an appropriate error */
  149.  
  150.   if (dwMsg==0 || dwMsg > hmsg->ha->num_msg)
  151.   {
  152.     /* If the user tries to go to message zero, reset pointers appropriately */
  153.  
  154.     if (dwMsg==0)
  155.     {
  156.       HSqd->foPrev=NULL_FRAME;
  157.       HSqd->foCur=NULL_FRAME;
  158.       HSqd->foNext=HSqd->foFirst;
  159.       hmsg->ha->cur_msg=0;
  160.     }
  161.  
  162.     msgapierr=MERR_NOENT;
  163.     return FALSE;
  164.   }
  165.  
  166.   /* Store the message number in the message handle */
  167.  
  168.   hmsg->dwMsg=dwMsg;
  169.  
  170.   /* Get the frame offset for this message */
  171.  
  172.   if ((foMsg=_SquishGetFrameOfs(hmsg->ha, dwMsg))==NULL_FRAME)
  173.     return FALSE;
  174.  
  175.   /* Read the frame header for this message and make sure that it's okay    *
  176.    * to read from this message.                                             */
  177.  
  178.   if (!_SquishReadHdr(hmsg->ha, foMsg, &sqh) ||
  179.       !_SquishHeaderValidRead(hmsg->ha, &sqh))
  180.   {
  181.     return FALSE;
  182.   }
  183.  
  184.   /* Adjust our area header to show this as the current message */
  185.  
  186.   return _SquishSetCurFrameRead(hmsg, dwMsg, foMsg, &sqh);
  187. }
  188.  
  189.  
  190.  
  191. /* We are creating a new message on top of an old one.  We have not         *
  192.  * written anything yet, but we need to indicate to other tasks that this   *
  193.  * mesage is in the process of being written.  To do this:                  *
  194.  *                                                                          *
  195.  * 1) Invalidate the index record for this message.                         *
  196.  *                                                                          *
  197.  * 2) Set a flag in the SQHDR indicating that the message is currently      *
  198.  *    being processed.                                                      *
  199.  *                                                                          *
  200.  * 3) Read in the SQHDR for the frame being killed, so that SquishWriteMsg  *
  201.  *    can use it at a later point in time.                                  *
  202.  *                                                                          *
  203.  * This function assumes that we have exclusive access to the base.         */
  204.  
  205. static unsigned near _SquishBlankOldMsg(HMSG hmsg, dword dwMsg)
  206. {
  207.   SQIDX sqi;
  208.  
  209.   assert(HSqd->fHaveExclusive);
  210.  
  211.   if (! SidxGet(HSqd->hix, dwMsg, &sqi))
  212.     return FALSE;
  213.  
  214.  
  215.   /* Make sure that this frame contains a valid message */
  216.  
  217.   if (sqi.ofs==NULL_FRAME)
  218.   {
  219.     msgapierr=MERR_BADF;
  220.     return FALSE;
  221.   }
  222.  
  223.  
  224.   /* Save the location of the SQHDR */
  225.  
  226.   hmsg->foRead=sqi.ofs;
  227.  
  228.   /* Now try to read in the message header */
  229.  
  230.   if (! _SquishReadHdr(hmsg->ha, hmsg->foRead, &hmsg->sqhRead))
  231.     return FALSE;
  232.  
  233.  
  234.   /* Two tasks cannot update the message at the same time! */
  235.  
  236.   if (hmsg->sqhRead.frame_type==FRAME_UPDATE)
  237.   {
  238.     msgapierr=MERR_SHARE;
  239.     return FALSE;
  240.   }
  241.  
  242.  
  243.   /* Invalidate this index, but leave the UMSGID alone */
  244.  
  245.   sqi.ofs=NULL_FRAME;
  246.   sqi.hash=(dword)-1L;
  247.  
  248.   /* Save a copy of the UMSGID so we know which message we are updating */
  249.  
  250.   hmsg->uidUs=sqi.umsgid;
  251.  
  252.   /* Write the record back to disk */
  253.  
  254.   if (! SidxPut(HSqd->hix, dwMsg, &sqi))
  255.     return FALSE;
  256.  
  257.  
  258.   hmsg->sqhRead.frame_type=FRAME_UPDATE;
  259.  
  260.   if (! _SquishWriteHdr(hmsg->ha, hmsg->foRead, &hmsg->sqhRead))
  261.     return FALSE;
  262.  
  263.   return TRUE;
  264. }
  265.  
  266.  
  267.  
  268. /* We are creating a new message and appending it to the end of the         *
  269.  * Squish base.  In this case, we only need to initialize the entry in      *
  270.  * the Squish index file.                                                   *
  271.  *                                                                          *
  272.  * This function assumes that we have exclusive access to the base.         */
  273.  
  274. static unsigned near _SquishBlankNewMsg(HMSG hmsg)
  275. {
  276.   SQIDX sqi;
  277.  
  278.   assert(HSqd->fHaveExclusive);
  279.  
  280.   sqi.ofs=NULL_FRAME;
  281.   sqi.hash=(dword)-1L;
  282.   sqi.umsgid=HSqd->uidNext++;
  283.   hmsg->uidUs=sqi.umsgid;
  284.  
  285.   return (unsigned)SidxPut(HSqd->hix, hmsg->dwMsg, &sqi);
  286. }
  287.  
  288.  
  289. /* This function removes as many messages as necessary (and places them     *
  290.  * on the free chain) such that we have no more than maxmsgs messages.      */
  291.  
  292. static unsigned near _SquishReduceMaxInternal(HAREA ha,
  293.                                               dword *pdwDeleted,
  294.                                               FOFS *pfoFirst,
  295.                                               FOFS *pfoFirstPrior)
  296. {
  297.   SQIDX sqi;
  298.   SQHDR sqh;
  299.  
  300.   /* Delete messages while we can... */
  301.  
  302.   while (ha->num_msg >= Sqd->dwMaxMsg)
  303.   {
  304.     ha->num_msg--;
  305.     ha->high_msg--;
  306.  
  307.     if (! SidxGet(Sqd->hix, Sqd->wSkipMsg+1, &sqi) ||
  308.         ! _SquishReadHdr(ha, sqi.ofs, &sqh) ||
  309.         ! _SquishInsertFreeChain(ha, sqi.ofs, &sqh))
  310.     {
  311.       return FALSE;
  312.     }
  313.  
  314.     /* Record the new starting point for the beginning of the base */
  315.  
  316.     *pfoFirst=sqh.next_frame;
  317.  
  318.     if (*pfoFirstPrior==NULL_FRAME)
  319.       *pfoFirstPrior=sqh.prev_frame;
  320.  
  321.     /* Delete this message from the index */
  322.  
  323.     if (!_SquishRemoveIndexEntry(Sqd->hix, Sqd->wSkipMsg+1L, NULL, &sqh, FALSE))
  324.       return FALSE;
  325.  
  326.     (*pdwDeleted)++;
  327.   }
  328.  
  329.   return TRUE;
  330. }
  331.  
  332.  
  333.  
  334.  
  335. /* This function adjusts our pointers after a massive deletion by           *
  336.  * _SquishReduceMaxInternal.                                                */
  337.  
  338. static unsigned near _SquishReduceMaxPointers(HAREA ha, FOFS foFirst,
  339.                                               dword dwDeleted,
  340.                                               FOFS foFirstPrior)
  341. {
  342.   unsigned rc=TRUE;
  343.  
  344.   /* Now adjust the 'first message' pointer.  If we have no skip_msgs,      *
  345.    * just set the pointer for the beginning of the area.  If we do have     *
  346.    * skip_msgs, we will have to update the 'next' pointer of the highest    *
  347.    * skip_msg to pointer over the messages that we just deleted.            */
  348.  
  349.   if (Sqd->wSkipMsg==0)
  350.   {
  351.     Sqd->foFirst=foFirst;
  352.     foFirstPrior=NULL_FRAME;
  353.   }
  354.   else
  355.   {
  356.     /* Link up the skipmsgs message and the one after it */
  357.  
  358.     if (! _SquishSetFrameNext(ha, foFirstPrior, foFirst))
  359.       rc=FALSE;
  360.   }
  361.  
  362.   if (! _SquishSetFramePrev(ha, foFirst, foFirstPrior))
  363.     rc=FALSE;
  364.  
  365.   /* If we were just outside the deleted range, adjust appropriately */
  366.  
  367.   if (ha->cur_msg==(dword)Sqd->wSkipMsg+dwDeleted+1)
  368.   {
  369.     /* Adjust our 'previous' pointer, if we come just after the deleted     *
  370.      * range.                                                               */
  371.  
  372.     if (Sqd->wSkipMsg==0)
  373.       Sqd->foPrev=NULL_FRAME;
  374.     else Sqd->foPrev=_SquishGetFrameOfs(ha, (dword)Sqd->wSkipMsg);
  375.   }
  376.   else if (ha->cur_msg==(dword)Sqd->wSkipMsg)
  377.   {
  378.     /* If we come just before the deleted range, adjust our 'next' ptr */
  379.  
  380.     Sqd->foNext=foFirst;
  381.   }
  382.  
  383.  
  384.   /* If the current message number was greater than skip_msg, we will       *
  385.    * need to adjust our pointers.                                           */
  386.  
  387.   if (ha->cur_msg > (dword)Sqd->wSkipMsg)
  388.   {
  389.     if (ha->cur_msg <= (dword)Sqd->wSkipMsg+dwDeleted)
  390.     {
  391.       SQHDR sqh;
  392.       FOFS fo;
  393.  
  394.       /* We were inside the range of messages that was deleted.  To handle  *
  395.        * this, simply set our pointer to the first message outside of that  *
  396.        * range.                                                             */
  397.  
  398.       if (Sqd->wSkipMsg &&
  399.           (fo=_SquishGetFrameOfs(ha, (dword)Sqd->wSkipMsg)) != NULL_FRAME &&
  400.           _SquishReadHdr(ha, fo, &sqh))
  401.       {
  402.         Sqd->foCur=fo;
  403.         Sqd->foPrev=sqh.prev_frame;
  404.         Sqd->foNext=sqh.next_frame;
  405.         ha->cur_msg=Sqd->wSkipMsg;
  406.       }
  407.       else
  408.       {
  409.         Sqd->foNext=Sqd->foFirst;
  410.         Sqd->foCur=NULL_FRAME;
  411.         Sqd->foPrev=NULL_FRAME;
  412.         ha->cur_msg=0;
  413.       }
  414.     }
  415.     else
  416.     {
  417.       /* We were above the deleted range, so we will have to decrement our  *
  418.        * message number.                                                    */
  419.  
  420.       ha->cur_msg -= dwDeleted;
  421.     }
  422.   }
  423.  
  424.   /* Adjust the HWM, if necessary */
  425.  
  426.   if (ha->high_water >= Sqd->wSkipMsg)
  427.   {
  428.     long lNewHWM = (long)ha->high_water - (long)dwDeleted;
  429.  
  430.     if (lNewHWM < (long)Sqd->wSkipMsg)
  431.       ha->high_water = Sqd->wSkipMsg;
  432.     else ha->high_water=(dword)lNewHWM;
  433.   }
  434.  
  435.   return rc;
  436. }
  437.  
  438.  
  439.  
  440.  
  441. /* Delete enough messages in this area so that we fall below the            *
  442.  * dwMaxMsg limit.                                                          *
  443.  *                                                                          *
  444.  * This function assumes that we have exclusive access to the Squish base.  */
  445.  
  446. static unsigned near _SquishReduceMaxMsgs(HAREA ha)
  447. {
  448.   FOFS foFirstPrior=NULL_FRAME;
  449.   FOFS foFirst=NULL_FRAME;
  450.   dword dwDeleted=0;
  451.   unsigned rc=TRUE;
  452.  
  453.   assert(Sqd->fHaveExclusive);
  454.  
  455.  
  456.   /* If we don't have too many messages, just return */
  457.  
  458.   if (!Sqd->dwMaxMsg ||
  459.       ha->num_msg < Sqd->dwMaxMsg ||
  460.       ha->num_msg <= (dword)Sqd->wSkipMsg)
  461.   {
  462.     return TRUE;
  463.   }
  464.  
  465.  
  466.   /* Read the index into memory */
  467.  
  468.   if (! _SquishBeginBuffer(Sqd->hix))
  469.     return FALSE;
  470.  
  471.   /* Move all of the messages to the free list */
  472.  
  473.   if (!_SquishReduceMaxInternal(ha, &dwDeleted, &foFirst, &foFirstPrior))
  474.     rc=FALSE;
  475.  
  476.   /* Make sure that our pointers are okay */
  477.  
  478.   if (!_SquishReduceMaxPointers(ha, foFirst, dwDeleted, foFirstPrior))
  479.     rc=FALSE;
  480.  
  481.   /* Write the index back */
  482.  
  483.   if (!_SquishEndBuffer(Sqd->hix))
  484.     rc=FALSE;
  485.  
  486.   return rc;
  487. }
  488.  
  489.  
  490.  
  491. /* Create a new Squish message, possibly overwriting an old one             *
  492.  * (if dwMsg==0).                                                           */
  493.  
  494. static unsigned near _SquishOpenMsgCreate(HMSG hmsg, dword dwMsg)
  495. {
  496.   unsigned rc=TRUE;
  497.  
  498.   /* If we are creating a completely-new message, we need to adjust the     *
  499.    * header and index file IMMEDIATELY to show that we want this            *
  500.    * new message number.                                                    */
  501.  
  502.   if (! _SquishExclusiveBegin(hmsg->ha))
  503.     return FALSE;
  504.  
  505.   /* If we are creating a new message, make sure that dwMsg is zero! */
  506.  
  507.   if (dwMsg > hmsg->ha->num_msg)
  508.     dwMsg=0;
  509.  
  510.   /* Make sure that we don't overrun the max_msgs limit! */
  511.  
  512.   if (dwMsg==0)
  513.     rc=_SquishReduceMaxMsgs(hmsg->ha);
  514.  
  515.   /* Set our message number */
  516.  
  517.   hmsg->dwMsg=dwMsg ? dwMsg : hmsg->ha->num_msg+1;
  518.  
  519.   /* Now fix up the index and data files to indicate that message creation  *
  520.    * is in process.                                                         */
  521.  
  522.   if (rc)
  523.   {
  524.     if (dwMsg)
  525.       rc=_SquishBlankOldMsg(hmsg, dwMsg);
  526.     else rc=_SquishBlankNewMsg(hmsg);
  527.   }
  528.  
  529.   /* If we are creating a message, increment the total number of messages */
  530.  
  531.   if (rc && !dwMsg)
  532.   {
  533.     hmsg->ha->num_msg++;
  534.     hmsg->ha->high_msg++;
  535.   }
  536.  
  537.   /* End exclusive access */
  538.  
  539.   if (! _SquishExclusiveEnd(hmsg->ha))
  540.     rc=FALSE;
  541.  
  542.   return rc;
  543. }
  544.  
  545.  
  546.  
  547.  
  548. /* Open a Squish message */
  549.  
  550. HMSG MAPIENTRY SquishOpenMsg(HAREA ha, word wMode, dword dwMsg)
  551. {
  552.   HMSG hmsg;
  553.   unsigned fOpened=FALSE;
  554.  
  555.   if (MsgInvalidHarea(ha))
  556.     return NULL;
  557.  
  558.   /* Allocate a handle for this message */
  559.  
  560.   if ((hmsg=NewHmsg(ha, wMode))==NULL)
  561.     return NULL;
  562.  
  563.   /* Translate dwMsg into a real message number, if necessary */
  564.  
  565.   dwMsg=_SquishTranslateNum(hmsg->ha, dwMsg);
  566.  
  567.   /* Create a new message, or open an existing message, as specified */
  568.  
  569.   if (wMode==MOPEN_CREATE)
  570.     fOpened=_SquishOpenMsgCreate(hmsg, dwMsg);
  571.   else
  572.     fOpened=_SquishOpenMsgExisting(hmsg, dwMsg);
  573.  
  574.   /* If the open succeeded, add this to the list of open msgs for this area */
  575.  
  576.   if (fOpened)
  577.   {
  578.     hmsg->hmsgNext=Sqd->hmsgOpen;
  579.     Sqd->hmsgOpen=hmsg;
  580.   }
  581.   else
  582.   {
  583.     /* Otherwise, free memory and get out */
  584.  
  585.     pfree(hmsg);
  586.     hmsg=NULL;
  587.   }
  588.  
  589.   return hmsg;
  590. }
  591.  
  592. /* This function undoes what SquishOpenMsg did when creating a new          *
  593.  * message.  This is only called if the write was not completed.            */
  594.  
  595. static unsigned near _SquishCloseUndoWrite(HMSG hmsg)
  596. {
  597.   if (! _SquishExclusiveBegin(hmsg->ha))
  598.     return FALSE;
  599.  
  600.   /* Check again, just in case something happened on another node */
  601.  
  602.   if (hmsg->dwMsg==hmsg->ha->num_msg)
  603.   {
  604.     hmsg->ha->num_msg--;
  605.     hmsg->ha->high_msg--;
  606.   }
  607.  
  608.   if (! _SquishExclusiveEnd(hmsg->ha))
  609.     return FALSE;
  610.  
  611.   return TRUE;
  612. }
  613.  
  614.  
  615. /* Remove the message 'hmsg' from the linked list of open messages */
  616.  
  617. static unsigned near _SquishCloseRemoveList(HMSG hmsg)
  618. {
  619.   HMSG hm=HSqd->hmsgOpen;
  620.  
  621.   if (!hm)
  622.   {
  623.     msgapierr=MERR_BADA;
  624.     return FALSE;
  625.   }
  626.  
  627.   /* If our message was at the head of the list, just adjust main ptr */
  628.  
  629.   if (HSqd->hmsgOpen==hmsg)
  630.   {
  631.     HSqd->hmsgOpen=hmsg->hmsgNext;
  632.     return TRUE;
  633.   }
  634.  
  635.   /* Otherwise, try to find this message in the linked list of msgs */
  636.  
  637.   while (hm)
  638.   {
  639.     /* If we found us, just skip the list over to the next msg */
  640.  
  641.     if (hm->hmsgNext==hmsg)
  642.     {
  643.       hm->hmsgNext=hmsg->hmsgNext;
  644.       return TRUE;
  645.     }
  646.  
  647.     hm=hm->hmsgNext;
  648.   }
  649.  
  650.   msgapierr=MERR_BADA;
  651.   return FALSE;
  652. }
  653.  
  654.  
  655.  
  656. /* Close an open message handle */
  657.  
  658. sword MAPIENTRY SquishCloseMsg(HMSG hmsg)
  659. {
  660.   if (MsgInvalidHmsg(hmsg))
  661.     return -1;
  662.  
  663.  
  664.   /* If we allocated a new number for this message, but we did not use it... */
  665.  
  666.   if (hmsg->wMode==MOPEN_CREATE && !hmsg->fWritten &&
  667.       hmsg->dwMsg==hmsg->ha->num_msg)
  668.   {
  669.     if (!_SquishCloseUndoWrite(hmsg))
  670.       return -1;
  671.   }
  672.  
  673.   /* Remove this msg from the list of open msgs for this area */
  674.  
  675.   (void)_SquishCloseRemoveList(hmsg);
  676.  
  677.   /* Reset the ID so that our functions will not accept the freed hmsg */
  678.  
  679.   hmsg->id=0L;
  680.  
  681.   /* Deallocate memory and return to caller */
  682.  
  683.   pfree(hmsg);
  684.   return 0;
  685. }
  686.  
  687.  
  688.