/***************************************************************************
* *
* Squish Developers Kit Source, Version 2.00 *
* Copyright 1989-1994 by SCI Communications. All rights reserved. *
* *
* USE OF THIS FILE IS SUBJECT TO THE RESTRICTIONS CONTAINED IN THE *
* SQUISH DEVELOPERS KIT LICENSING AGREEMENT IN SQDEV.PRN. IF YOU DO NOT *
* FIND THE TEXT OF THIS AGREEMENT IN THE AFOREMENTIONED FILE, OR IF YOU *
* DO NOT HAVE THIS FILE, YOU SHOULD IMMEDIATELY CONTACT THE AUTHOR AT *
* ONE OF THE ADDRESSES LISTED BELOW. IN NO EVENT SHOULD YOU PROCEED TO *
* USE THIS FILE WITHOUT HAVING ACCEPTED THE TERMS OF THE SQUISH *
* DEVELOPERS KIT LICENSING AGREEMENT, OR SUCH OTHER AGREEMENT AS YOU ARE *
* ABLE TO REACH WITH THE AUTHOR. *
* *
* You can contact the author at one of the address listed below: *
* *
* Scott Dudley FidoNet 1:249/106 *
* 777 Downing St. Internet sjd@f106.n249.z1.fidonet.org *
* Kingston, Ont. CompuServe >INTERNET:sjd@f106.n249.z1.fidonet.org *
* Canada K7M 5N3 BBS 1-613-634-3058, V.32bis *
* *
***************************************************************************/
/*
#pragma off(unreferenced)
static char rcs_id[]="$Id: sq_msg.c,v 1.9 2003/01/15 05:40:38 stas_degteff Exp $";
#pragma on(unreferenced)
*/
#define MSGAPI_HANDLERS
#define MSGAPI_NO_OLD_TYPES
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "compiler.h"
#ifdef HAS_IO_H
# include <io.h>
#endif
#ifdef HAS_SHARE_H
#include <share.h>
#endif
#ifdef HAS_MALLOC_H
#include <malloc.h>
#endif
#include "prog.h"
#include "old_msg.h"
#include "msgapi.h"
#include "api_sq.h"
#include "api_sqp.h"
#include "apidebug.h"
#include "unused.h"
/* Allocate a new message handle */
static HMSG near NewHmsg(HAREA ha, word wMode)
{
HMSG hmsg;
/* Allocate memory for the message handle */
if ((hmsg=palloc(sizeof *hmsg))==NULL)
return NULL;
(void)memset(hmsg, 0, sizeof *hmsg);
/* Initialize the handle to the standard defaults */
hmsg->ha=ha;
hmsg->id=MSGH_ID;
hmsg->bytes_written=0;
hmsg->cur_pos=0;
hmsg->foRead=NULL_FRAME;
hmsg->foWrite=NULL_FRAME;
hmsg->wMode=wMode;
hmsg->fDiskErr=FALSE;
hmsg->dwMsg=0L;
hmsg->uidUs=(UMSGID)0L;
hmsg->hmsgNext=NULL;
return hmsg;
}
/* Returns TRUE if this is a valid message which can be read */
static unsigned near _SquishHeaderValidRead(HAREA ha, SQHDR *psqh)
{
if (psqh->next_frame > Sqd->foEnd ||
psqh->prev_frame > Sqd->foEnd)
{
msgapierr=MERR_BADF;
return FALSE;
}
/* Now make sure that it's okay to read from this frame type */
if (psqh->frame_type==FRAME_NORMAL)
return TRUE;
else if (psqh->frame_type==FRAME_FREE)
{
msgapierr=MERR_BADMSG;
return FALSE;
}
else if (psqh->frame_type==FRAME_UPDATE)
{
msgapierr=MERR_SHARE;
return FALSE;
}
else
{
msgapierr=MERR_BADF;
return FALSE;
}
}
/* Translate an absolute message number into a relative one */
static dword near _SquishTranslateNum(HAREA ha, dword dwMsg)
{
if (dwMsg==MSGNUM_CUR)
return ha->cur_msg;
else if (dwMsg==MSGNUM_PREV)
return ha->cur_msg-1;
else if (dwMsg==MSGNUM_NEXT)
return ha->cur_msg+1;
else return dwMsg;
}
/* Set the HAREA struct to show this as the current message being read */
static unsigned near _SquishSetCurFrameRead(HMSG hmsg, dword dwMsg, FOFS foCur, SQHDR *psqh)
{
hmsg->ha->cur_msg=dwMsg;
HSqd->foCur=foCur;
HSqd->foPrev=psqh->prev_frame;
HSqd->foNext=psqh->next_frame;
hmsg->foRead=foCur;
hmsg->sqhRead=*psqh;
return TRUE;
}
/* Open an existing message */
static unsigned near _SquishOpenMsgExisting(HMSG hmsg, dword dwMsg)
{
SQHDR sqh;
FOFS foMsg;
/* If the message number is invalid, return an appropriate error */
if (dwMsg==0 || dwMsg > hmsg->ha->num_msg)
{
/* If the user tries to go to message zero, reset pointers appropriately */
if (dwMsg==0)
{
HSqd->foPrev=NULL_FRAME;
HSqd->foCur=NULL_FRAME;
HSqd->foNext=HSqd->foFirst;
hmsg->ha->cur_msg=0;
}
msgapierr=MERR_NOENT;
return FALSE;
}
/* Store the message number in the message handle */
hmsg->dwMsg=dwMsg;
/* Get the frame offset for this message */
if ((foMsg=_SquishGetFrameOfs(hmsg->ha, dwMsg))==NULL_FRAME)
return FALSE;
/* Read the frame header for this message and make sure that it's okay *
* to read from this message. */
if (!_SquishReadHdr(hmsg->ha, foMsg, &sqh) ||
!_SquishHeaderValidRead(hmsg->ha, &sqh))
{
return FALSE;
}
/* Adjust our area header to show this as the current message */
return _SquishSetCurFrameRead(hmsg, dwMsg, foMsg, &sqh);
}
/* We are creating a new message on top of an old one. We have not *
* written anything yet, but we need to indicate to other tasks that this *
* mesage is in the process of being written. To do this: *
* *
* 1) Invalidate the index record for this message. *
* *
* 2) Set a flag in the SQHDR indicating that the message is currently *
* being processed. *
* *
* 3) Read in the SQHDR for the frame being killed, so that SquishWriteMsg *
* can use it at a later point in time. *
* *
* This function assumes that we have exclusive access to the base. */
static unsigned near _SquishBlankOldMsg(HMSG hmsg, dword dwMsg)
{
SQIDX sqi;
assert(HSqd->fHaveExclusive);
if (! SidxGet(HSqd->hix, dwMsg, &sqi))
return FALSE;
/* Make sure that this frame contains a valid message */
if (sqi.ofs==NULL_FRAME)
{
msgapierr=MERR_BADF;
return FALSE;
}
/* Save the location of the SQHDR */
hmsg->foRead=sqi.ofs;
/* Now try to read in the message header */
if (! _SquishReadHdr(hmsg->ha, hmsg->foRead, &hmsg->sqhRead))
return FALSE;
/* Two tasks cannot update the message at the same time! */
if (hmsg->sqhRead.frame_type==FRAME_UPDATE)
{
msgapierr=MERR_SHARE;
return FALSE;
}
/* Invalidate this index, but leave the UMSGID alone */
sqi.ofs=NULL_FRAME;
sqi.hash=(dword)-1L;
/* Save a copy of the UMSGID so we know which message we are updating */
hmsg->uidUs=sqi.umsgid;
/* Write the record back to disk */
if (! SidxPut(HSqd->hix, dwMsg, &sqi))
return FALSE;
hmsg->sqhRead.frame_type=FRAME_UPDATE;
if (! _SquishWriteHdr(hmsg->ha, hmsg->foRead, &hmsg->sqhRead))
return FALSE;
return TRUE;
}
/* We are creating a new message and appending it to the end of the *
* Squish base. In this case, we only need to initialize the entry in *
* the Squish index file. *
* *
* This function assumes that we have exclusive access to the base. */
static unsigned near _SquishBlankNewMsg(HMSG hmsg)
{
SQIDX sqi;
assert(HSqd->fHaveExclusive);
sqi.ofs=NULL_FRAME;
sqi.hash=(dword)-1L;
sqi.umsgid=HSqd->uidNext++;
hmsg->uidUs=sqi.umsgid;
return (unsigned)SidxPut(HSqd->hix, hmsg->dwMsg, &sqi);
}
/* This function removes as many messages as necessary (and places them *
* on the free chain) such that we have no more than maxmsgs messages. */
static unsigned near _SquishReduceMaxInternal(HAREA ha,
dword *pdwDeleted,
FOFS *pfoFirst,
FOFS *pfoFirstPrior)
{
SQIDX sqi;
SQHDR sqh;
/* Delete messages while we can... */
while (ha->num_msg >= Sqd->dwMaxMsg)
{
ha->num_msg--;
ha->high_msg--;
if (! SidxGet(Sqd->hix, Sqd->wSkipMsg+1, &sqi) ||
! _SquishReadHdr(ha, sqi.ofs, &sqh) ||
! _SquishInsertFreeChain(ha, sqi.ofs, &sqh))
{
return FALSE;
}
/* Record the new starting point for the beginning of the base */
*pfoFirst=sqh.next_frame;
if (*pfoFirstPrior==NULL_FRAME)
*pfoFirstPrior=sqh.prev_frame;
/* Delete this message from the index */
if (!_SquishRemoveIndexEntry(Sqd->hix, Sqd->wSkipMsg+1L, NULL, &sqh, FALSE))
return FALSE;
(*pdwDeleted)++;
}
return TRUE;
}
/* This function adjusts our pointers after a massive deletion by *
* _SquishReduceMaxInternal. */
static unsigned near _SquishReduceMaxPointers(HAREA ha, FOFS foFirst,
dword dwDeleted,
FOFS foFirstPrior)
{
unsigned rc=TRUE;
/* Now adjust the 'first message' pointer. If we have no skip_msgs, *
* just set the pointer for the beginning of the area. If we do have *
* skip_msgs, we will have to update the 'next' pointer of the highest *
* skip_msg to pointer over the messages that we just deleted. */
if (Sqd->wSkipMsg==0)
{
Sqd->foFirst=foFirst;
foFirstPrior=NULL_FRAME;
}
else
{
/* Link up the skipmsgs message and the one after it */
if (! _SquishSetFrameNext(ha, foFirstPrior, foFirst))
rc=FALSE;
}
if (! _SquishSetFramePrev(ha, foFirst, foFirstPrior))
rc=FALSE;
/* If we were just outside the deleted range, adjust appropriately */
if (ha->cur_msg==(dword)Sqd->wSkipMsg+dwDeleted+1)
{
/* Adjust our 'previous' pointer, if we come just after the deleted *
* range. */
if (Sqd->wSkipMsg==0)
Sqd->foPrev=NULL_FRAME;
else Sqd->foPrev=_SquishGetFrameOfs(ha, (dword)Sqd->wSkipMsg);
}
else if (ha->cur_msg==(dword)Sqd->wSkipMsg)
{
/* If we come just before the deleted range, adjust our 'next' ptr */
Sqd->foNext=foFirst;
}
/* If the current message number was greater than skip_msg, we will *
* need to adjust our pointers. */
if (ha->cur_msg > (dword)Sqd->wSkipMsg)
{
if (ha->cur_msg <= (dword)Sqd->wSkipMsg+dwDeleted)
{
SQHDR sqh;
FOFS fo;
/* We were inside the range of messages that was deleted. To handle *
* this, simply set our pointer to the first message outside of that *
* range. */
if (Sqd->wSkipMsg &&
(fo=_SquishGetFrameOfs(ha, (dword)Sqd->wSkipMsg)) != NULL_FRAME &&
_SquishReadHdr(ha, fo, &sqh))
{
Sqd->foCur=fo;
Sqd->foPrev=sqh.prev_frame;
Sqd->foNext=sqh.next_frame;
ha->cur_msg=Sqd->wSkipMsg;
}
else
{
Sqd->foNext=Sqd->foFirst;
Sqd->foCur=NULL_FRAME;
Sqd->foPrev=NULL_FRAME;
ha->cur_msg=0;
}
}
else
{
/* We were above the deleted range, so we will have to decrement our *
* message number. */
ha->cur_msg -= dwDeleted;
}
}
/* Adjust the HWM, if necessary */
if (ha->high_water >= Sqd->wSkipMsg)
{
long lNewHWM = (long)ha->high_water - (long)dwDeleted;
if (lNewHWM < (long)Sqd->wSkipMsg)
ha->high_water = Sqd->wSkipMsg;
else ha->high_water=(dword)lNewHWM;
}
return rc;
}
/* Delete enough messages in this area so that we fall below the *
* dwMaxMsg limit. *
* *
* This function assumes that we have exclusive access to the Squish base. */
static unsigned near _SquishReduceMaxMsgs(HAREA ha)
{
FOFS foFirstPrior=NULL_FRAME;
FOFS foFirst=NULL_FRAME;
dword dwDeleted=0;
unsigned rc=TRUE;
assert(Sqd->fHaveExclusive);
/* If we don't have too many messages, just return */
if (!Sqd->dwMaxMsg ||
ha->num_msg < Sqd->dwMaxMsg ||
ha->num_msg <= (dword)Sqd->wSkipMsg)
{
return TRUE;
}
/* Read the index into memory */
if (! _SquishBeginBuffer(Sqd->hix))
return FALSE;
/* Move all of the messages to the free list */
if (!_SquishReduceMaxInternal(ha, &dwDeleted, &foFirst, &foFirstPrior))
rc=FALSE;
/* Make sure that our pointers are okay */
if (!_SquishReduceMaxPointers(ha, foFirst, dwDeleted, foFirstPrior))
rc=FALSE;
/* Write the index back */
if (!_SquishEndBuffer(Sqd->hix))
rc=FALSE;
return rc;
}
/* Create a new Squish message, possibly overwriting an old one *
* (if dwMsg==0). */
static unsigned near _SquishOpenMsgCreate(HMSG hmsg, dword dwMsg)
{
unsigned rc=TRUE;
/* If we are creating a completely-new message, we need to adjust the *
* header and index file IMMEDIATELY to show that we want this *
* new message number. */
if (! _SquishExclusiveBegin(hmsg->ha))
return FALSE;
/* If we are creating a new message, make sure that dwMsg is zero! */
if (dwMsg > hmsg->ha->num_msg)
dwMsg=0;
/* Make sure that we don't overrun the max_msgs limit! */
if (dwMsg==0)
rc=_SquishReduceMaxMsgs(hmsg->ha);
/* Set our message number */
hmsg->dwMsg=dwMsg ? dwMsg : hmsg->ha->num_msg+1;
/* Now fix up the index and data files to indicate that message creation *
* is in process. */
if (rc)
{
if (dwMsg)
rc=_SquishBlankOldMsg(hmsg, dwMsg);
else rc=_SquishBlankNewMsg(hmsg);
}
/* If we are creating a message, increment the total number of messages */
if (rc && !dwMsg)
{
hmsg->ha->num_msg++;
hmsg->ha->high_msg++;
}
/* End exclusive access */
if (! _SquishExclusiveEnd(hmsg->ha))
rc=FALSE;
return rc;
}
/* Open a Squish message */
HMSG _XPENTRY apiSquishOpenMsg(HAREA ha, word wMode, dword dwMsg)
{
HMSG hmsg;
unsigned fOpened=FALSE;
if (MsgInvalidHarea(ha))
return NULL;
/* Allocate a handle for this message */
if ((hmsg=NewHmsg(ha, wMode))==NULL)
{
return NULL;
}
/* Translate dwMsg into a real message number, if necessary */
dwMsg=_SquishTranslateNum(hmsg->ha, dwMsg);
/* Create a new message, or open an existing message, as specified */
if (wMode==MOPEN_CREATE)
fOpened=_SquishOpenMsgCreate(hmsg, dwMsg);
else
fOpened=_SquishOpenMsgExisting(hmsg, dwMsg);
/* If the open succeeded, add this to the list of open msgs for this area */
if (fOpened)
{
hmsg->hmsgNext=Sqd->hmsgOpen;
Sqd->hmsgOpen=hmsg;
}
else
{
/* Otherwise, free memory and get out */
pfree(hmsg);
hmsg=NULL;
}
return hmsg;
}
/* This function undoes what SquishOpenMsg did when creating a new *
* message. This is only called if the write was not completed. */
static unsigned near _SquishCloseUndoWrite(HMSG hmsg)
{
if (! _SquishExclusiveBegin(hmsg->ha))
return FALSE;
/* Check again, just in case something happened on another node */
if (hmsg->dwMsg==hmsg->ha->num_msg)
{
hmsg->ha->num_msg--;
hmsg->ha->high_msg--;
}
if (! _SquishExclusiveEnd(hmsg->ha))
return FALSE;
return TRUE;
}
/* Remove the message 'hmsg' from the linked list of open messages */
static unsigned near _SquishCloseRemoveList(HMSG hmsg)
{
HMSG hm=HSqd->hmsgOpen;
if (!hm)
{
msgapierr=MERR_BADA;
return FALSE;
}
/* If our message was at the head of the list, just adjust main ptr */
if (HSqd->hmsgOpen==hmsg)
{
HSqd->hmsgOpen=hmsg->hmsgNext;
return TRUE;
}
/* Otherwise, try to find this message in the linked list of msgs */
while (hm)
{
/* If we found us, just skip the list over to the next msg */
if (hm->hmsgNext==hmsg)
{
hm->hmsgNext=hmsg->hmsgNext;
return TRUE;
}
hm=hm->hmsgNext;
}
msgapierr=MERR_BADA;
return FALSE;
}
/* Close an open message handle */
sword _XPENTRY apiSquishCloseMsg(HMSG hmsg)
{
if (MsgInvalidHmsg(hmsg))
return -1;
/* If we allocated a new number for this message, but we did not use it... */
if (hmsg->wMode==MOPEN_CREATE && !hmsg->fWritten &&
hmsg->dwMsg==hmsg->ha->num_msg)
{
if (!_SquishCloseUndoWrite(hmsg))
return -1;
}
/* Remove this msg from the list of open msgs for this area */
(void)_SquishCloseRemoveList(hmsg);
/* Reset the ID so that our functions will not accept the freed hmsg */
hmsg->id=0L;
/* Deallocate memory and return to caller */
pfree(hmsg);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1