diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 9c70038f4e..a576fd93ef 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -17,6 +17,96 @@ past entries belonging to author(s): Viktor Szakats. */ +2009-09-01 00:56 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/harbour.spec + * harbour/harbour-win-spec + * harbour/harbour-wce-spec + * harbour/bin/hb-func.sh + * harbour/contrib/Makefile + + harbour/contrib/hbnetio + + harbour/contrib/hbnetio/netio.h + + harbour/contrib/hbnetio/netiomt.prg + + harbour/contrib/hbnetio/netiocli.c + + harbour/contrib/hbnetio/netiosrv.c + + harbour/contrib/hbnetio/Makefile + + added new library: HBNETIO. + It implements alternative RDD IO API which uses own TCP/IP sockets + to exchange data between client and server. + This library contains client and server code and is fully MT safe. + On client side it's enough to execute: + NETIO_CONNECT( [], [], [] ) -> + function to register alternative NETIO RDD API and set default + server address and port. + - server addres (default 127.0.0.1) + - server port (default 2941) + - connection timeout (default -1 - not timeout) + Above settings are thread local and parameters of the 1-st successful + connection are used as default values for each new thread. + After registering NETIO client by above function each file starting + "net:" prefix is automatically redirected to given NETIO server, i.e. + use "net:mytable" + It's also possible to pass NETIO server and port as part of file name, + i.e.: + use "net:192.168.0.1:10005:mytable" + On the server side the following functions are available: + create NETIO listen socket: + NETIO_LISTEN( [], [], [] ) + -> | NIL + accept new connection on NETIO listen socket: + NETIO_ACCEPT( [, ] ) + -> | NIL + start connection server: + NETIO_SERVER( ) -> NIL + stop connection accepting or connection server: + NETIO_SERVERSTOP( | , ) + -> NIL + activate MT NETIO server (it needs MT HVM): + NETIO_MTSERVER( [], [] ) -> | NIL + + To create NETIO server is enough to compile and link with MT HVM + this code: + + proc main() + local pListenSocket + + pListenSocket := netio_mtserver() + if empty( pListenSocket ) + ? "Cannot start server." + else + wait "Press any key to stop NETIO server." + netio_serverstop( pListenSocket ) + pListenSocket := NIL + endif + return + + NETIO works with all core RDDs (DBF, DBFFPT, DBFBLOB, DBFNTX, DBFCDX, + DBFNSX, SDF, DELIM) and any other RDD which inherits from above or + use standard RDD IO API (hb_file*() functions). + Without touching even single line in RDD code it gives the same + functionality as REDBFCDX in xHarbour but for all RDDs. + It's possible that such direct TCP/IP connection is faster then + file server protocols especially if they need more then one IP frame + to exchange data so it's one of the reason to use it in such cases. + Please make real speed tests. + The second reason to use NETIO server is resolving problem with + concurrent access to the same files using Harbour applications + compiled for different platforms, i.e. DOS, LINUX, Windows and OS2. + It's very hard to configure all client stations to use correct + locking system. NETIO fully resolves this problem so it can be + interesting alternative also for MS-Windows users only if they + do not want to play with oplocks setting on each station. + I'm interesting in user opinions about real life NETIO usage. + + Have a fun with this new toy ;-) + + + harbour/contrib/hbnetio/tests + + harbour/contrib/hbnetio/tests/netiotst.prg + + harbour/contrib/hbnetio/tests/netiotst.hbp + + harbour/contrib/hbnetio/tests/data + + added simple test code for NETIO. It activates NETIO MT server + and then connects to this server to create and browse table with + memo file and production index with few tags. + 2009-08-31 22:09 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl) * harbour/source/rdd/sdf1.c * harbour/source/rdd/delim1.c diff --git a/harbour/bin/hb-func.sh b/harbour/bin/hb-func.sh index e104f3c061..0d42f822b8 100755 --- a/harbour/bin/hb-func.sh +++ b/harbour/bin/hb-func.sh @@ -110,7 +110,7 @@ mk_hbgetlibsctb() then libs="$libs gtwin" fi - echo "$libs hbct hbnf hbmzip hbtip xhb hbgd hbfimage rddsql sddfb sddmy sddpg hbodbc hbpgsql hbmysql hbfbird rddads rddado hbhpdf hbvpdf hbcurl hbwin gtwvg gtalleg hbsqlit3 hbbtree $HB_USER_LIBS" + echo "$libs hbct hbnf hbmzip hbnetio hbtip xhb hbgd hbfimage rddsql sddfb sddmy sddpg hbodbc hbpgsql hbmysql hbfbird rddads rddado hbhpdf hbvpdf hbcurl hbwin gtwvg gtalleg hbsqlit3 hbbtree $HB_USER_LIBS" #"hbgf hbgt hbbmcdx hbmisc hbtpathy hbwhat hbziparc hbmsql" else echo "$@" diff --git a/harbour/contrib/Makefile b/harbour/contrib/Makefile index 371f2db1b7..43b8162855 100644 --- a/harbour/contrib/Makefile +++ b/harbour/contrib/Makefile @@ -17,6 +17,7 @@ DIRS := \ hbgt \ hbmisc \ hbmzip \ + hbnetio \ hbnf \ hbodbc \ hbsqlit3 \ diff --git a/harbour/contrib/hbnetio/Makefile b/harbour/contrib/hbnetio/Makefile new file mode 100644 index 0000000000..c504c6ce7d --- /dev/null +++ b/harbour/contrib/hbnetio/Makefile @@ -0,0 +1,16 @@ +# +# $Id$ +# + +ROOT := ../../ + +LIBNAME := hbnetio + +C_SOURCES := \ + netiosrv.c \ + netiocli.c \ + +PRG_SOURCES := \ + netiomt.prg \ + +include $(TOP)$(ROOT)config/lib.mk diff --git a/harbour/contrib/hbnetio/netio.h b/harbour/contrib/hbnetio/netio.h new file mode 100644 index 0000000000..c58dd3a724 --- /dev/null +++ b/harbour/contrib/hbnetio/netio.h @@ -0,0 +1,112 @@ +/* + * $Id$ + */ + +/* + * Harbour Project source code: + * demonstration code for alternative RDD IO API which uses own + * very simple TCP/IP file server. + * All files which names starts with 'net:' are redirected to this API. + * This is header file used by client and server code with some constant + * definitions. + * + * Copyright 2009 Przemyslaw Czerpak + * www - http://www.harbour-project.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +/* file name prefix used by this file IO implementation */ +#define NETIO_FILE_PREFIX "net:" +#define NETIO_FILE_PREFIX_LEN strlen( NETIO_FILE_PREFIX ) + +/* default server address, port and timeout */ +#define NETIO_DEFAULT_SERVER "127.0.0.1" +#define NETIO_DEFAULT_PORT 2941 +#define NETIO_DEFAULT_TIMEOUT -1 + +/* message size */ +#define NETIO_MSGLEN 24 + +/* maximal number of open files per connection */ +#define NETIO_FILES_MAX 8192 + +/* maximal length of server name */ +#define NETIO_SERVERNAME_MAX 256 + +/* login string */ +#define NETIO_LOGINSTRID "HarbourFileTcpIpServer\003" + +/* messages */ +#define NETIO_LOGIN 1 +#define NETIO_EXISTS 2 +#define NETIO_DELETE 3 +#define NETIO_COMMIT 4 +#define NETIO_SIZE 5 +#define NETIO_TRUNC 6 +#define NETIO_READ 7 +#define NETIO_WRITE 8 +#define NETIO_LOCK 9 +#define NETIO_OPEN 10 +#define NETIO_CLOSE 11 +#define NETIO_ERROR 12 +#define NETIO_CONNECTED 0x4321DEAD + +/* messages format */ +/* { NETIO_LOGIN, len[ 2 ]... } + loginstr[ len ] -> { NETIO_LOGIN, NETIO_CONNECTED[ 4 ], ... } */ +/* { NETIO_EXISTS, len[ 2 ]... } + filename[ len ] -> { NETIO_EXISTS, ... } */ +/* { NETIO_DELETE, len[ 2 ]... } + filename[ len ] -> { NETIO_DELETE, ... } */ +/* { NETIO_OPEN, len[ 2 ], flags[ 2 ], def_ext[], 0, ... } + filename[ len ] -> { NETIO_OPEN, file_no[2], ... } */ +/* { NETIO_READ, file_no[2], size[ 4 ], offset[ 8 ], ... } -> { NETIO_READ, read[ 4 ], err[ 2 ], ... } + data[ read ] */ +/* { NETIO_WRITE, file_no[2], size[ 4 ], offset[ 8 ], ... } + data[ size ] -> { NETIO_WRITE, written[ 4 ], err[ 2 ], ... } */ +/* { NETIO_LOCK, file_no[2], start[ 8 ], len[ 8 ], flags[ 2 ], ... } -> { NETIO_LOCK, ... } */ +/* { NETIO_TRUNC, file_no[2], offset[ 8 ], ... } -> { NETIO_TRUNC, ... } */ +/* { NETIO_SIZE, file_no[2], ... } -> { NETIO_SIZE, size[ 8 ], err[ 2 ], ... } */ +/* { NETIO_COMMIT, file_no[2], ... } -> NULL */ +/* { NETIO_CLOSE, file_no[2], ... } -> { NETIO_CLOSE, ... } */ + +/* netio errors */ +#define NETIO_ERR_UNKNOWN_COMMAND 0xff01 +#define NETIO_ERR_WRONG_PARAM 0xff02 +#define NETIO_ERR_WRONG_FILE_PATH 0xff03 +#define NETIO_ERR_WRONG_FILE_HANDLE 0xff04 +#define NETIO_ERR_FILES_MAX 0xff05 +#define NETIO_ERR_READ 0xff06 diff --git a/harbour/contrib/hbnetio/netiocli.c b/harbour/contrib/hbnetio/netiocli.c new file mode 100644 index 0000000000..661368c0e4 --- /dev/null +++ b/harbour/contrib/hbnetio/netiocli.c @@ -0,0 +1,791 @@ +/* + * $Id$ + */ + +/* + * Harbour Project source code: + * demonstration code for alternative RDD IO API which uses own + * very simple TCP/IP file server. + * All files which names starts 'net:' are redirected to this API. + * This is client code with + * NETIO_CONNECT( [], [], [] ) -> + * function which register alternative RDD IO API, sets server + * address and port and connection timeout parameter. + * Then it tries to connect to the server and returns .T. on success. + * + * Copyright 2009 Przemyslaw Czerpak + * www - http://www.harbour-project.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +/* this has to be declared before hbapifs.h is included */ +#define _HB_FILE_IMPLEMENTATION_ + +#include "hbapi.h" +#include "hbapifs.h" +#include "hbapiitm.h" +#include "hbapierr.h" +#include "hbsocket.h" +#include "hbinit.h" +#include "hbvm.h" +#include "hbstack.h" +#include "hbthread.h" +#include "netio.h" + +/* + * client code + */ + +typedef struct _HB_CONCLI +{ + HB_COUNTER used; + PHB_ITEM mutex; + int timeout; + int port; + HB_SOCKET sd; + struct _HB_CONCLI * next; + char server[ 1 ]; +} +HB_CONCLI, * PHB_CONCLI; + +typedef struct _HB_FILE +{ + const HB_FILE_FUNCS * pFuncs; + PHB_CONCLI conn; + USHORT fd; +} +HB_FILE; + +typedef struct +{ + int timeout; + int port; + char server[ NETIO_SERVERNAME_MAX ]; +} HB_CONDATA, * PHB_CONDATA; + +/* MT macros */ +#define HB_NETIO_LOCK hb_threadEnterCriticalSection( &s_netioMtx ); +#define HB_NETIO_UNLOCK hb_threadLeaveCriticalSection( &s_netioMtx ); +static HB_CRITICAL_NEW( s_netioMtx ); + +static HB_TSD_NEW( s_conData, sizeof( HB_CONDATA ), NULL, NULL ); + +static PHB_CONCLI s_connections = NULL; + +static BOOL s_defaultInit = TRUE; +static char s_defaultServer[ NETIO_SERVERNAME_MAX ] = NETIO_DEFAULT_SERVER; +static int s_defaultPort = NETIO_DEFAULT_PORT; +static int s_defaultTimeOut = NETIO_DEFAULT_TIMEOUT; + +static const HB_FILE_FUNCS * s_fileMethods( void ); + +static long s_fileRecvAll( PHB_CONCLI conn, void * buffer, long len ) +{ + BYTE * ptr = ( BYTE * ) buffer; + long lRead = 0, l; + + while( lRead < len ) + { + l = hb_socketRecv( conn->sd, ptr + lRead, len - lRead, 0, -1 ); + if( l <= 0 ) + break; + lRead += l; + } + return lRead; +} + +static BOOL s_fileSendMsg( PHB_CONCLI conn, BYTE * msgbuf, + const void * data, long len, BOOL fWait ) +{ + BYTE buffer[ 2048 ]; + BYTE * msg, * ptr = NULL; + LONG lSent = 0, l; + BOOL fResult = FALSE; + + if( len == 0 ) + { + msg = msgbuf; + len = NETIO_MSGLEN; + } + else + { + len += NETIO_MSGLEN; + if( len > ( long ) sizeof( buffer ) ) + msg = ptr = ( BYTE * ) hb_xgrab( len ); + else + msg = buffer; + memcpy( msg, msgbuf, NETIO_MSGLEN ); + memcpy( msg + NETIO_MSGLEN, data, len - NETIO_MSGLEN ); + } + + while( lSent < len ) + { + l = hb_socketSend( conn->sd, msg + lSent, len - lSent, 0, -1 ); + if( l <= 0 ) + break; + lSent += l; + } + + if( ptr ) + hb_xfree( ptr ); + + if( lSent == len ) + { + if( fWait ) + { + int iMsg = HB_GET_LE_INT32( msgbuf ); + + if( s_fileRecvAll( conn, msgbuf, NETIO_MSGLEN ) == NETIO_MSGLEN ) + { + int iResult = HB_GET_LE_INT32( msgbuf ); + + if( iResult == NETIO_ERROR ) + hb_fsSetError( HB_GET_LE_UINT16( &msgbuf[ 4 ] ) ); + else if( iResult == iMsg ) + fResult = TRUE; + } + } + else + fResult = TRUE; + } + + return fResult; +} + +static PHB_CONCLI s_fileConNew( HB_SOCKET sd, const char * pszServer, + int iPort, int iTimeOut ) +{ + PHB_CONCLI conn; + int iLen; + + iLen = ( int ) strlen( pszServer ); + conn = ( PHB_CONCLI ) hb_xgrab( sizeof( HB_CONCLI ) + iLen ); + hb_atomic_set( &conn->used, 1 ); + conn->mutex = hb_threadMutexCreate( FALSE ); + conn->sd = sd; + conn->next = NULL; + conn->timeout = iTimeOut; + conn->port = iPort; + memcpy( conn->server, pszServer, iLen + 1 ); + + return conn; +} + +static void s_fileConFree( PHB_CONCLI conn ) +{ + hb_socketShutdown( conn->sd, HB_SOCKET_SHUT_RDWR ); + hb_socketClose( conn->sd ); + if( conn->mutex ) + hb_itemRelease( conn->mutex ); + hb_xfree( conn ); +} + +static void s_fileConRegister( PHB_CONCLI conn ) +{ + PHB_CONCLI * connPtr; + + HB_NETIO_LOCK + connPtr = &s_connections; + while( *connPtr ) + connPtr = &( *connPtr )->next; + *connPtr = conn; + HB_NETIO_UNLOCK +} + +static void s_fileConClose( PHB_CONCLI conn ) +{ + if( hb_atomic_dec( &conn->used ) ) + { + HB_NETIO_LOCK + if( hb_atomic_get( &conn->used ) == 0 ) + { + PHB_CONCLI * connPtr = &s_connections; + while( *connPtr ) + { + if( *connPtr == conn ) + { + *connPtr = conn->next; + break; + } + connPtr = &( *connPtr )->next; + } + } + else + conn = NULL; /* reused by other thread */ + HB_NETIO_UNLOCK + + if( conn ) + s_fileConFree( conn ); + } +} + +static PHB_CONCLI s_fileConFind( const char * pszServer, int iPort ) +{ + PHB_CONCLI conn; + + HB_NETIO_LOCK + conn = s_connections; + while( conn ) + { + if( conn->port == iPort && hb_stricmp( conn->server, pszServer ) == 0 ) + { + hb_atomic_inc( &conn->used ); + break; + } + conn = conn->next; + } + HB_NETIO_UNLOCK + + return conn; +} + +static BOOL s_fileConLock( PHB_CONCLI conn ) +{ + return !conn->mutex || hb_threadMutexLock( conn->mutex ); +} + +static void s_fileConUnlock( PHB_CONCLI conn ) +{ + if( conn->mutex ) + hb_threadMutexUnlock( conn->mutex ); +} + +static PHB_CONCLI s_fileConnect( const char ** pszFilename, + const char * pszServer, + int iPort, int iTimeOut ) +{ + PHB_CONCLI conn = NULL; + HB_SOCKET sd; + PHB_CONDATA pConData = ( PHB_CONDATA ) hb_stackGetTSD( &s_conData ); + char server[ NETIO_SERVERNAME_MAX ]; + + if( pConData->port ) + { + pszServer = pConData->server; + iPort = pConData->port; + iTimeOut = pConData->timeout; + } + else + { + if( !pszServer ) + pszServer = s_defaultServer; + if( !iPort ) + iPort = s_defaultPort; + if( iTimeOut == 0 || iTimeOut < -1 ) + iTimeOut = s_defaultTimeOut; + } + + if( pszFilename ) + { + /* decode server address and port if given as part of file name + * in format like: + * "192.168.0.1:2941:path/to/file" + */ + const char * psz = strchr( *pszFilename, ':' ); + if( psz ) + { + int iLen = ( int ) ( psz - *pszFilename ); + + if( iLen == 0 || iLen > 1 ) + { + char port_buf[ 10 ]; + if( iLen >= ( int ) sizeof( server ) ) + iLen = ( int ) sizeof( server ) - 1; + if( iLen > 0 ) + { + hb_strncpy( server, *pszFilename, iLen ); + pszServer = server; + } + *pszFilename = psz + 1; + iLen = 0; + while( HB_ISDIGIT( ( *pszFilename )[ iLen ] ) && + iLen < ( int ) sizeof( port_buf ) - 1 ) + { + port_buf[ iLen ] = ( *pszFilename )[ iLen ]; + ++iLen; + } + if( ( *pszFilename )[ iLen ] == ':' ) + { + if( iLen == 0 ) + ++( *pszFilename ); + else if( iLen > 0 ) + { + int iOverflow; + HB_LONG llPort; + + port_buf[ iLen ] = '\0'; + llPort = hb_strValInt( port_buf, &iOverflow ); + + if( !iOverflow && llPort > 0 && llPort < 0x10000 ) + { + *pszFilename += iLen + 1; + iPort = ( int ) llPort; + } + } + } + } + } + } + + conn = s_fileConFind( pszServer, iPort ); + if( conn == NULL ) + { + sd = hb_socketOpen( HB_SOCKET_PF_INET, HB_SOCKET_PT_STREAM, 0 ); + if( sd != HB_NO_SOCKET ) + { + void * pSockAddr; + unsigned uiLen; + + if( hb_socketInetAddr( &pSockAddr, &uiLen, pszServer, iPort ) ) + { + hb_socketSetKeepAlive( sd, TRUE ); + if( hb_socketConnect( sd, pSockAddr, uiLen, iTimeOut ) == 0 ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + UINT16 len = ( UINT16 ) strlen( NETIO_LOGINSTRID ); + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_LOGIN ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], len ); + memset( msgbuf + 6, '\0', sizeof( msgbuf ) - 6 ); + + conn = s_fileConNew( sd, pszServer, iPort, iTimeOut ); + sd = HB_NO_SOCKET; + + if( !s_fileSendMsg( conn, msgbuf, NETIO_LOGINSTRID, len, TRUE ) || + HB_GET_LE_UINT32( &msgbuf[ 4 ] ) != NETIO_CONNECTED ) + { + s_fileConFree( conn ); + conn = NULL; + } + else + s_fileConRegister( conn ); + } + hb_xfree( pSockAddr ); + } + if( !conn && sd != HB_NO_SOCKET ) + hb_socketClose( sd ); + } + } + + if( conn != NULL && s_defaultInit ) + { + HB_NETIO_LOCK + if( s_defaultInit ) + { + if( pszServer != s_defaultServer ) + hb_strncpy( s_defaultServer, pszServer, sizeof( s_defaultServer ) - 1 ); + s_defaultPort = iPort; + s_defaultTimeOut = iTimeOut; + s_defaultInit = FALSE; + } + HB_NETIO_UNLOCK + } + + return conn; +} + +static void s_netio_exit( void* cargo ) +{ + HB_SYMBOL_UNUSED( cargo ); + + while( s_connections ) + { + PHB_CONCLI conn = s_connections; + s_connections = conn->next; + s_fileConFree( conn ); + } +} + +static void s_netio_init( void ) +{ + static BOOL s_fInit = TRUE; + + if( s_fInit ) + { + hb_fileRegister( s_fileMethods() ); + hb_vmAtQuit( s_netio_exit, NULL ); + s_fInit = FALSE; + } +} + +HB_FUNC( NETIO_CONNECT ) +{ + const char * pszServer = hb_parc( 1 ); + int iPort = hb_parni( 2 ), iTimeOut = hb_parni( 3 ); + PHB_CONCLI conn = NULL; + + s_netio_init(); + + conn = s_fileConnect( NULL, pszServer, iPort, iTimeOut ); + if( conn ) + { + PHB_CONDATA pConData = ( PHB_CONDATA ) hb_stackGetTSD( &s_conData ); + + pConData->timeout = conn->timeout; + pConData->port = conn->port; + hb_strncpy( pConData->server, conn->server, sizeof( pConData->server ) - 1 ); + + s_fileConClose( conn ); + } + + hb_retl( conn != NULL ); +} + +/* Client methods + */ +static BOOL s_fileAccept( const char * pFilename ) +{ + return hb_strnicmp( pFilename, NETIO_FILE_PREFIX, NETIO_FILE_PREFIX_LEN ) == 0; +} + +static BOOL s_fileExists( const char * pFilename, char * pRetPath ) +{ + BOOL fResult = FALSE; + PHB_CONCLI conn; + + if( pRetPath ) + hb_strncpy( pRetPath, pFilename, HB_PATH_MAX - 1 ); + + pFilename += NETIO_FILE_PREFIX_LEN; + + conn = s_fileConnect( &pFilename, NULL, 0, 0 ); + if( conn ) + { + if( s_fileConLock( conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + UINT16 len = ( UINT16 ) strlen( pFilename ); + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_EXISTS ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], len ); + memset( msgbuf + 6, '\0', sizeof( msgbuf ) - 6 ); + fResult = s_fileSendMsg( conn, msgbuf, pFilename, len, TRUE ); + s_fileConUnlock( conn ); + } + s_fileConClose( conn ); + } + + return fResult; +} + +static BOOL s_fileDelete( const char * pFilename ) +{ + BOOL fResult = FALSE; + PHB_CONCLI conn; + + pFilename += NETIO_FILE_PREFIX_LEN; + + conn = s_fileConnect( &pFilename, NULL, 0, 0 ); + if( conn ) + { + if( s_fileConLock( conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + UINT16 len = ( UINT16 ) strlen( pFilename ); + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_DELETE ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], len ); + memset( msgbuf + 6, '\0', sizeof( msgbuf ) - 6 ); + fResult = s_fileSendMsg( conn, msgbuf, pFilename, len, TRUE ); + s_fileConUnlock( conn ); + } + s_fileConClose( conn ); + } + + return fResult; +} + +static PHB_FILE s_fileOpen( const char * pFilename, const char * pDefExt, + USHORT uiExFlags, const char * pPaths, + PHB_ITEM pError ) +{ + PHB_FILE pFile = NULL; + PHB_CONCLI conn; + const char * pszFile = pFilename + NETIO_FILE_PREFIX_LEN; + + HB_SYMBOL_UNUSED( pPaths ); + + conn = s_fileConnect( &pszFile, NULL, 0, 0 ); + if( conn ) + { + if( s_fileConLock( conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + UINT16 len = ( UINT16 ) strlen( pszFile ); + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_OPEN ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], len ); + HB_PUT_LE_UINT16( &msgbuf[ 6 ], uiExFlags ); + memset( msgbuf + 8, '\0', sizeof( msgbuf ) - 8 ); + if( pDefExt ) + hb_strncpy( ( char * ) &msgbuf[ 8 ], + ( const char * ) pDefExt, sizeof( msgbuf ) - 9 ); + + if( s_fileSendMsg( conn, msgbuf, pszFile, len, TRUE ) ) + { + pFile = ( PHB_FILE ) hb_xgrab( sizeof( HB_FILE ) ); + pFile->pFuncs = s_fileMethods(); + pFile->conn = conn; + pFile->fd = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + } + s_fileConUnlock( conn ); + } + + if( !pFile ) + s_fileConClose( conn ); + } + + if( pError ) + { + hb_errPutFileName( pError, pFilename ); + if( pFile == NULL ) + { + hb_errPutOsCode( pError, hb_fsError() ); + hb_errPutGenCode( pError, ( USHORT ) ( ( uiExFlags & FXO_TRUNCATE ) ? EG_CREATE : EG_OPEN ) ); + } + } + + return pFile; +} + +static void s_fileClose( PHB_FILE pFile ) +{ + if( s_fileConLock( pFile->conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_CLOSE ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], pFile->fd ); + memset( msgbuf + 6, '\0', sizeof( msgbuf ) - 6 ); + s_fileSendMsg( pFile->conn, msgbuf, NULL, 0, TRUE ); + s_fileConUnlock( pFile->conn ); + } + s_fileConClose( pFile->conn ); + hb_xfree( pFile ); +} + +static BOOL s_fileLock( PHB_FILE pFile, HB_FOFFSET ulStart, HB_FOFFSET ulLen, + int iType ) +{ + BOOL fResult = FALSE; + + if( s_fileConLock( pFile->conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_LOCK ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], pFile->fd ); + HB_PUT_LE_UINT64( &msgbuf[ 6 ], ulStart ); + HB_PUT_LE_UINT64( &msgbuf[ 14 ], ulLen ); + HB_PUT_LE_UINT16( &msgbuf[ 22 ], ( USHORT ) iType ); + memset( msgbuf + 24, '\0', sizeof( msgbuf ) - 24 ); + + fResult = s_fileSendMsg( pFile->conn, msgbuf, NULL, 0, TRUE ); + s_fileConUnlock( pFile->conn ); + } + + return fResult; +} + +static ULONG s_fileReadAt( PHB_FILE pFile, void * data, ULONG ulSize, + HB_FOFFSET llOffset ) +{ + ULONG ulResult = 0; + + if( s_fileConLock( pFile->conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_READ ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], pFile->fd ); + HB_PUT_LE_UINT32( &msgbuf[ 6 ], ulSize ); + HB_PUT_LE_UINT64( &msgbuf[ 10 ], llOffset ); + memset( msgbuf + 18, '\0', sizeof( msgbuf ) - 18 ); + + if( s_fileSendMsg( pFile->conn, msgbuf, NULL, 0, TRUE ) ) + { + ulResult = HB_GET_LE_UINT32( &msgbuf[ 4 ] ); + if( ulResult > 0 ) + { + if( ulResult > ulSize ) /* error, it should not happen, enemy attack? */ + ulResult = ulSize; + ulResult = s_fileRecvAll( pFile->conn, data, ulResult ); + } + hb_fsSetError( HB_GET_LE_UINT16( &msgbuf[ 8 ] ) ); + } + s_fileConUnlock( pFile->conn ); + } + + return ulResult; +} + +static ULONG s_fileWriteAt( PHB_FILE pFile, const void * data, ULONG ulSize, + HB_FOFFSET llOffset ) +{ + ULONG ulResult = 0; + + if( s_fileConLock( pFile->conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_WRITE ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], pFile->fd ); + HB_PUT_LE_UINT32( &msgbuf[ 6 ], ulSize ); + HB_PUT_LE_UINT64( &msgbuf[ 10 ], llOffset ); + memset( msgbuf + 18, '\0', sizeof( msgbuf ) - 18 ); + + if( s_fileSendMsg( pFile->conn, msgbuf, data, ulSize, TRUE ) ) + { + ulResult = HB_GET_LE_UINT32( &msgbuf[ 4 ] ); + hb_fsSetError( HB_GET_LE_UINT16( &msgbuf[ 8 ] ) ); + } + s_fileConUnlock( pFile->conn ); + } + + return ulResult; +} + +static BOOL s_fileTruncAt( PHB_FILE pFile, HB_FOFFSET llOffset ) +{ + BOOL fResult = FALSE; + + if( s_fileConLock( pFile->conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_TRUNC ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], pFile->fd ); + HB_PUT_LE_UINT64( &msgbuf[ 6 ], llOffset ); + memset( msgbuf + 14, '\0', sizeof( msgbuf ) - 14 ); + + fResult = s_fileSendMsg( pFile->conn, msgbuf, NULL, 0, TRUE ); + s_fileConUnlock( pFile->conn ); + } + + return fResult; +} + +static HB_FOFFSET s_fileSize( PHB_FILE pFile ) +{ + HB_FOFFSET llOffset = 0; + + if( s_fileConLock( pFile->conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + + HB_PUT_LE_UINT32( msgbuf, NETIO_SIZE ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], pFile->fd ); + memset( msgbuf + 6, '\0', sizeof( msgbuf ) - 6 ); + + if( s_fileSendMsg( pFile->conn, msgbuf, NULL, 0, TRUE ) ) + { + llOffset = HB_GET_LE_UINT64( &msgbuf[ 4 ] ); + hb_fsSetError( HB_GET_LE_UINT16( &msgbuf[ 12 ] ) ); + } + s_fileConUnlock( pFile->conn ); + } + + return llOffset; +} + +static void s_fileCommit( PHB_FILE pFile ) +{ + if( s_fileConLock( pFile->conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + + HB_PUT_LE_UINT32( msgbuf, NETIO_COMMIT ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], pFile->fd ); + memset( msgbuf + 6, '\0', sizeof( msgbuf ) - 6 ); + + s_fileSendMsg( pFile->conn, msgbuf, NULL, 0, FALSE ); + s_fileConUnlock( pFile->conn ); + } +} + +static HB_FHANDLE s_fileHandle( PHB_FILE pFile ) +{ + return pFile ? pFile->conn->sd : HB_NO_SOCKET; +} + +static const HB_FILE_FUNCS * s_fileMethods( void ) +{ + static const HB_FILE_FUNCS s_fileFuncs = + { + s_fileAccept, + s_fileExists, + s_fileDelete, + s_fileOpen, + s_fileClose, + s_fileLock, + s_fileReadAt, + s_fileWriteAt, + s_fileTruncAt, + s_fileSize, + s_fileCommit, + s_fileHandle + }; + + return &s_fileFuncs; +} + +/* set HB_NETIO_STARTUP_INIT macro if you want to register NETIO client + * automatically at application startup + */ +#if defined( HB_NETIO_STARTUP_INIT ) + +HB_CALL_ON_STARTUP_BEGIN( _hb_file_io_init_ ) + s_netio_init(); +HB_CALL_ON_STARTUP_END( _hb_file_io_init_ ) + +#if defined( HB_PRAGMA_STARTUP ) + #pragma startup _hb_file_io_init_ +#elif defined( HB_MSC_STARTUP ) + #if defined( HB_OS_WIN_64 ) + #pragma section( HB_MSC_START_SEGMENT, long, read ) + #endif + #pragma data_seg( HB_MSC_START_SEGMENT ) + static HB_$INITSYM hb_vm_auto_hb_file_io_init_ = _hb_file_io_init_; + #pragma data_seg() +#endif + +#endif /* HB_NETIO_STARTUP_INIT */ diff --git a/harbour/contrib/hbnetio/netiomt.prg b/harbour/contrib/hbnetio/netiomt.prg new file mode 100644 index 0000000000..d7ed9a6099 --- /dev/null +++ b/harbour/contrib/hbnetio/netiomt.prg @@ -0,0 +1,81 @@ +/* + * $Id$ + */ + +/* + * Harbour Project source code: + * demonstration code for alternative RDD IO API which uses own + * very simple TCP/IP file server. + * All files which names starts 'net:' are redirected to this API. + * This is code for simple MT server which is activated by: + * NETIO_MTSERVER( [], [] ) -> | NIL + * and can be stopped by: + * NETIO_SERVERSTOP( , .T. ) + * + * Copyright 2009 Przemyslaw Czerpak + * www - http://www.harbour-project.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +FUNCTION NETIO_MTSERVER( ... ) + LOCAL pListenSocket + + IF hb_mtvm() + pListenSocket := netio_listen( ... ) + IF !Empty( pListenSocket ) + hb_threadDetach( hb_threadStart( @netio_srvloop(), pListenSocket ) ) + ENDIF + ENDIF + RETURN pListenSocket + +STATIC FUNCTION NETIO_SRVLOOP( pListenSocket ) + LOCAL pConnectionSocket + + WHILE .T. + pConnectionSocket := netio_accept( pListenSocket ) + IF Empty( pConnectionSocket ) + EXIT + ENDIF + hb_threadDetach( hb_threadStart( @netio_server(), pConnectionSocket ) ) + pConnectionSocket := NIL + ENDDO + RETURN NIL diff --git a/harbour/contrib/hbnetio/netiosrv.c b/harbour/contrib/hbnetio/netiosrv.c new file mode 100644 index 0000000000..fc11a152d5 --- /dev/null +++ b/harbour/contrib/hbnetio/netiosrv.c @@ -0,0 +1,749 @@ +/* + * $Id$ + */ + +/* + * Harbour Project source code: + * demonstration code for alternative RDD IO API which uses own + * very simple TCP/IP file server. + * All files which names starts 'net:' are redirected to this API. + * This is server code giving the following .prg functions: + * NETIO_LISTEN( [], [], [] ) + * -> | NIL + * NETIO_ACCEPT( [, ] ) + * -> | NIL + * NETIO_SERVER( ) -> NIL + * NETIO_SERVERSTOP( | , ) + * -> NIL + * + * Copyright 2009 Przemyslaw Czerpak + * www - http://www.harbour-project.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +#include "hbapi.h" +#include "hbapifs.h" +#include "hbapierr.h" +#include "hbsocket.h" +#include "hbinit.h" +#include "hbvm.h" +#include "hbthread.h" +#include "netio.h" + + +/* + * server code + */ + +typedef struct _HB_CONSRV +{ + HB_SOCKET sd; + PHB_FILE fileTable[ NETIO_FILES_MAX ]; + int filesCount; + int firstFree; + BOOL stop; + int rootPathLen; + char rootPath[ HB_PATH_MAX ]; +} +HB_CONSRV, * PHB_CONSRV; + +typedef struct _HB_LISTENSD +{ + HB_SOCKET sd; + BOOL stop; + char rootPath[ HB_PATH_MAX ]; +} +HB_LISTENSD, * PHB_LISTENSD; + + +static BOOL s_isDirSep( char c ) +{ + /* intentionally used explicit values instead of harbour macros + * because client can use different OS + */ + return c == '/' || c == '\\'; +} + +static const char * s_consrvFilePath( char * pszFileName, + PHB_CONSRV conn ) +{ + int iPos = 0, iLevel = 0; + char ch = HB_OS_PATH_DELIM_CHR; + + if( conn->rootPathLen ) + { + while( s_isDirSep( pszFileName[ 0 ] ) ) + ++pszFileName; + } + + while( pszFileName[ iPos ] && iLevel >= 0 ) + { + if( conn->rootPathLen && s_isDirSep( ch ) ) + { + if( pszFileName[ iPos ] == '.' && + pszFileName[ iPos + 1 ] == '.' && + ( pszFileName[ iPos + 2 ] == '\0' || + s_isDirSep( pszFileName[ iPos + 2 ] ) ) ) + --iLevel; + else if( pszFileName[ iPos ] && + !s_isDirSep( pszFileName[ iPos ] ) && + !( pszFileName[ iPos ] == '.' && + s_isDirSep( pszFileName[ iPos + 1 ] ) ) ) + ++iLevel; + } + ch = pszFileName[ iPos ]; + if( s_isDirSep( ch ) ) + pszFileName[ iPos ] = HB_OS_PATH_DELIM_CHR; + ++iPos; + } + + if( iLevel < 0 ) + pszFileName = NULL; + else if( conn->rootPathLen ) + { + memmove( pszFileName + conn->rootPathLen, pszFileName, + iPos + 1 ); + memcpy( pszFileName, conn->rootPath, conn->rootPathLen ); + } + + return pszFileName; +} + +static int s_srvFileNew( PHB_CONSRV conn, PHB_FILE pFile ) +{ + if( conn->filesCount < NETIO_FILES_MAX ) + { + while( conn->firstFree < NETIO_FILES_MAX ) + { + if( conn->fileTable[ conn->firstFree ] == NULL ) + { + conn->filesCount++; + conn->fileTable[ conn->firstFree ] = pFile; + return conn->firstFree; + } + conn->firstFree++; + } + } + return -1; +} + +static PHB_FILE s_srvFileFree( PHB_CONSRV conn, int iFile ) +{ + if( iFile < NETIO_FILES_MAX && conn->filesCount > 0 ) + { + PHB_FILE pFile = conn->fileTable[ iFile ]; + if( pFile ) + { + conn->fileTable[ iFile ] = NULL; + conn->filesCount--; + if( conn->firstFree > iFile ) + conn->firstFree = iFile; + return pFile; + } + } + + return NULL; +} + +static PHB_FILE s_srvFileGet( PHB_CONSRV conn, int iFile ) +{ + if( iFile < NETIO_FILES_MAX && conn->filesCount > 0 ) + return conn->fileTable[ iFile ]; + else + return NULL; +} + +static void s_consrv_close( PHB_CONSRV conn ) +{ + int i = 0; + + if( conn->sd != HB_NO_SOCKET ) + hb_socketClose( conn->sd ); + + while( conn->filesCount > 0 ) + { + if( i >= NETIO_FILES_MAX ) + break; /* internal error, it should not happen */ + + if( conn->fileTable[ i ] ) + { + hb_fileClose( conn->fileTable[ i ] ); + conn->filesCount--; + } + ++i; + } + conn->firstFree = 0; + hb_xfree( conn ); +} + +static HB_GARBAGE_FUNC( s_consrv_destructor ) +{ + PHB_CONSRV * conn_ptr = ( PHB_CONSRV * ) Cargo; + + if( *conn_ptr ) + { + PHB_CONSRV conn = *conn_ptr; + *conn_ptr = NULL; + s_consrv_close( conn ); + } +} + +static PHB_CONSRV s_consrvParam( int iParam ) +{ + PHB_CONSRV * conn_ptr = ( PHB_CONSRV * ) hb_parptrGC( s_consrv_destructor, + iParam ); + + if( conn_ptr && *conn_ptr ) + return *conn_ptr; + + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + return NULL; +} + +static void s_consrvRet( PHB_CONSRV conn ) +{ + if( conn ) + { + PHB_CONSRV * conn_ptr = ( PHB_CONSRV * ) hb_gcAlloc( sizeof( PHB_CONSRV ), + s_consrv_destructor ); + *conn_ptr = conn; + hb_retptrGC( conn_ptr ); + } + else + hb_ret(); +} + +static PHB_CONSRV s_consrvNew( HB_SOCKET connsd, const char * szRootPath ) +{ + PHB_CONSRV conn = ( PHB_CONSRV ) memset( hb_xgrab( sizeof( HB_CONSRV ) ), + 0, sizeof( HB_CONSRV ) ); + conn->sd = connsd; + if( szRootPath ) + { + hb_strncpy( conn->rootPath, szRootPath, sizeof( conn->rootPath ) - 1 ); + conn->rootPathLen = ( int ) strlen( conn->rootPath ); + } + + return conn; +} + +static long s_srvRecvAll( PHB_CONSRV conn, void * buffer, long len ) +{ + BYTE * ptr = ( BYTE * ) buffer; + long lRead = 0, l; + + while( lRead < len && !conn->stop ) + { + l = hb_socketRecv( conn->sd, ptr + lRead, len - lRead, 0, 1000 ); + if( l <= 0 ) + { + if( hb_socketGetError() != HB_SOCKET_ERR_TIMEOUT || + hb_vmRequestQuery() != 0 ) + break; + } + else + lRead += l; + } + + return lRead; +} + +static long s_srvSendAll( PHB_CONSRV conn, void * buffer, long len ) +{ + BYTE * ptr = ( BYTE * ) buffer; + long lSent = 0, l; + + while( lSent < len && !conn->stop ) + { + l = hb_socketSend( conn->sd, ptr + lSent, len - lSent, 0, 1000 ); + if( l <= 0 ) + { + if( hb_socketGetError() != HB_SOCKET_ERR_TIMEOUT || + hb_vmRequestQuery() != 0 ) + break; + } + else + lSent += l; + } + + return lSent; +} + +static HB_GARBAGE_FUNC( s_listensd_destructor ) +{ + PHB_LISTENSD * lsd_ptr = ( PHB_LISTENSD * ) Cargo; + + if( *lsd_ptr ) + { + PHB_LISTENSD lsd = *lsd_ptr; + *lsd_ptr = NULL; + if( lsd->sd != HB_NO_SOCKET ) + { + hb_socketClose( lsd->sd ); + lsd->sd = HB_NO_SOCKET; + } + hb_xfree( lsd ); + } +} + +static PHB_LISTENSD s_listenParam( int iParam, BOOL fError ) +{ + PHB_LISTENSD * lsd_ptr = ( PHB_LISTENSD * ) + hb_parptrGC( s_listensd_destructor, iParam ); + + if( lsd_ptr && *lsd_ptr ) + return *lsd_ptr; + + if( fError ) + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + return NULL; +} + +static void s_listenRet( HB_SOCKET sd, const char * szRootPath ) +{ + if( sd != HB_NO_SOCKET ) + { + PHB_LISTENSD lsd, * lsd_ptr; + int iLen; + + lsd = ( PHB_LISTENSD ) memset( hb_xgrab( sizeof( HB_LISTENSD ) ), + 0, sizeof( HB_LISTENSD ) ); + lsd->sd = sd; + if( szRootPath ) + hb_strncpy( lsd->rootPath, szRootPath, sizeof( lsd->rootPath ) - 1 ); + else + hb_fsBaseDirBuff( lsd->rootPath ); + iLen = ( int ) strlen( lsd->rootPath ); + if( iLen > 0 ) + { + if( !s_isDirSep( lsd->rootPath[ iLen - 1 ] ) ) + { + if( iLen == sizeof( lsd->rootPath ) - 1 ) + --iLen; + lsd->rootPath[ iLen ] = HB_OS_PATH_DELIM_CHR; + } + } + lsd_ptr = ( PHB_LISTENSD * ) hb_gcAlloc( sizeof( PHB_LISTENSD ), + s_listensd_destructor ); + *lsd_ptr = lsd; + hb_retptrGC( lsd_ptr ); + } + else + hb_ret(); +} + + +HB_FUNC( NETIO_SERVERSTOP ) +{ + PHB_LISTENSD lsd = s_listenParam( 1, FALSE ); + BOOL fStop = !HB_ISLOG( 2 ) || hb_parl( 2 ); + + if( lsd ) + lsd->stop = fStop; + else + { + PHB_CONSRV conn = s_consrvParam( 1 ); + if( conn ) + lsd->stop = fStop; + } +} + +HB_FUNC( NETIO_LISTEN ) +{ + int iPort = hb_parnidef( 1, NETIO_DEFAULT_PORT ); + const char * szAddress = hb_parc( 2 ); + const char * szRootPath = hb_parc( 3 ); + void * pSockAddr; + unsigned uiLen; + HB_SOCKET sd = HB_NO_SOCKET; + + if( hb_socketInetAddr( &pSockAddr, &uiLen, szAddress, iPort ) ) + { + sd = hb_socketOpen( HB_SOCKET_PF_INET, HB_SOCKET_PT_STREAM, 0 ); + if( sd != HB_NO_SOCKET ) + { + if( hb_socketBind( sd, pSockAddr, uiLen ) != 0 || + hb_socketListen( sd, 10 ) != 0 ) + { + hb_socketClose( sd ); + sd = HB_NO_SOCKET; + } + } + hb_xfree( pSockAddr ); + } + + s_listenRet( sd, szRootPath ); +} + +HB_FUNC( NETIO_ACCEPT ) +{ + PHB_LISTENSD lsd = s_listenParam( 1, TRUE ); + PHB_CONSRV conn = NULL; + + if( lsd && lsd->sd != HB_NO_SOCKET && !lsd->stop ) + { + HB_LONG timeout = HB_ISNUM( 2 ) ? hb_parnint( 2 ) : -1; + HB_SOCKET connsd; + + do + connsd = hb_socketAccept( lsd->sd, NULL, NULL, timeout < 0 ? 1000 : timeout ); + while( connsd == HB_NO_SOCKET && !lsd->stop && timeout < 0 && + hb_socketGetError() == HB_SOCKET_ERR_TIMEOUT && + hb_vmRequestQuery() == 0 ); + + if( connsd != HB_NO_SOCKET ) + { + BOOL fOK = FALSE; + BYTE msgbuf[ 64 ]; + + conn = s_consrvNew( connsd, lsd->rootPath ); + + if( s_srvRecvAll( conn, msgbuf, NETIO_MSGLEN ) == NETIO_MSGLEN && + HB_GET_LE_INT32( msgbuf ) == NETIO_LOGIN ) + { + long len = HB_GET_LE_INT16( &msgbuf[ 4 ] ); + + if( len < ( long ) sizeof( msgbuf ) && + len == ( long ) strlen( NETIO_LOGINSTRID ) && + s_srvRecvAll( conn, msgbuf, len ) == len ) + { + if( memcmp( NETIO_LOGINSTRID, msgbuf, len ) == 0 ) + { + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_LOGIN ); + HB_PUT_LE_UINT32( &msgbuf[ 4 ], NETIO_CONNECTED ); + memset( msgbuf + 8, '\0', NETIO_MSGLEN - 8 ); + if( s_srvSendAll( conn, msgbuf, NETIO_MSGLEN ) == NETIO_MSGLEN ) + fOK = TRUE; + } + } + } + + if( !fOK ) + { + s_consrv_close( conn ); + conn = NULL; + } + } + } + + s_consrvRet( conn ); +} + +HB_FUNC( NETIO_SERVER ) +{ + PHB_CONSRV conn = s_consrvParam( 1 ); + + if( conn && conn->sd != HB_NO_SOCKET && !conn->stop ) + { + for( ;; ) + { + BYTE msgbuf[ NETIO_MSGLEN ], buffer[ 2048 ], * ptr = NULL, * msg; + BOOL fNoAnswer = FALSE; + long len = 0, size; + int iError = 0, iFileNo; + USHORT uiFalgs, uiFsError; + char * szExt; + PHB_FILE pFile; + HB_FOFFSET llOffset, llSize; + + msg = buffer; + + if( s_srvRecvAll( conn, msgbuf, NETIO_MSGLEN ) != NETIO_MSGLEN ) + break; + + switch( HB_GET_LE_UINT32( msgbuf ) ) + { + case NETIO_EXISTS: + size = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + if( size <= 0 ) + iError = NETIO_ERR_WRONG_PARAM; + else + { + if( size + conn->rootPathLen >= ( long ) sizeof( buffer ) ) + ptr = msg = ( BYTE * ) hb_xgrab( size + conn->rootPathLen + 1 ); + msg[ size ] = '\0'; + if( s_srvRecvAll( conn, msg, size ) != size ) + iError = NETIO_ERR_READ; + else + { + const char * szFile = s_consrvFilePath( ( char * ) msg, conn ); + + if( !szFile ) + iError = NETIO_ERR_WRONG_FILE_PATH; + else if( !hb_fileExists( ( const char * ) msg, NULL ) ) + iError = hb_fsError(); + else + { + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_EXISTS ); + memset( msg + 4, '\0', NETIO_MSGLEN - 4 ); + } + } + } + break; + + case NETIO_DELETE: + size = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + if( size <= 0 ) + iError = NETIO_ERR_WRONG_PARAM; + else + { + if( size + conn->rootPathLen >= ( long ) sizeof( buffer ) ) + ptr = msg = ( BYTE * ) hb_xgrab( size + conn->rootPathLen + 1 ); + msg[ size ] = '\0'; + if( s_srvRecvAll( conn, msg, size ) != size ) + iError = NETIO_ERR_READ; + else + { + const char * szFile = s_consrvFilePath( ( char * ) msg, conn ); + + if( !szFile ) + iError = NETIO_ERR_WRONG_FILE_PATH; + else if( !hb_fileDelete( ( const char * ) msg ) ) + iError = hb_fsError(); + else + { + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_DELETE ); + memset( msg + 4, '\0', NETIO_MSGLEN - 4 ); + } + } + } + break; + + case NETIO_OPEN: + uiFalgs = HB_GET_LE_UINT16( &msgbuf[ 6 ] ); + szExt = msgbuf[ 8 ] ? hb_strndup( ( const char * ) &msgbuf[ 8 ], + NETIO_MSGLEN - 8 ) : NULL; + size = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + if( size <= 0 ) + iError = NETIO_ERR_WRONG_PARAM; + else + { + if( size + conn->rootPathLen >= ( long ) sizeof( buffer ) ) + ptr = msg = ( BYTE * ) hb_xgrab( size + conn->rootPathLen + 1 ); + msg[ size ] = '\0'; + if( s_srvRecvAll( conn, msg, size ) != size ) + iError = NETIO_ERR_READ; + else if( conn->filesCount >= NETIO_FILES_MAX ) + iError = NETIO_ERR_FILES_MAX; + else + { + const char * szFile = s_consrvFilePath( ( char * ) msg, conn ); + + if( !szFile ) + iError = NETIO_ERR_WRONG_FILE_PATH; + else + { + pFile = hb_fileExtOpen( szFile, szExt, uiFalgs, NULL, NULL ); + if( !pFile ) + iError = hb_fsError(); + else + { + iFileNo = s_srvFileNew( conn, pFile ); + if( iFileNo < 0 ) + { + iError = NETIO_ERR_FILES_MAX; + hb_fileClose( pFile ); + } + else + { + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_OPEN ); + HB_PUT_LE_UINT16( &msg[ 4 ], iFileNo ); + memset( msg + 6, '\0', NETIO_MSGLEN - 6 ); + } + } + } + } + } + if( szExt ) + hb_xfree( szExt ); + break; + + case NETIO_READ: + iFileNo = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + size = HB_GET_LE_UINT32( &msgbuf[ 6 ] ); + llOffset = HB_GET_LE_INT64( &msgbuf[ 10 ] ); + if( size < 0 ) + iError = NETIO_ERR_WRONG_PARAM; + else + { + if( size >= ( long ) ( sizeof( buffer ) - NETIO_MSGLEN ) ) + ptr = msg = ( BYTE * ) hb_xgrab( size + NETIO_MSGLEN ); + pFile = s_srvFileGet( conn, iFileNo ); + if( pFile == NULL ) + iError = NETIO_ERR_WRONG_FILE_HANDLE; + else + { + len = hb_fileReadAt( pFile, msg + NETIO_MSGLEN, size, llOffset ); + uiFsError = hb_fsError(); + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_READ ); + HB_PUT_LE_UINT32( &msg[ 4 ], len ); + HB_PUT_LE_UINT32( &msg[ 8 ], uiFsError ); + memset( msg + 10, '\0', NETIO_MSGLEN - 10 ); + } + } + break; + + case NETIO_WRITE: + iFileNo = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + size = HB_GET_LE_UINT32( &msgbuf[ 6 ] ); + llOffset = HB_GET_LE_INT64( &msgbuf[ 10 ] ); + if( size < 0 ) + iError = NETIO_ERR_WRONG_PARAM; + else + { + if( size >= ( long ) sizeof( buffer ) ) + ptr = msg = ( BYTE * ) hb_xgrab( size ); + if( s_srvRecvAll( conn, msg, size ) != size ) + iError = NETIO_ERR_READ; + else + { + pFile = s_srvFileGet( conn, iFileNo ); + if( pFile == NULL ) + iError = NETIO_ERR_WRONG_FILE_HANDLE; + else + { + size = hb_fileWriteAt( pFile, msg, size, llOffset ); + uiFsError = hb_fsError(); + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_WRITE ); + HB_PUT_LE_UINT32( &msg[ 4 ], size ); + HB_PUT_LE_UINT32( &msg[ 8 ], uiFsError ); + memset( msg + 10, '\0', NETIO_MSGLEN - 10 ); + } + } + } + break; + + case NETIO_LOCK: + iFileNo = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + llOffset = HB_GET_LE_INT64( &msgbuf[ 6 ] ); + llSize = HB_GET_LE_INT64( &msgbuf[ 14 ] ); + uiFalgs = HB_GET_LE_UINT16( &msgbuf[ 22 ] ); + pFile = s_srvFileGet( conn, iFileNo ); + if( pFile == NULL ) + iError = NETIO_ERR_WRONG_FILE_HANDLE; + else if( !hb_fileLock( pFile, llOffset, llSize, uiFalgs ) ) + iError = hb_fsError(); + else + { + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_LOCK ); + memset( msg + 4, '\0', NETIO_MSGLEN - 4 ); + } + break; + + case NETIO_TRUNC: + iFileNo = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + llOffset = HB_GET_LE_INT64( &msgbuf[ 6 ] ); + pFile = s_srvFileGet( conn, iFileNo ); + if( pFile == NULL ) + iError = NETIO_ERR_WRONG_FILE_HANDLE; + else if( !hb_fileTruncAt( pFile, llOffset ) ) + iError = hb_fsError(); + else + { + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_TRUNC ); + memset( msg + 4, '\0', NETIO_MSGLEN - 4 ); + } + break; + + case NETIO_SIZE: + iFileNo = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + pFile = s_srvFileGet( conn, iFileNo ); + if( pFile == NULL ) + iError = NETIO_ERR_WRONG_FILE_HANDLE; + else + { + llOffset = hb_fileSize( pFile ); + uiFsError = hb_fsError(); + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_SIZE ); + HB_PUT_LE_UINT64( &msg[ 4 ], llOffset ); + HB_PUT_LE_UINT32( &msg[ 12 ], uiFsError ); + memset( msg + 14, '\0', NETIO_MSGLEN - 14 ); + } + break; + + case NETIO_CLOSE: + iFileNo = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + pFile = s_srvFileFree( conn, iFileNo ); + if( pFile == NULL ) + iError = NETIO_ERR_WRONG_FILE_HANDLE; + else + { + hb_fileClose( pFile ); + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_CLOSE ); + memset( msg + 4, '\0', NETIO_MSGLEN - 4 ); + } + break; + + case NETIO_COMMIT: + iFileNo = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); + pFile = s_srvFileGet( conn, iFileNo ); + if( pFile ) + hb_fileCommit( pFile ); + fNoAnswer = TRUE; + break; + + default: /* unkown message */ + iError = NETIO_ERR_UNKNOWN_COMMAND; + break; + } + + if( !fNoAnswer ) + { + if( iError != 0 ) + { + HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_ERROR ); + HB_PUT_LE_UINT16( &msg[ 4 ], iError ); + memset( msg + 6, '\0', NETIO_MSGLEN - 6 ); + len = NETIO_MSGLEN; + } + else + len += NETIO_MSGLEN; + + if( s_srvSendAll( conn, msg, len ) != len ) + break; + } + + if( ptr ) + hb_xfree( ptr ); + } + } +} diff --git a/harbour/contrib/hbnetio/tests/netiotst.hbp b/harbour/contrib/hbnetio/tests/netiotst.hbp new file mode 100644 index 0000000000..762bf37aa6 --- /dev/null +++ b/harbour/contrib/hbnetio/tests/netiotst.hbp @@ -0,0 +1,7 @@ +# +# $Id: uhttpd2.hbp 11370 2009-06-15 18:22:09Z vszakats $ +# + +netiotst.prg +-lhbnetio +-mt diff --git a/harbour/contrib/hbnetio/tests/netiotst.prg b/harbour/contrib/hbnetio/tests/netiotst.prg new file mode 100644 index 0000000000..ef8ce32ab6 --- /dev/null +++ b/harbour/contrib/hbnetio/tests/netiotst.prg @@ -0,0 +1,92 @@ +#define DBNAME "net:127.0.0.1:2941:data/_tst_" + +request DBFCDX + +proc main() + local pSockSrv + + set exclusive off + rddSetDefault( "DBFCDX" ) + + pSockSrv := netio_mtserver() + + hb_idleSleep( 0.1 ) + + ? "NETIO_CONNECT():", netio_connect() + + createdb( DBNAME ) + testdb( DBNAME ) + wait + + test( DBNAME ) + wait + + ? + ? "deleteing..." + dbDrop( DBNAME ) + test( DBNAME ) + wait + + netio_serverstop( pSockSrv, .t. ) +return + +proc createdb( cName ) + local n + + dbCreate( cName, {{"F1", "C", 20, 0},; + {"F2", "M", 4, 0},; + {"F3", "N", 10, 2},; + {"F4", "T", 8, 0}} ) + ? "create neterr:", neterr(), hb_osError() + use (cName) + ? "use neterr:", neterr(), hb_osError() + while lastrec() < 100 + dbAppend() + n := recno() - 1 + field->F1 := chr( n % 26 + asc( "A" ) ) + " " + time() + field->F2 := field->F1 + field->F3 := n / 100 + field->F4 := hb_dateTime() + enddo + index on field->F1 tag T1 + index on field->F3 tag T3 + index on field->F4 tag T4 + close + ? +return + +proc testdb( cName ) + local i, j + use (cName) + ? "used:", used() + ? "nterr:", neterr() + ? "alias:", alias() + ? "lastrec:", lastrec() + ? "ordCount:", ordCount() + for i:=1 to ordCount() + ordSetFocus( i ) + ? i, "name:", ordName(), "key:", ordKey(), "keycount:", ordKeyCount() + next + ordSetFocus( 1 ) + dbgotop() + while !eof() + if ! field->F1 == field->F2 + ? "error at record:", recno() + ? " ! '" + field->F1 + "' == '" + field->F2 + "'" + endif + dbSkip() + enddo + wait + i := row() + j := col() + dbgotop() + browse() + setpos( i, j ) + close +return + +proc test( cName ) + ? "test start" + ? "File exists: ", dbExists( cName ) + ? "test end" +return diff --git a/harbour/harbour-wce-spec b/harbour/harbour-wce-spec index f385e8ef75..a7db7c5cbb 100644 --- a/harbour/harbour-wce-spec +++ b/harbour/harbour-wce-spec @@ -108,7 +108,7 @@ export HB_XBUILD=wce export HB_BIN_INSTALL=%{_bindir} export HB_INC_INSTALL=%{_includedir}/%{name} export HB_LIB_INSTALL=%{_libdir}/%{name} -export HB_CONTRIBLIBS="gtwvg hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbtip hbvpdf hbwin hbziparc rddado xhb %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_allegro:gtalleg}" +export HB_CONTRIBLIBS="gtwvg hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbnetio hbtip hbvpdf hbwin hbziparc rddado xhb %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_allegro:gtalleg}" export HB_EXTERNALLIBS=no make %{?_smp_mflags} @@ -137,7 +137,7 @@ export HB_XBUILD=wce export HB_BIN_INSTALL=%{_bindir} export HB_INC_INSTALL=%{_includedir}/%{name} export HB_LIB_INSTALL=%{_libdir}/%{name} -export HB_CONTRIBLIBS="gtwvg hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbtip hbvpdf hbwin hbziparc rddado xhb %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_allegro:gtalleg}" +export HB_CONTRIBLIBS="gtwvg hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbnetio hbtip hbvpdf hbwin hbziparc rddado xhb %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_allegro:gtalleg}" export HB_EXTERNALLIBS=no export _DEFAULT_BIN_DIR=$HB_BIN_INSTALL diff --git a/harbour/harbour-win-spec b/harbour/harbour-win-spec index f81b058c9f..06115942a3 100644 --- a/harbour/harbour-win-spec +++ b/harbour/harbour-win-spec @@ -108,7 +108,7 @@ export HB_XBUILD=win export HB_BIN_INSTALL=%{_bindir} export HB_INC_INSTALL=%{_includedir}/%{name} export HB_LIB_INSTALL=%{_libdir}/%{name} -export HB_CONTRIBLIBS="gtwvg hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbtip hbvpdf hbwin hbziparc rddado xhb %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_allegro:gtalleg}" +export HB_CONTRIBLIBS="gtwvg hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbnetio hbtip hbvpdf hbwin hbziparc rddado xhb %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_allegro:gtalleg}" export HB_EXTERNALLIBS=no make %{?_smp_mflags} @@ -137,7 +137,7 @@ export HB_XBUILD=win export HB_BIN_INSTALL=%{_bindir} export HB_INC_INSTALL=%{_includedir}/%{name} export HB_LIB_INSTALL=%{_libdir}/%{name} -export HB_CONTRIBLIBS="gtwvg hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbtip hbvpdf hbwin hbziparc rddado xhb %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_allegro:gtalleg}" +export HB_CONTRIBLIBS="gtwvg hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbnetio hbtip hbvpdf hbwin hbziparc rddado xhb %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_allegro:gtalleg}" export HB_EXTERNALLIBS=no export _DEFAULT_BIN_DIR=$HB_BIN_INSTALL diff --git a/harbour/harbour.spec b/harbour/harbour.spec index a26646ae87..8268655499 100644 --- a/harbour/harbour.spec +++ b/harbour/harbour.spec @@ -81,7 +81,7 @@ %define hb_idir export HB_INC_INSTALL=%{_includedir}/%{name} %define hb_ldir export HB_LIB_INSTALL=%{_libdir}/%{name} %define hb_cmrc export HB_COMMERCE=%{?_without_gpllib:yes} -%define hb_ctrb export HB_CONTRIBLIBS="hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbtip hbtpathy hbhpdf hbvpdf hbziparc xhb rddsql %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_libharu:hbhpdf} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_fbsql:hbfbird} %{?_with_allegro:gtalleg}" +%define hb_ctrb export HB_CONTRIBLIBS="hbbmcdx hbbtree hbclipsm hbct hbgt hbmisc hbmzip hbnetio hbtip hbtpathy hbhpdf hbvpdf hbziparc xhb rddsql %{!?_without_nf:hbnf} %{?_with_odbc:hbodbc} %{?_with_curl:hbcurl} %{?_with_libharu:hbhpdf} %{?_with_ads:rddads} %{?_with_gd:hbgd} %{?_with_pgsql:hbpgsql} %{?_with_mysql:hbmysql} %{?_with_fbsql:hbfbird} %{?_with_allegro:gtalleg}" %define hb_extrn export HB_EXTERNALLIBS=no %define hb_env %{hb_plat} ; %{hb_cc} ; %{hb_cflag} ; %{hb_lflag} ; %{hb_gpm} ; %{hb_crs} ; %{hb_sln} ; %{hb_x11} ; %{hb_bdir} ; %{hb_idir} ; %{hb_ldir} ; %{hb_ctrb} ; %{hb_extrn} ; %{hb_cmrc} %define hb_host www.harbour-project.org @@ -659,6 +659,7 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/%{name}/libhbbtree.a %{_libdir}/%{name}/libhbmisc.a %{_libdir}/%{name}/libhbmzip.a +%{_libdir}/%{name}/libhbnetio.a %{_libdir}/%{name}/libhbct.a %{_libdir}/%{name}/libhbtip*.a %{_libdir}/%{name}/libxhb.a