/* JAMLIB - A JAM subroutine library Copyright (C) 1999 Björn Stenberg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Changes made by Johan Billing 2000-04-16: - Added support for Win32 and Linux - Changed JAM_OpenMB to open files in binary mode - Changed source to use feof() instead of errno == EPASTEOF - Changed source to use structrw to read and write structures - Fixed broken JAM_FindUser() - #includes string.h and stdlib.h instead of memory.h Backported changes from JAMLIB 1.4.7 made by Johan Billing 2003-10-26 - Now uses calloc instead of malloc/memset - (*NewArea_PPS) will be set to zero even if calloc() failed in JAM_OpenMB() and JAM_CreateMB() - JAM_CreateMB() no longer attempts to forever to lock the newly created messagebase. If the first attempt fails, it will return an error. - jam_Lock() now sets Base_PS->Errno under Linux Other changes made by Johan Billing 2003-10-26 - Fixed comparison between signed and unsigned variable in JAM_GetMBSize() - JAM_CreateMB() would not unlock and close the newly created messagebase upon failure. Changes made by Johan Billing 2004-07-10 - Updated the Win32-specific parts of the code to make it compatible with newer versions of MinGW (tested with 3.1.0-1): * Now uses Sleep() instead of sleep() * Changed _LK_UNLOCK to _LK_UNLCK in jam_Lock() */ /*********************************************************************** ** ** MBASE.C -- Message base handling ** ** Author: Bj”rn Stenberg (bjorn.stenberg@sth.frontec.se) ** ***********************************************************************/ #include <stdio.h> #include <errno.h> #include <time.h> #include <stdlib.h> #include <string.h> #include "jam.h" #include "structrw.h" #if defined( __OS2__ ) #include <os2.h> /* ANSI C does not include file locking :-( */ #endif #if defined( __WIN32__ ) #include <sys/locking.h> #include <io.h> #include <windows.h> #if !defined( _LK_UNLCK ) && defined ( _LK_UNLOCK ) #define _LK_UNLCK _LK_UNLOCK /* For backwards compatibility */ #endif #endif #if defined( __LINUX__ ) #include <sys/file.h> #include <unistd.h> #endif #define OS_ERROR_OFFSET 10000 #if defined( __OS2__ ) #define JAM_Sleep( _x_ ) DosSleep( _x_*1000 ) #endif #if defined( __WIN32__ ) #define JAM_Sleep(x) Sleep(x*1000) #endif #if defined( __LINUX__ ) #define JAM_Sleep(x) sleep(x) #endif /*************************************<********************************** ** ** File-global functions ** ***********************************************************************/ int jam_Open( s_JamBase* Base_PS, char* Filename_PC, char* Mode_PC ); int jam_Lock( s_JamBase* Base_PS, int DoLock_I ); /*********************************************************************** ** ** JAM_OpenMB - Open message base ** ***********************************************************************/ int JAM_OpenMB( char* Basename_PC, s_JamBase** NewArea_PPS ) { s_JamBase* Base_PS; int Status_I; if ( !NewArea_PPS ) return JAM_BAD_PARAM; *NewArea_PPS = NULL; Base_PS = (s_JamBase*) calloc( 1, sizeof( s_JamBase ) ); if (!Base_PS) return JAM_NO_MEMORY; *NewArea_PPS = Base_PS; Status_I = jam_Open( Base_PS, Basename_PC, "r+b" ); if ( Status_I ) { return Status_I; } return 0; } /*********************************************************************** ** ** JAM_CreateMB - Create a new message base ** ***********************************************************************/ int JAM_CreateMB( char* Basename_PC, uint32_t BaseMsg_I, s_JamBase** NewArea_PPS ) { s_JamBaseHeader Base_S; int Status_I; s_JamBase* Base_PS; if ( !NewArea_PPS || !BaseMsg_I ) return JAM_BAD_PARAM; *NewArea_PPS = NULL; Base_PS = (s_JamBase*) calloc( 1, sizeof( s_JamBase ) ); if (!Base_PS) return JAM_NO_MEMORY; *NewArea_PPS = Base_PS; Status_I = jam_Open( Base_PS, Basename_PC, "w+b" ); if ( Status_I ) return Status_I; Base_S.DateCreated = time(NULL); Base_S.ModCounter = 0; Base_S.ActiveMsgs = 0; Base_S.PasswordCRC = 0xffffffff; Base_S.BaseMsgNum = BaseMsg_I; memset( &Base_S.RSRVD, 0, sizeof( Base_S.RSRVD ) ); Status_I = JAM_LockMB( Base_PS, 0 ); /* If the new base cannot be locked directly, something is seriously wrong */ if ( Status_I ) { JAM_CloseMB(Base_PS); return Status_I; } Status_I = JAM_WriteMBHeader( Base_PS, &Base_S ); if ( Status_I ) { JAM_UnlockMB( Base_PS ); JAM_CloseMB(Base_PS); return Status_I; } JAM_UnlockMB( Base_PS ); return 0; } /*********************************************************************** ** ** JAM_CloseMB - Close message base ** ***********************************************************************/ int JAM_CloseMB( s_JamBase* Base_PS ) { if ( Base_PS->Locked_I ) { int Status_I = JAM_UnlockMB( Base_PS ); if ( Status_I ) return Status_I; } if ( Base_PS->HdrFile_PS ) { fclose( Base_PS->HdrFile_PS ); Base_PS->HdrFile_PS = NULL; fclose( Base_PS->TxtFile_PS ); Base_PS->TxtFile_PS = NULL; fclose( Base_PS->IdxFile_PS ); Base_PS->IdxFile_PS = NULL; fclose( Base_PS->LrdFile_PS ); Base_PS->LrdFile_PS = NULL; } Base_PS->Locked_I = 0; return 0; } /*********************************************************************** ** ** JAM_RemoveMB - Remove a message base ** ***********************************************************************/ int JAM_RemoveMB( s_JamBase* Base_PS, char* Basename_PC ) { char Filename_AC[250]; int Status_AI[4]; /* .JHR file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_HDRFILE ); Status_AI[0] = remove( Filename_AC ); if ( Status_AI[0] ) Base_PS->Errno_I = errno; /* .JDT file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_TXTFILE ); Status_AI[1] = remove( Filename_AC ); if ( Status_AI[1] ) Base_PS->Errno_I = errno; /* .JDX file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_IDXFILE ); Status_AI[2] = remove( Filename_AC ); if ( Status_AI[2] ) Base_PS->Errno_I = errno; /* .JLR file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_LRDFILE ); Status_AI[3] = remove( Filename_AC ); if ( Status_AI[3] ) Base_PS->Errno_I = errno; if (Status_AI[0] || Status_AI[1] || Status_AI[2] || Status_AI[3]) return JAM_IO_ERROR; return 0; } /*********************************************************************** ** ** JAM_GetMBSize - Get the number of messages in message base ** ***********************************************************************/ int JAM_GetMBSize( s_JamBase* Base_PS, uint32_t* Messages_PI ) { long Offset_I; /* go to end of index file */ if ( fseek( Base_PS->IdxFile_PS, 0, SEEK_END ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } Offset_I = ftell( Base_PS->IdxFile_PS ); if ( Offset_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } *Messages_PI = Offset_I / sizeof( s_JamIndex ); return 0; } /*********************************************************************** ** ** JAM_LockMB - Lock message base ** ***********************************************************************/ int JAM_LockMB( s_JamBase* Base_PS, int Timeout_I ) { if ( Base_PS->Locked_I ) return 0; switch ( Timeout_I ) { /* unlimited timeout */ case -1: while ( jam_Lock( Base_PS, 1 ) == JAM_LOCK_FAILED ) JAM_Sleep( 1 ); return 0; /* no timeout */ case 0: return jam_Lock( Base_PS, 1 ); /* X seconds timeout */ default: { time_t Time_I = time(NULL) + Timeout_I; while ( time(NULL) < Time_I ) { int Result_I; Result_I = jam_Lock( Base_PS, 1 ); if ( Result_I == JAM_LOCK_FAILED ) JAM_Sleep( 1 ); else return Result_I; } return JAM_LOCK_FAILED; } } } /*********************************************************************** ** ** JAM_UnlockMB - Flush all writes and unlock message base ** ***********************************************************************/ int JAM_UnlockMB( s_JamBase* Base_PS ) { fflush( Base_PS->HdrFile_PS ); fflush( Base_PS->TxtFile_PS ); fflush( Base_PS->IdxFile_PS ); fflush( Base_PS->LrdFile_PS ); return jam_Lock( Base_PS, 0 ); } /*********************************************************************** ** ** JAM_ReadMBHeader - Read message base header ** ***********************************************************************/ int JAM_ReadMBHeader( s_JamBase* Base_PS, s_JamBaseHeader* Header_PS ) { if ( !Header_PS || !Base_PS ) return JAM_BAD_PARAM; if ( fseek( Base_PS->HdrFile_PS, 0, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( 1 > freadjambaseheader(Base_PS->HdrFile_PS,Header_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } return 0; } /*********************************************************************** ** ** JAM_WriteMBHeader - Write message base header ** ***********************************************************************/ int JAM_WriteMBHeader( s_JamBase* Base_PS, s_JamBaseHeader* Header_PS ) { if ( !Header_PS || !Base_PS ) return JAM_BAD_PARAM; if ( !Base_PS->Locked_I ) return JAM_NOT_LOCKED; if ( fseek( Base_PS->HdrFile_PS, 0, SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* ensure header looks right */ memcpy( Header_PS->Signature, HEADERSIGNATURE, 4 ); Header_PS->ModCounter++; if ( 1 > fwritejambaseheader(Base_PS->HdrFile_PS,Header_PS) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } fflush( Base_PS->HdrFile_PS ); return 0; } /*********************************************************************** ** ** JAM_FindUser - Scan scan file for messages to a user ** ***********************************************************************/ int JAM_FindUser( s_JamBase* Base_PS, uint32_t UserCrc_I, uint32_t StartMsg_I, uint32_t* MsgNo_PI ) { uint32_t MsgNo_I; /* go to start message */ if ( fseek( Base_PS->IdxFile_PS, StartMsg_I * sizeof( s_JamIndex ), SEEK_SET ) ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* scan file */ for ( MsgNo_I = StartMsg_I; ; MsgNo_I++ ) { s_JamIndex Index_S; if ( 1 > freadjamindex(Base_PS->IdxFile_PS,&Index_S) ) { if ( feof(Base_PS->IdxFile_PS) ) return JAM_NO_USER; Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( Index_S.UserCRC == UserCrc_I ) break; } *MsgNo_PI = MsgNo_I; return 0; } /*********************************************************************** ** ** jam_Lock - Lock/unlock a message base ** ***********************************************************************/ int jam_Lock( s_JamBase* Base_PS, int DoLock_I ) { #if defined(__OS2__) FILELOCK Area_S; APIRET Status_I; ULONG Timeout_I = 0; int Handle_I; Handle_I = fileno( Base_PS->HdrFile_PS ); if ( Handle_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } Area_S.lOffset = 0; Area_S.lRange = 1; if ( DoLock_I ) Status_I = DosSetFileLocks( Handle_I, NULL, &Area_S, Timeout_I, 0 ); else Status_I = DosSetFileLocks( Handle_I, &Area_S, NULL, Timeout_I, 0 ); if ( Status_I ) { if ( 232 == Status_I ) return JAM_LOCK_FAILED; Base_PS->Errno_I = Status_I + OS_ERROR_OFFSET; return JAM_IO_ERROR; } if ( DoLock_I ) Base_PS->Locked_I = 1; else Base_PS->Locked_I = 0; return 0; #elif defined(__WIN32__) int Handle_I,Status_I; fseek(Base_PS->HdrFile_PS,0,SEEK_SET); /* Lock from start of file */ Handle_I = fileno( Base_PS->HdrFile_PS ); if ( Handle_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if ( DoLock_I ) Status_I = _locking(Handle_I,_LK_NBLCK,1); else Status_I = _locking(Handle_I,_LK_UNLCK,1); if ( Status_I ) return JAM_LOCK_FAILED; if ( DoLock_I ) Base_PS->Locked_I = 1; else Base_PS->Locked_I = 0; return 0; #elif defined(__LINUX__) int Handle_I,Status_I; struct flock fl; fseek(Base_PS->HdrFile_PS,0,SEEK_SET); /* Lock from start of file */ Handle_I = fileno( Base_PS->HdrFile_PS ); if ( Handle_I == -1 ) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } if(DoLock_I) fl.l_type=F_WRLCK; else fl.l_type=F_UNLCK; fl.l_whence=SEEK_SET; fl.l_start=0; fl.l_len=1; fl.l_pid=getpid(); Status_I=fcntl(Handle_I,F_SETLK,&fl); if ( Status_I ) { Base_PS->Errno_I = errno; return JAM_LOCK_FAILED; } if ( DoLock_I ) Base_PS->Locked_I = 1; else Base_PS->Locked_I = 0; return 0; #else #error Unsupported platform #endif } /*********************************************************************** ** ** jam_Open - Open/create message base files ** ***********************************************************************/ int jam_Open( s_JamBase* Base_PS, char* Basename_PC, char* Mode_PC ) { char Filename_AC[250]; /* .JHR file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_HDRFILE ); Base_PS->HdrFile_PS = fopen( Filename_AC, Mode_PC ); if (!Base_PS->HdrFile_PS) { Base_PS->Errno_I = errno; return JAM_IO_ERROR; } /* .JDT file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_TXTFILE ); Base_PS->TxtFile_PS = fopen( Filename_AC, Mode_PC ); if (!Base_PS->TxtFile_PS) { Base_PS->Errno_I = errno; fclose( Base_PS->HdrFile_PS ); Base_PS->HdrFile_PS = NULL; return JAM_IO_ERROR; } /* .JDX file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_IDXFILE ); Base_PS->IdxFile_PS = fopen( Filename_AC, Mode_PC ); if (!Base_PS->IdxFile_PS) { Base_PS->Errno_I = errno; fclose( Base_PS->HdrFile_PS ); Base_PS->HdrFile_PS = NULL; fclose( Base_PS->TxtFile_PS ); Base_PS->TxtFile_PS = NULL; return JAM_IO_ERROR; } /* .JLR file */ sprintf( Filename_AC, "%s%s", Basename_PC, EXT_LRDFILE ); Base_PS->LrdFile_PS = fopen( Filename_AC, Mode_PC ); if (!Base_PS->LrdFile_PS) { Base_PS->Errno_I = errno; fclose( Base_PS->HdrFile_PS ); Base_PS->HdrFile_PS = NULL; fclose( Base_PS->TxtFile_PS ); Base_PS->TxtFile_PS = NULL; fclose( Base_PS->IdxFile_PS ); Base_PS->IdxFile_PS = NULL; return JAM_IO_ERROR; } return 0; }