From d1f8cc8478493eb35934039484d8d5fafae35bdd Mon Sep 17 00:00:00 2001 From: Przemyslaw Czerpak Date: Fri, 4 May 2007 13:36:02 +0000 Subject: [PATCH] 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() --- harbour/ChangeLog | 38 ++++++++++++ harbour/contrib/Makefile | 2 +- harbour/contrib/bmdbfcdx/Makefile | 4 +- harbour/include/hbapifs.h | 6 ++ harbour/source/rdd/Makefile | 2 +- harbour/source/rdd/dbcmd.c | 25 +++----- harbour/source/rdd/wafunc.c | 98 +++++++++++++------------------ harbour/source/rtl/filesys.c | 13 ++++ harbour/tests/tstuse.prg | 48 +++++++++++++++ 9 files changed, 158 insertions(+), 78 deletions(-) create mode 100644 harbour/tests/tstuse.prg 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