diff --git a/harbour/ChangeLog b/harbour/ChangeLog index ee1b107f2e..e42b16da0c 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -8,6 +8,44 @@ 2002-12-01 13:30 UTC+0100 Foo Bar */ +2007-05-04 15:35 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/contrib/Makefile + * harbour/contrib/bmdbfcdx/Makefile + * harbour/source/rdd/Makefile + ! fixed GNU make compilation + + * harbour/include/hbapifs.h + * harbour/source/rtl/filesys.c + + added support for DOS/Windows DENY_* flag emulation in POSIX + systems using BSD locks. So far such emulatin was done with + standard POSIX locks. The new emulation can be enabled by setting + HB_USE_BSDLOCKS and disabled by HB_USE_BSDLOCKS_OFF. By default + it's enabled in Linux and BSD* based systems. + Please not that it BSD locks work in differ way then POSIX ones. + They are bound with file handle and process ID not i-node and PID. + It will allow to synchronize even single process but it will not + work in NFS and some old BSD and Linux kernels. This synchronization + cannot be used also between different platforms. If someone needs + such synchronization then he should build Harbour on all platforms + with HB_USE_BSDLOCKS_OFF and HB_USE_SHARELOCKS with valid multi + platform lock offset + + * harbour/source/rdd/dbcmd.c + * removed all explicit NETERR setting - now it can be set only + by NETERR() function (executed by default errorsys) + * changed ordListAdd()/dbSetIndex() - now they return logical value + with information about success + + * harbour/source/rdd/wafunc.c + * changed hb_rddOpenTable()/hb_rddCreateTable() to make dbUseArea()/ + dbCreate() more Clipper compatible - it's not exact Clipper behavior + but I intentionally do not want to replicate some strange for me + things, f.e. I do not know why Clipper ignores RDDNAME when it has + only one character + + + harbour/tests/tstuse.prg + + test code for dbUseArea() + 2007-05-04 11:06 UTC+0100 Miguel Angel Marchuet (miguelangel/at/marchuet.net) * source/rdd/wafunc.c * change of the behavior of DbUseArea opening, so that one behaves like made Clipper, when it fails the diff --git a/harbour/contrib/Makefile b/harbour/contrib/Makefile index ccf9e03751..0b907a3ea8 100644 --- a/harbour/contrib/Makefile +++ b/harbour/contrib/Makefile @@ -12,7 +12,7 @@ DIRS=\ libmisc \ libnf \ samples \ - bmdbfcdx \ + bmdbfcdx \ ifeq ($(HB_ARCHITECTURE),w32) diff --git a/harbour/contrib/bmdbfcdx/Makefile b/harbour/contrib/bmdbfcdx/Makefile index e4a6aefb15..8d8f8067cc 100644 --- a/harbour/contrib/bmdbfcdx/Makefile +++ b/harbour/contrib/bmdbfcdx/Makefile @@ -2,11 +2,11 @@ # $Id$ # -ROOT = ../../../ +ROOT = ../../ C_SOURCES=\ bmdbfcdx1.c \ - bmsixcdx1.c + bmsixcdx1.c LIBNAME=bmdbfcdx diff --git a/harbour/include/hbapifs.h b/harbour/include/hbapifs.h index 8459dd4da7..261d3a69a4 100644 --- a/harbour/include/hbapifs.h +++ b/harbour/include/hbapifs.h @@ -152,6 +152,12 @@ extern HB_EXPORT FHANDLE hb_fsGetOsHandle( FHANDLE hFileHandle ); # define HB_USE_SHARELOCKS # define HB_SHARELOCK_POS 0x7fffffffUL # define HB_SHARELOCK_SIZE 0x1UL +# if defined( HB_USE_BSDLOCKS_OFF ) +# undef HB_USE_BSDLOCKS +# elif ( defined( HB_OS_LINUX ) || defined( HB_OS_BSD ) ) && \ + !defined( __WATCOMC__ ) && !defined( HB_USE_BSDLOCKS ) +# define HB_USE_BSDLOCKS +# endif #endif #define HB_MAX_DRIVE_LENGTH 10 diff --git a/harbour/source/rdd/Makefile b/harbour/source/rdd/Makefile index b2022c3284..11a055ecb8 100644 --- a/harbour/source/rdd/Makefile +++ b/harbour/source/rdd/Makefile @@ -37,7 +37,7 @@ LIBNAME=rdd # The list of all valid DB drivers is defined in config/global.cf. -DIRS=$(HB_DB_DRIVERS) +DIRS=nulsys $(HB_DB_DRIVERS) include $(TOP)$(ROOT)config/lib.cf include $(TOP)$(ROOT)config/dir.cf diff --git a/harbour/source/rdd/dbcmd.c b/harbour/source/rdd/dbcmd.c index e632d22560..dfff8bcac0 100644 --- a/harbour/source/rdd/dbcmd.c +++ b/harbour/source/rdd/dbcmd.c @@ -269,17 +269,10 @@ HB_FUNC( DBAPPEND ) BOOL bUnLockAll = ISLOG( 1 ) ? hb_parl( 1 ) : TRUE; ERRCODE errCode; - /* Clipper clears NETERR flag when table is open */ + /* Clipper clears NETERR flag before APPEND */ hb_rddSetNetErr( FALSE ); errCode = SELF_APPEND( pArea, bUnLockAll ); hb_retl( errCode == SUCCESS ); - - /* - * Warning: this is not Clipper compatible. NETERR() should be set by - * error handler not here - */ - if( errCode != SUCCESS ) - hb_rddSetNetErr( TRUE ); } else hb_errRT_DBCMD( EG_NOTABLE, EDBCMD_NOTABLE, NULL, "DBAPPEND" ); @@ -361,7 +354,7 @@ HB_FUNC( DBCREATE ) #ifdef HB_C52_STRICT hb_arrayLen( pStruct ) == 0 || #endif - !szFileName || !szFileName[ 0 ] ) + !szFileName ) { hb_errRT_DBCMD( EG_ARG, EDBCMD_DBCMDBADPARAMETER, NULL, "DBCREATE" ); return; @@ -1765,7 +1758,7 @@ HB_FUNC( ORDLISTADD ) DBORDERINFO pOrderInfo; ERRCODE errCode; - /* Clipper clears NETERR flag when table is open */ + /* Clipper clears NETERR flag when index is open */ hb_rddSetNetErr( FALSE ); memset( &pOrderInfo, 0, sizeof( DBORDERINFO ) ); @@ -1783,14 +1776,12 @@ HB_FUNC( ORDLISTADD ) errCode = SELF_ORDLSTADD( pArea, &pOrderInfo ); - /* - * Warning: this is not Clipper compatible. NETERR() should be set by - * error handler not here - */ - if( errCode != SUCCESS ) - hb_rddSetNetErr( TRUE ); + if( HB_IS_NIL( pOrderInfo.itmResult ) ) + hb_retl( errCode == SUCCESS ); + else + hb_itemReturn( pOrderInfo.itmResult ); - hb_itemRelease( hb_itemReturn( pOrderInfo.itmResult ) ); + hb_itemRelease( pOrderInfo.itmResult ); } else hb_errRT_DBCMD( EG_NOTABLE, EDBCMD_NOTABLE, NULL, "ORDLISTADD" ); diff --git a/harbour/source/rdd/wafunc.c b/harbour/source/rdd/wafunc.c index 826b334a89..b0698f897f 100644 --- a/harbour/source/rdd/wafunc.c +++ b/harbour/source/rdd/wafunc.c @@ -578,47 +578,57 @@ ERRCODE hb_rddOpenTable( const char * szFileName, const char * szDriver, char szDriverBuffer[ HARBOUR_MAX_RDD_DRIVERNAME_LENGTH + 1 ]; DBOPENINFO pInfo; ERRCODE errCode; - //USHORT uiPrevArea; AREAP pArea; - /* Clipper clears NETERR flag before RT error below */ + /* uiArea = 0 in hb_rddInsertAreaNode() means chose first + * available free area, otherwise we should close table in + * current WA and it should be done before parameter validation + * RT errors below. This breaks xHarbour like MT code which + * shares WA between threads so dbUseArea() should be covered + * by external mutex to make lNewArea MT safe, [druzus] + */ + if( uiArea ) + { + hb_rddSelectWorkAreaNumber( uiArea ); + hb_rddReleaseCurrentArea(); + } + else + hb_rddSelectFirstAvailable(); + + /* Clipper clears NETERR flag before parameter validation, [druzus] + */ hb_rddSetNetErr( FALSE ); - if( !szFileName || !szFileName[ 0 ] ) - { - hb_errRT_DBCMD( EG_ARG, EDBCMD_USE_BADPARAMETER, NULL, "DBUSEAREA" ); - return FAILURE; - } - + /* Now check parameters, first RDD name. + * Clipper seems to make sth like: + * if( szDriver && strlen( szDriver ) > 1 ) + * but I do not think we should replicate it, [druzus] + */ if( szDriver && szDriver[ 0 ] ) { hb_strncpyUpper( szDriverBuffer, szDriver, HARBOUR_MAX_RDD_DRIVERNAME_LENGTH ); szDriver = szDriverBuffer; } else - { szDriver = hb_rddDefaultDrv( NULL ); - } - //uiPrevArea = hb_rddGetCurrentWorkAreaNumber(); - - /* - * 0 means chose first available in hb_rddInsertAreaNode() - * This hack is necessary to avoid race condition in MT - * if we don't want to lock whole RDD subsystem, Druzus - */ - hb_rddSelectWorkAreaNumber( uiArea ); - if( uiArea ) - { - hb_rddReleaseCurrentArea(); - } - - /* Create a new WorkArea node */ + /* First try to create new are node and validate RDD name */ if( ! hb_rddInsertAreaNode( szDriver ) ) { hb_errRT_DBCMD( EG_ARG, EDBCMD_BADPARAMETER, NULL, "DBUSEAREA" ); return FAILURE; } + + /* Then check if valid file name was given - Clipper allows to use empty + * ("") file name + */ + if( !szFileName ) + { + hb_rddReleaseCurrentArea(); + hb_errRT_DBCMD( EG_ARG, EDBCMD_USE_BADPARAMETER, NULL, "DBUSEAREA" ); + return FAILURE; + } + pArea = ( AREAP ) hb_rddGetCurrentWorkAreaPointer(); /* Fill pInfo structure */ @@ -631,42 +641,18 @@ ERRCODE hb_rddOpenTable( const char * szFileName, const char * szDriver, pInfo.ulConnection = ulConnection; pInfo.lpdbHeader = NULL; - if( pStruct ) - { - errCode = SELF_CREATEFIELDS( pArea, pStruct ); - } - else - { - errCode = SUCCESS; - } - + errCode = pStruct ? SELF_CREATEFIELDS( pArea, pStruct ) : SUCCESS; if( errCode == SUCCESS ) { if( pDelim && !HB_IS_NIL( pDelim ) ) errCode = SELF_INFO( pArea, DBI_SETDELIMITER, pDelim ); - if( errCode == SUCCESS ) - { /* Open file */ errCode = SELF_OPEN( pArea, &pInfo ); - /*-----------------04/05/2007 11:00----------------- - * Clipper not restore the old workarea - * --------------------------------------------------*/ - /* - if( errCode != SUCCESS ) - { - hb_rddReleaseCurrentArea(); - hb_rddSelectWorkAreaNumber( uiPrevArea ); - }*/ - } } - /* - * Warning: this is not Clipper compatible. NETERR() should be set by - * error handler not here - */ if( errCode != SUCCESS ) - hb_rddSetNetErr( TRUE ); + hb_rddReleaseCurrentArea(); return errCode; } @@ -683,7 +669,7 @@ ERRCODE hb_rddCreateTable( const char * szFileName, const char * szDriver, USHORT uiPrevArea; AREAP pArea; - if( !szFileName || !szFileName[ 0 ] ) + if( !szFileName ) { hb_errRT_DBCMD( EG_ARG, EDBCMD_DBCMDBADPARAMETER, NULL, "DBCREATE" ); return FAILURE; @@ -695,27 +681,25 @@ ERRCODE hb_rddCreateTable( const char * szFileName, const char * szDriver, szDriver = szDriverBuffer; } else - { szDriver = hb_rddDefaultDrv( NULL ); - } uiPrevArea = hb_rddGetCurrentWorkAreaNumber(); /* * 0 means chose first available in hb_rddInsertAreaNode() * This hack is necessary to avoid race condition in MT - * if we don't want to lock whole RDD subsystem, Druzus + * mode where workareas are shared between threads and + * we don't want to lock whole RDD subsystem, [druzus] */ hb_rddSelectWorkAreaNumber( uiArea ); if( uiArea ) - { hb_rddReleaseCurrentArea(); - } /* Create a new WorkArea node */ if( ! hb_rddInsertAreaNode( szDriver ) ) { - hb_errRT_DBCMD( EG_CREATE, EDBCMD_BADPARAMETER, NULL, "DBCREATE" ); + hb_rddSelectWorkAreaNumber( uiPrevArea ); + hb_errRT_DBCMD( EG_ARG, EDBCMD_BADPARAMETER, NULL, "DBCREATE" ); return FAILURE; } pArea = ( AREAP ) hb_rddGetCurrentWorkAreaPointer(); diff --git a/harbour/source/rtl/filesys.c b/harbour/source/rtl/filesys.c index 6cd63b51cf..922a32792d 100644 --- a/harbour/source/rtl/filesys.c +++ b/harbour/source/rtl/filesys.c @@ -185,6 +185,9 @@ #define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif #endif +#if defined( HB_USE_SHARELOCKS ) && defined( HB_USE_BSDLOCKS ) + #include +#endif /* 27/08/2004 - HB_FS_GETDRIVE() should return a number in the range 0..25 ('A'..'Z') @@ -2230,6 +2233,15 @@ HB_EXPORT FHANDLE hb_fsExtOpen( BYTE * pFilename, BYTE * pDefExt, #if defined( HB_USE_SHARELOCKS ) if( hFile != FS_ERROR && uiExFlags & FXO_SHARELOCK ) { +#if defined( HB_USE_BSDLOCKS ) + int iLock; + if( ( uiFlags & ( FO_READ | FO_WRITE | FO_READWRITE ) ) == FO_READ || + ( uiFlags & ( FO_DENYREAD | FO_DENYWRITE | FO_EXCLUSIVE ) ) == 0 ) + iLock = LOCK_SH | LOCK_NB; + else + iLock = LOCK_EX | LOCK_NB; + if( flock( hFile, iLock ) != 0 ) +#else USHORT uiLock; if( ( uiFlags & ( FO_READ | FO_WRITE | FO_READWRITE ) ) == FO_READ || ( uiFlags & ( FO_DENYREAD | FO_DENYWRITE | FO_EXCLUSIVE ) ) == 0 ) @@ -2238,6 +2250,7 @@ HB_EXPORT FHANDLE hb_fsExtOpen( BYTE * pFilename, BYTE * pDefExt, uiLock = FL_LOCK | FLX_EXCLUSIVE; if( !hb_fsLockLarge( hFile, HB_SHARELOCK_POS, HB_SHARELOCK_SIZE, uiLock ) ) +#endif { hb_fsClose( hFile ); hFile = FS_ERROR; diff --git a/harbour/tests/tstuse.prg b/harbour/tests/tstuse.prg new file mode 100644 index 0000000000..ea8274ac62 --- /dev/null +++ b/harbour/tests/tstuse.prg @@ -0,0 +1,48 @@ +/* + * $Id$ + */ + +#define EOL chr(13)+chr(10) +#command ? [] => outstd(EOL)[;outstd()] +proc main() + ? OS(), VERSION() + if !file("_tst.dbf") + dbCreate("_tst",{{"F1","C",1,0}}) + endif + if !file("_tst2.dbf") + dbCreate("_tst2",{{"F1","C",1,0}}) + endif + + USE _tst NEW ALIAS "ONE" EXCLUSIVE + ? select(), alias(), netErr(), used() + ? + + mkTest( .T., "NORDD",, "TWO", .T., .F. ) + mkTest( .T., "DBF",, "TWO", .T., .F. ) + mkTest( .T., "DBF", "", "TWO", .T., .F. ) + mkTest( .T., "DBF", "nofile", "TWO", .T., .F. ) + mkTest( .T., "DBF", "_tst2", "ONE", .T., .F. ) + mkTest( .T., "DBF", "_tst", "ONE", .T., .F. ) + mkTest( .T., "DBF", "_tst", "TWO", .T., .F. ) + ? + dbUseArea( .T., "DBF", "_tst", "ONE", .T., .F. ) + ? select(), alias(), netErr(), used() + dbUseArea( .T., "DBF", "_tst", "TWO", .T., .F. ) + ? select(), alias(), netErr(), used() + ? + dbSelectArea( 1 ) + mkTest( .F., "NORDD",, "TWO", .T., .F. ) + ? +return + +proc mkTest( lNewArea, cRdd, cFile, cAlias, lShared, lReadOnly ) +local cbErr:=errorBlock({|oErr|break(oErr)}), oErr +netErr(.f.) +begin sequence + dbUseArea( lNewArea, cRdd, cFile, cAlias, lShared, lReadOnly ) +recover using oErr + ? "Error:", oErr:subCode, oErr:description, oErr:operation, oErr:osCode +end +? select(), alias(), netErr(), used() +errorBlock(cbErr) +return