/*************************************************************************** * * * 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_write.c,v 1.13 2003/02/01 22:14:51 stas_degteff Exp $"; #pragma on(unreferenced) */ #define MSGAPI_HANDLERS #define MSGAPI_NO_OLD_TYPES #include #include #include #include #include "compiler.h" #ifdef HAS_UNISTD_H #include #endif #ifdef HAS_IO_H # include #endif #ifdef HAS_SHARE_H #include #endif #ifdef HAS_MALLOC_H #include #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" /* This function searches the list of free frames to find one which is * * large enough to hold a message of size dwLen. * * * * This function assumes that we have exclusive access to the Squish base. */ static unsigned near _SquishProbeFreeChain(HAREA ha, dword dwLen, FOFS *pfo, SQHDR *psqh, dword *pdwFrameLen) { FOFS foThis, foLast; assert(Sqd->fHaveExclusive); /* Assume that we haven't found anything */ *pfo=NULL_FRAME; *pdwFrameLen=0L; foLast=NULL_FRAME; /* Look through all of the entries in the free chain */ for (foThis=Sqd->foFree; foThis != NULL_FRAME; foLast=foThis, foThis=psqh->next_frame) { /* Try to read the header at this point in the list */ if (!_SquishReadHdr(ha, foThis, psqh)) return FALSE; /* Verify that this is a valid frame and that we aren't going around * * in circles. */ if (psqh->frame_type != FRAME_FREE || foLast != psqh->prev_frame || psqh->next_frame==foThis) { msgapierr=MERR_BADF; return FALSE; } /* If the frame is long enough, we can use it */ if (psqh->frame_length >= dwLen) { *pdwFrameLen=psqh->frame_length; *pfo=foThis; break; } } return TRUE; } /* This function removes the frame at offset 'fo' from the free chain. * * * * This function assumes that we have exclusive access to the Squish base. */ static unsigned near _SquishRemoveFreeChain(HAREA ha, FOFS fo, SQHDR *psqh) { assert(Sqd->fHaveExclusive); /* Validate that the frames which pretend to be list heads/tails are * * actually what they seem. */ if ((psqh->prev_frame==NULL_FRAME && fo != Sqd->foFree) || (psqh->next_frame==NULL_FRAME && fo != Sqd->foLastFree)) { msgapierr=MERR_BADF; return FALSE; } /* If there is a frame before this one, set it to skip over this frame */ if (psqh->prev_frame) if (!_SquishSetFrameNext(ha, psqh->prev_frame, psqh->next_frame)) return FALSE; /* Do the same for the other side of the linked list */ if (psqh->next_frame) if (!_SquishSetFramePrev(ha, psqh->next_frame, psqh->prev_frame)) return FALSE; /* Now update the head and tail pointers for the free message list */ if (Sqd->foFree==fo) Sqd->foFree=psqh->next_frame; if (Sqd->foLastFree==fo) Sqd->foLastFree=psqh->prev_frame; return TRUE; } /* Allocate a frame of length dwLen at end-of-file, and store result in pfo.* * * * This function assumes that we have exclusive access to the Squish * * base. */ static unsigned near _SquishGetFrameEOF(HAREA ha, FOFS *pfo, dword dwLen) { char nul=0; long ofs; assert(Sqd->fHaveExclusive); /* Find the last byte that we will have to write for this frame, to * * ensure that we have enough disk space to write this message. */ ofs=Sqd->foEnd + (long)Sqd->cbSqhdr + (long)dwLen - 1L; /* Now try to write it, just to make sure... */ if (lseek(Sqd->sfd, ofs, SEEK_SET) != ofs || write(Sqd->sfd, &nul, 1) != 1) { msgapierr=MERR_NODS; return FALSE; } /* We got it! So, update the frame offset, and modify the end-of-file * * offset appropriately. */ *pfo=Sqd->foEnd; Sqd->foEnd=ofs+1; return TRUE; } /* This function searches the free chain to find a message of at least the * * specified size. If that fails, we allocate a new frame at EOF. * * * * This function assumes that we have exclusive access to the Squish base. */ static unsigned near _SquishGetNewFrame(HMSG hmsg, dword dwLen, FOFS *pfoNew, dword *pdwFrameLen) { SQHDR sqh; FOFS fo; assert(HSqd->fHaveExclusive); /* Assume we got a new frame */ *pdwFrameLen=0L; /* Check the free chain */ if (!_SquishProbeFreeChain(hmsg->ha, dwLen, &fo, &sqh, pdwFrameLen)) return FALSE; /* If we got a frame from the free chain, remove it */ if (fo) { if (!_SquishRemoveFreeChain(hmsg->ha, fo, &sqh)) return FALSE; *pfoNew=fo; return TRUE; } /* Nothing in the free chain, so we will add a new frame at EOF, and * * make it as long as necessary. */ *pdwFrameLen=0; return _SquishGetFrameEOF(hmsg->ha, pfoNew, dwLen); } /* Given some blank space at offset hmsg->foWrite, we are to create a new * * message frame there and link it into the message chain. If hmsg->foRead * * is blank, just append the message to the end of the chain. However, * * if foRead is NOT blank, we should set the new frame so that it will * * appear between hmsg->sqhRead.prev and hmsg->sqhRead.next, as we are * * overwriting an old message. * * * * At exit, the header that we just created will be left in hmsg->sqhWrite. * * * * This function assumes that we have exclusive access to the Squish base. */ static unsigned near _SquishLinkMessageFrame(HMSG hmsg, dword dwTotal, dword dwCtrlLen, dword dwFrameLen) { assert(HSqd->fHaveExclusive); assert(dwFrameLen==0 || dwFrameLen >= dwTotal); /* Fill out default values for the frame header */ hmsg->sqhWrite.id=SQHDRID; hmsg->sqhWrite.frame_length=dwFrameLen ? dwFrameLen : dwTotal; hmsg->sqhWrite.msg_length=dwTotal; hmsg->sqhWrite.clen=dwCtrlLen; hmsg->sqhWrite.frame_type=FRAME_NORMAL; hmsg->sqhWrite.rsvd=0; /* If we have to link it into the middle of the base */ if (hmsg->foRead) { /* Set the links for our own frame */ hmsg->sqhWrite.prev_frame=hmsg->sqhRead.prev_frame; hmsg->sqhWrite.next_frame=hmsg->sqhRead.next_frame; /* Fix the link for the message after us, if any */ if (hmsg->sqhWrite.next_frame != NULL_FRAME) { if (!_SquishSetFramePrev(hmsg->ha, hmsg->sqhWrite.next_frame, hmsg->foWrite)) { return FALSE; } } } else { /* Append this frame at the end of the list */ hmsg->sqhWrite.prev_frame=HSqd->foLast; hmsg->sqhWrite.next_frame=NULL_FRAME; } /* Set the links for the message before us */ if (hmsg->sqhWrite.prev_frame != NULL_FRAME) { if (!_SquishSetFrameNext(hmsg->ha, hmsg->sqhWrite.prev_frame, hmsg->foWrite)) { return FALSE; } } /* If this has just become the head of this list, fix pointers... */ if (hmsg->sqhWrite.prev_frame==NULL_FRAME) { /* Sanity check: if we are becoming the beginning frame, we must * * have either replaced the first message, or we are simply creating * * the first message in a Squish base. */ assert(hmsg->foRead==HSqd->foFirst || HSqd->foFirst==NULL_FRAME); HSqd->foFirst=hmsg->foWrite; } /* If this has just become the tail of the list, fix pointers... */ if (hmsg->sqhWrite.next_frame==NULL_FRAME) { /* Sanity check: if we are becoming the EOF frame, we must have * * either added a new message, or replaced the old end-of-file message */ if (hmsg->foRead) assert(hmsg->foRead==HSqd->foLast); HSqd->foLast=hmsg->foWrite; } /* If we wrote the message before or after the lastread msg, update ptrs */ if (hmsg->dwMsg==hmsg->ha->cur_msg) HSqd->foCur=hmsg->foWrite; else if (hmsg->dwMsg==hmsg->ha->cur_msg+1) HSqd->foNext=hmsg->foWrite; else if (hmsg->dwMsg==hmsg->ha->cur_msg-1) HSqd->foPrev=hmsg->foWrite; /* Now update the frame for the message that we just wrote */ return _SquishWriteHdr(hmsg->ha, hmsg->foWrite, &hmsg->sqhWrite); } /* Find a frame within the Squish file for writing this message, then * * allocate it. * * * * This function assumes that we have exclusive access to the Squish base. */ static unsigned near _SquishGetWriteFrame(HMSG hmsg, dword dwTxtTotal, dword dwCtrlLen) { dword dwTotal=(dword)XMSG_SIZE+dwTxtTotal+dwCtrlLen; dword dwFrameLen=0; assert(HSqd->fHaveExclusive); /* If we're writing over an existing message, verify that the * * total and control lengths are not more than what we have already. */ if (hmsg->wMode==MOPEN_RW || hmsg->wMode==MOPEN_WRITE) { if (dwTotal > hmsg->sqhRead.msg_length) { msgapierr=MERR_TOOBIG; return FALSE; } /* Copy the read-mode parameters, since we are now writing over this * * message at the specified offset. */ hmsg->foWrite=hmsg->foRead; hmsg->sqhWrite=hmsg->sqhRead; } else if (hmsg->wMode==MOPEN_CREATE) { /* First, if we are replacing an existing message, release its frame * * to the free chain. */ if (hmsg->foRead) { if (! _SquishInsertFreeChain(hmsg->ha, hmsg->foRead, &hmsg->sqhRead)) return FALSE; } /* We are creating a new message, so we just have to find a frame * * big enough to hold this message. */ if (! _SquishGetNewFrame(hmsg, dwTotal, &hmsg->foWrite, &dwFrameLen)) { /* That failed, so we can't write the message. However, if we were * * trying to replace an existing message, we have added that message * * to the free chain, but we have NOT linked the other messages * * to skip over it. The only recourse is to do the linking and * * remove it from the index file (effectively deleting that message) */ if (hmsg->foRead) { /* Link the old messages over this one */ (void)_SquishSetFrameNext(hmsg->ha, hmsg->sqhRead.prev_frame, hmsg->sqhRead.next_frame); (void)_SquishSetFramePrev(hmsg->ha, hmsg->sqhRead.next_frame, hmsg->sqhRead.prev_frame); /* Now remove the old one from the index */ (void)_SquishRemoveIndexEntry(HSqd->hix, hmsg->dwMsg, NULL, &hmsg->sqhRead, TRUE); } hmsg->foWrite=NULL_FRAME; return FALSE; } /* Now link this new message frame into our list of messages to write */ if (!_SquishLinkMessageFrame(hmsg, dwTotal, dwCtrlLen, dwFrameLen)) { hmsg->foWrite=NULL_FRAME; return FALSE; } } hmsg->dwWritePos=0L; return TRUE; } /* Write the XMSG header to the Squish file */ static unsigned near _SquishWriteXmsg(HMSG hmsg, PXMSG pxm, dword *pdwOfs) { /* XMSG xmsg; */ #define xmsg (*pxm) long ofs=hmsg->foWrite + HSqd->cbSqhdr; /* If we don't know our UMSGID, retrieve it from the index file */ if (!hmsg->uidUs) { SQIDX sqi; /*if (_SquishReadIndexRecord(hmsg->ha, hmsg->dwMsg, &sqi))*/ if (SidxGet(HSqd->hix, hmsg->dwMsg, &sqi)) hmsg->uidUs=sqi.umsgid; } /* Make local copy of XMSG struct */ /* OG: Wozu ? */ /* xmsg=*pxm; */ /* OG: Changes to make Squish 1.11 Y2K - experimental Some stupid programs don't fill out the 'FTSC-Date' Field */ if (xmsg.date_written.date.yr > 19 || xmsg.__ftsc_date[0] == 0) MsgCvtFTSCDateToBinary((char*)(xmsg.__ftsc_date), (union stamp_combo *)&xmsg.date_written); /* KLUDGE: Store the UMSGID in the message header so that SQFIX can * * use it to restore the index file, if necessary. However, if the * * umsgid is not known, make sure that the MSGUID bit is not set. */ if (!hmsg->uidUs) { xmsg.attr &= ~MSGUID; xmsg.umsgid=(UMSGID)0L; } else { xmsg.attr |= MSGUID; xmsg.umsgid=hmsg->uidUs; } /* Write the message to disk */ if (ofs != (long)*pdwOfs) if (lseek(HSqd->sfd, ofs, SEEK_SET) != ofs) { msgapierr=MERR_NODS; return FALSE; } if (write_xmsg(HSqd->sfd, pxm) != 1) { msgapierr=MERR_NODS; return FALSE; } *pdwOfs = (dword)ofs + (dword)XMSG_SIZE; return TRUE; #undef xmsg } /* Write the control information to the Squish file */ static unsigned near _SquishWriteCtrl(HMSG hmsg, byte *szCtrl, dword dwCtrlLen, dword *pdwOfs) { long ofs; /* We can only write control information on the first pass */ if (hmsg->fWritten) return TRUE; /* Make sure that the control information is not longer than * what we wrote the first time, if we're updating a message. */ if (dwCtrlLen > hmsg->sqhWrite.clen) dwCtrlLen=hmsg->sqhWrite.clen; /* Make sure that we don't try to do a write with len==0 */ if (!dwCtrlLen) return TRUE; /* Now seek to the appropriate offset */ ofs=hmsg->foWrite + HSqd->cbSqhdr + XMSG_SIZE; /* Write the control information at the appropriate offset */ if (ofs != (long)*pdwOfs) if (lseek(HSqd->sfd, ofs, SEEK_SET) != ofs) { msgapierr=MERR_NODS; return FALSE; } if (write(HSqd->sfd, (char *)szCtrl, (unsigned)dwCtrlLen) != (int)dwCtrlLen) { msgapierr=MERR_NODS; return FALSE; } *pdwOfs=(dword)ofs + dwCtrlLen; return TRUE; } /* Now write the message body, appending if necessary */ static unsigned near _SquishWriteTxt(HMSG hmsg, unsigned fAppend, byte *szTxt, dword dwTxtLen, dword *pdwOfs) { dword dwMaxWrite; long ofs; /* Figure out where to start writing text */ ofs=hmsg->foWrite + (long)HSqd->cbSqhdr + (long)XMSG_SIZE + (long)hmsg->sqhWrite.clen; /* Figure out how much we can write, at most */ dwMaxWrite=hmsg->sqhWrite.msg_length - XMSG_SIZE - hmsg->sqhWrite.clen; /* If we're appending to existing text, make sure that we adjust properly */ if (fAppend) { ofs += (long)hmsg->dwWritePos; dwMaxWrite -= hmsg->dwWritePos; } dwMaxWrite=min(dwMaxWrite, dwTxtLen); /* Now seek to the right spot and write the message text */ if (*pdwOfs != (dword)ofs) if (lseek(HSqd->sfd, ofs, SEEK_SET) != ofs) { msgapierr=MERR_NODS; return FALSE; } if (write(HSqd->sfd, (char *)szTxt, (unsigned)dwMaxWrite) != (int)dwMaxWrite) { msgapierr=MERR_NODS; return FALSE; } *pdwOfs = (dword)ofs + dwMaxWrite; /* Update the pointer in case we append to this msg in the future */ hmsg->dwWritePos += dwMaxWrite; return TRUE; } /* Update the index at offset hmsg->dwMsg with the information given in * * in the XMSG structure. */ static unsigned near _SquishUpdateIndex(HMSG hmsg, PXMSG pxm) { SQIDX sqi; if (!SidxGet(HSqd->hix, hmsg->dwMsg, &sqi)) return FALSE; /* Update this with the position of the message and the contents * * of the 'To:' field. */ sqi.ofs=hmsg->foWrite; sqi.hash=SquishHash(pxm->to); /* If the message has been read, set the high bit of the hash */ if (pxm->attr & MSGREAD) sqi.hash |= IDXE_MSGREAD; return (unsigned)SidxPut(HSqd->hix, hmsg->dwMsg, &sqi); } /* Write a message to a Squish base */ sword _XPENTRY apiSquishWriteMsg(HMSG hmsg, word fAppend, PXMSG pxm, byte *szTxt, dword dwTxtLen, dword dwTxtTotal, dword dwCtrlLen, byte *szCtrl) { dword dwOfs=(dword)-1L; /* Make sure that we have appropriate access to this message */ if (MsgInvalidHmsg(hmsg) || !_SquishWriteMode(hmsg)) return -1; /* Make sure that szTxt and szCtrl are consistent */ if (!dwTxtLen) szTxt=NULL; if (!dwCtrlLen) szCtrl=NULL; /* If we need to get a frame offset, allocate it here... */ if (!hmsg->foWrite) { unsigned rc; /* We need to have an XMSG on the first write to any message */ if (!pxm) { msgapierr=MERR_BADA; return -1; } if (! _SquishExclusiveBegin(hmsg->ha)) { return -1; } rc=_SquishGetWriteFrame(hmsg, dwTxtTotal, dwCtrlLen); if (! _SquishExclusiveEnd(hmsg->ha) || !rc) { return -1; } } assert(hmsg->foWrite); /* Now write the various bits of the message */ if (pxm) if (!_SquishWriteXmsg(hmsg, pxm, &dwOfs)) { return -1; } if (szCtrl) if (!_SquishWriteCtrl(hmsg, szCtrl, dwCtrlLen, &dwOfs)) { return -1; } if (szTxt) if (!_SquishWriteTxt(hmsg, fAppend, szTxt, dwTxtLen, &dwOfs)) { return -1; } hmsg->fWritten=TRUE; /* If this was our first write to this message, update the index entry * * appropriately. */ if (pxm) if (! _SquishUpdateIndex(hmsg, pxm)) { return -1; } return 0; }