From ed45b65b0417c9e90d6bee662b816a7270acc697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Czerpak?= Date: Mon, 24 Mar 2014 22:19:45 +0100 Subject: [PATCH] 2014-03-24 22:19 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) * include/Makefile + include/hbiousr.ch * src/rtl/Makefile + src/rtl/iousr.c + added module for writting Harbour FILE IO redirectors at PRG level + tests/iotcp.prg + example code for FILE IO redirector written in PRG. It's IOTCP and gives exactly the same functionality as TCP redirector in contrib which is written in C. * src/vm/runner.c % modified internal structure to eliminate some memory allocations * contrib/hbpgsql/rddcopy.c % small simplification --- ChangeLog.txt | 18 + contrib/hbpgsql/rddcopy.c | 18 +- include/Makefile | 1 + include/hbiousr.ch | 93 +++++ src/rtl/Makefile | 1 + src/rtl/iousr.c | 767 ++++++++++++++++++++++++++++++++++++++ src/vm/runner.c | 18 +- tests/iotcp.prg | 262 +++++++++++++ 8 files changed, 1161 insertions(+), 17 deletions(-) create mode 100644 include/hbiousr.ch create mode 100644 src/rtl/iousr.c create mode 100644 tests/iotcp.prg diff --git a/ChangeLog.txt b/ChangeLog.txt index a58390ce51..192224c23c 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -10,6 +10,24 @@ * Change, ! Fix, % Optimization, + Addition, - Removal, ; Comment */ +2014-03-24 22:19 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) + * include/Makefile + + include/hbiousr.ch + * src/rtl/Makefile + + src/rtl/iousr.c + + added module for writting Harbour FILE IO redirectors at PRG level + + + tests/iotcp.prg + + example code for FILE IO redirector written in PRG. + It's IOTCP and gives exactly the same functionality as TCP redirector + in contrib which is written in C. + + * src/vm/runner.c + % modified internal structure to eliminate some memory allocations + + * contrib/hbpgsql/rddcopy.c + % small simplification + 2014-03-20 14:59 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) * src/rtl/filesys.c ! added workaround for offset returned by FSeek( hDir, 0, FS_END ) diff --git a/contrib/hbpgsql/rddcopy.c b/contrib/hbpgsql/rddcopy.c index ea5be86994..ee386fc673 100644 --- a/contrib/hbpgsql/rddcopy.c +++ b/contrib/hbpgsql/rddcopy.c @@ -155,16 +155,20 @@ static HB_BOOL exportBufSqlVar( pgCopyContext * context, PHB_ITEM pValue, const while( *szVal && nCnt++ < nLen ) { - /* if( *szVal == *szDelim || *szVal == *szEsc || *szVal == *szQuote ) - we don't need to escape delim in CSV mode, - only the quote and the escape itself */ - - if( *szVal == *szQuote || *szVal == *szEsc ) - if( ! addToContext( context, *szEsc ) ) - return HB_FALSE; if( ( HB_UCHAR ) *szVal >= 32 ) + { + /* if( *szVal == *szDelim || *szVal == *szEsc || *szVal == *szQuote ) + we don't need to escape delim in CSV mode, + only the quote and the escape itself */ + + if( *szVal == *szQuote || *szVal == *szEsc ) + { + if( ! addToContext( context, *szEsc ) ) + return HB_FALSE; + } if( ! addToContext( context, *szVal ) ) return HB_FALSE; + } szVal++; } if( ! addStrToContext( context, szQuote ) ) diff --git a/include/Makefile b/include/Makefile index a2893cc1ba..afaf927afe 100644 --- a/include/Makefile +++ b/include/Makefile @@ -116,6 +116,7 @@ PRG_HEADERS := \ hbhash.ch \ hbhrb.ch \ hbinkey.ch \ + hbiousr.ch \ hblang.ch \ hblpp.ch \ hbmacro.ch \ diff --git a/include/hbiousr.ch b/include/hbiousr.ch new file mode 100644 index 0000000000..0d44a241ce --- /dev/null +++ b/include/hbiousr.ch @@ -0,0 +1,93 @@ +/* + * Harbour Project source code: + * IOUSRD - module to create new FILE IO redirectors at prg level + * + * Copyright 2014 Przemyslaw Czerpak + * www - http://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.txt. 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. + * + */ + +#ifndef _HBIOUSR_CH +#define _HBIOUSR_CH + +/* method numbers */ +#define IOUSR_ACCEPT 1 +#define IOUSR_EXISTS 2 +#define IOUSR_DELETE 3 +#define IOUSR_RENAME 4 +#define IOUSR_COPY 5 + +#define IOUSR_DIREXISTS 6 +#define IOUSR_DIRMAKE 7 +#define IOUSR_DIRREMOVE 8 +#define IOUSR_DIRSPACE 9 +#define IOUSR_DIRECTORY 10 + +#define IOUSR_TIMEGET 11 +#define IOUSR_TIMESET 12 +#define IOUSR_ATTRGET 13 +#define IOUSR_ATTRSET 14 + +#define IOUSR_LINK 15 +#define IOUSR_LINKSYM 16 +#define IOUSR_LINKREAD 17 + +#define IOUSR_OPEN 18 +#define IOUSR_CLOSE 19 +#define IOUSR_LOCK 20 +#define IOUSR_LOCKTEST 21 +#define IOUSR_READ 22 +#define IOUSR_WRITE 23 +#define IOUSR_READAT 24 +#define IOUSR_WRITEAT 25 +#define IOUSR_TRUNCAT 26 +#define IOUSR_SEEK 27 +#define IOUSR_SIZE 28 +#define IOUSR_EOF 29 +#define IOUSR_FLUSH 30 +#define IOUSR_COMMIT 31 +#define IOUSR_CONFIGURE 32 +#define IOUSR_HANDLE 33 + +#define IOUSR_METHODCOUNT 33 + +#endif /* _HBIOUSR_CH */ diff --git a/src/rtl/Makefile b/src/rtl/Makefile index 7b866a095a..456db82bce 100644 --- a/src/rtl/Makefile +++ b/src/rtl/Makefile @@ -120,6 +120,7 @@ C_SOURCES := \ idle.c \ inkey.c \ inkeyapi.c \ + iousr.c \ is.c \ isprint.c \ itemseri.c \ diff --git a/src/rtl/iousr.c b/src/rtl/iousr.c new file mode 100644 index 0000000000..77fc8e6e1c --- /dev/null +++ b/src/rtl/iousr.c @@ -0,0 +1,767 @@ +/* + * Harbour Project source code: + * IOUSRD - library to create new FILE IO redirectors at prg level + * + * Copyright 2014 Przemyslaw Czerpak + * www - http://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.txt. 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 "hbapierr.h" +#include "hbapiitm.h" +#include "hbxvm.h" +#include "hbstack.h" +#include "hbthread.h" + +#include "hbiousr.ch" + +#define HB_FILE_ERR_UNSUPPORTED ( ( HB_ERRCODE ) FS_ERROR ) + +typedef struct _HB_IOUSR +{ + HB_FILE_FUNCS funcs; + char * prefix; + int prefix_len; + PHB_SYMB prg_funcs[ IOUSR_METHODCOUNT ]; +} +HB_IOUSR, * PHB_IOUSR; + +typedef struct _HB_FILE +{ + const HB_FILE_FUNCS * pFuncs; + PHB_ITEM pFileItm; +} +HB_FILE; + +static HB_CRITICAL_NEW( s_iousrMtx ); +#define HB_IOUSR_LOCK() do { hb_threadEnterCriticalSection( &s_iousrMtx ) +#define HB_IOUSR_UNLOCK() hb_threadLeaveCriticalSection( &s_iousrMtx ); } while( 0 ) + +static int s_iCount = 0; +static PHB_IOUSR s_ioUsrs[ HB_FILE_TYPE_MAX ]; + +static void s_errRT_IOUSR( HB_ERRCODE errGenCode, HB_ERRCODE errSubCode, + const char * szDescription ) +{ + PHB_ITEM pError, pArray; + + pError = hb_errRT_New( ES_ERROR, "IOUSR", errGenCode, errSubCode, + szDescription, HB_ERR_FUNCNAME, 0, EF_NONE ); + pArray = hb_arrayBaseParams(); + if( pArray ) + { + hb_errPutArgsArray( pError, pArray ); + hb_itemRelease( pArray ); + } + hb_errLaunch( pError ); + hb_itemRelease( pError ); +} + +static void s_iousrFreeAll( void * cargo ) +{ + HB_SYMBOL_UNUSED( cargo ); + + while( s_iCount > 0 ) + { + PHB_IOUSR pIO = s_ioUsrs[ --s_iCount ]; + + hb_xfree( pIO->prefix ); + hb_xfree( pIO ); + } +} + +static PHB_FILE s_fileNew( PHB_IOUSR pIO, PHB_ITEM pFileItm ) +{ + PHB_FILE pFile = ( PHB_FILE ) hb_xgrab( sizeof( HB_FILE ) ); + + pFile->pFuncs = &pIO->funcs; + pFile->pFileItm = pFileItm; + + return pFile; +} + +static PHB_IOUSR s_iousrAddNew( const char * pszPrefix ) +{ + PHB_IOUSR pIO = NULL; + int iCount; + + HB_IOUSR_LOCK(); + + iCount = s_iCount; + while( --iCount >= 0 ) + { + if( hb_stricmp( pszPrefix, s_ioUsrs[ iCount ]->prefix ) == 0 ) + break; + } + if( iCount < 0 ) + { + if( s_iCount == 0 ) + hb_vmAtQuit( s_iousrFreeAll, NULL ); + pIO = ( PHB_IOUSR ) hb_xgrab( sizeof( HB_IOUSR ) ); + memset( pIO, 0, sizeof( HB_IOUSR ) ); + pIO->prefix = hb_strdup( pszPrefix ); + pIO->prefix_len = strlen( pszPrefix ); + s_ioUsrs[ s_iCount++ ] = pIO; + } + + HB_IOUSR_UNLOCK(); + + return pIO; +} + +#define s_hasMethod( pIO, iMethod ) \ + ( ( pIO )->prg_funcs[ ( iMethod ) - 1 ] != NULL ) +static void s_pushMethod( PHB_IOUSR pIO, int iMethod ) +{ + hb_vmPushSymbol( pIO->prg_funcs[ iMethod - 1 ] ); + hb_vmPushNil(); +} + +static HB_BOOL s_fileAccept( PHB_FILE_FUNCS pFuncs, const char * pszFileName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + HB_BOOL fResult = HB_FALSE; + + if( hb_strnicmp( pszFileName, pIO->prefix, pIO->prefix_len ) == 0 ) + { + if( s_hasMethod( pIO, IOUSR_ACCEPT ) ) + { + s_pushMethod( pIO, IOUSR_ACCEPT ); + hb_vmPushString( pszFileName, strlen( pszFileName ) ); + hb_vmDo( 1 ); + fResult = hb_parl( -1 ); + } + else if( pIO->prefix_len > 0 ) + fResult = HB_TRUE; + } + + return fResult; +} + +static HB_BOOL s_fileExists( PHB_FILE_FUNCS pFuncs, const char * pszFileName, char * pRetPath ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + HB_SYMBOL_UNUSED( pRetPath ); + + s_pushMethod( pIO, IOUSR_EXISTS ); + hb_vmPushString( pszFileName, strlen( pszFileName ) ); + hb_vmDo( 1 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileDelete( PHB_FILE_FUNCS pFuncs, const char * pszFileName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_DELETE ); + hb_vmPushString( pszFileName, strlen( pszFileName ) ); + hb_vmDo( 1 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileRename( PHB_FILE_FUNCS pFuncs, const char * pszName, const char * pszNewName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_RENAME ); + hb_vmPushString( pszName, strlen( pszName ) ); + hb_vmPushString( pszNewName, strlen( pszNewName ) ); + hb_vmDo( 2 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileCopy( PHB_FILE_FUNCS pFuncs, const char * pSrcFile, const char * pszDstFile ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_COPY ); + hb_vmPushString( pSrcFile, strlen( pSrcFile ) ); + hb_vmPushString( pszDstFile, strlen( pszDstFile ) ); + hb_vmDo( 2 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileDirExists( PHB_FILE_FUNCS pFuncs, const char * pszDirName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_DIREXISTS ); + hb_vmPushString( pszDirName, strlen( pszDirName ) ); + hb_vmDo( 1 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileDirMake( PHB_FILE_FUNCS pFuncs, const char * pszDirName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_DIRMAKE ); + hb_vmPushString( pszDirName, strlen( pszDirName ) ); + hb_vmDo( 1 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileDirRemove( PHB_FILE_FUNCS pFuncs, const char * pszDirName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_DIRREMOVE ); + hb_vmPushString( pszDirName, strlen( pszDirName ) ); + hb_vmDo( 1 ); + + return hb_parl( -1 ); +} + +static double s_fileDirSpace( PHB_FILE_FUNCS pFuncs, const char * pszDirName, HB_USHORT uiType ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_DIRSPACE ); + hb_vmPushString( pszDirName, strlen( pszDirName ) ); + hb_vmPushInteger( uiType ); + hb_vmDo( 2 ); + + return hb_parnd( -1 ); +} + +static PHB_ITEM s_fileDirectory( PHB_FILE_FUNCS pFuncs, const char * pszDirSpec, const char * pszAttr ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_DIRECTORY ); + hb_vmPushString( pszDirSpec, strlen( pszDirSpec ) ); + hb_vmPushString( pszAttr, strlen( pszAttr ) ); + hb_vmDo( 2 ); + + return hb_itemNew( hb_stackReturnItem() ); +} + +static HB_BOOL s_fileTimeGet( PHB_FILE_FUNCS pFuncs, const char * pszFileName, long * plJulian, long * plMillisec ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + HB_BOOL fResult; + int iOffset; + + iOffset = ( int ) ( hb_stackTopOffset() - hb_stackBaseOffset() ); + hb_vmPushNil(); + hb_vmPushNil(); + + s_pushMethod( pIO, IOUSR_TIMEGET ); + hb_vmPushString( pszFileName, strlen( pszFileName ) ); + hb_xvmPushLocalByRef( ( HB_SHORT ) iOffset ); + hb_xvmPushLocalByRef( ( HB_SHORT ) ( iOffset + 1 ) ); + hb_vmDo( 3 ); + + fResult = hb_parl( -1 ); + if( fResult ) + { + *plJulian = hb_itemGetNL( hb_stackItemFromBase( iOffset ) ); + *plMillisec = hb_itemGetNL( hb_stackItemFromBase( iOffset + 1 ) ); + } + hb_stackPop(); + hb_stackPop(); + + return fResult; +} + +static HB_BOOL s_fileTimeSet( PHB_FILE_FUNCS pFuncs, const char * pszFileName, long lJulian, long lMillisec ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_TIMESET ); + hb_vmPushString( pszFileName, strlen( pszFileName ) ); + hb_vmPushLong( lJulian ); + hb_vmPushLong( lMillisec ); + hb_vmDo( 3 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileAttrGet( PHB_FILE_FUNCS pFuncs, const char * pszFileName, HB_FATTR * pnAttr ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + HB_BOOL fResult; + int iOffset; + + iOffset = ( int ) ( hb_stackTopOffset() - hb_stackBaseOffset() ); + hb_vmPushNil(); + + s_pushMethod( pIO, IOUSR_ATTRGET ); + hb_vmPushString( pszFileName, strlen( pszFileName ) ); + hb_xvmPushLocalByRef( ( HB_SHORT ) iOffset ); + hb_vmDo( 2 ); + + fResult = hb_parl( -1 ); + if( fResult ) + *pnAttr = ( HB_FATTR ) hb_itemGetNL( hb_stackItemFromBase( iOffset ) ); + hb_stackPop(); + + return fResult; +} + +static HB_BOOL s_fileAttrSet( PHB_FILE_FUNCS pFuncs, const char * pszFileName, HB_FATTR nAttr ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_ATTRSET ); + hb_vmPushString( pszFileName, strlen( pszFileName ) ); + hb_vmPushLong( nAttr ); + hb_vmDo( 2 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileLink( PHB_FILE_FUNCS pFuncs, const char * pszExisting, const char * pszNewName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_LINK ); + hb_vmPushString( pszExisting, strlen( pszExisting ) ); + hb_vmPushString( pszNewName, strlen( pszNewName ) ); + hb_vmDo( 2 ); + + return hb_parl( -1 ); +} + +static HB_BOOL s_fileLinkSym( PHB_FILE_FUNCS pFuncs, const char * pszTarget, const char * pszNewName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + + s_pushMethod( pIO, IOUSR_LINKSYM ); + hb_vmPushString( pszTarget, strlen( pszTarget ) ); + hb_vmPushString( pszNewName, strlen( pszNewName ) ); + hb_vmDo( 2 ); + + return hb_parl( -1 ); +} + +static char * s_fileLinkRead( PHB_FILE_FUNCS pFuncs, const char * pszFileName ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + const char * pszLink; + + s_pushMethod( pIO, IOUSR_LINKREAD ); + hb_vmPushString( pszFileName, strlen( pszFileName ) ); + hb_vmDo( 1 ); + + pszLink = hb_parc( -1 ); + return pszLink != NULL ? hb_strdup( pszLink ) : NULL; +} + +static PHB_FILE s_fileOpen( PHB_FILE_FUNCS pFuncs, const char * pszName, + const char * pszDefExt, HB_USHORT uiExFlags, + const char * pPaths, PHB_ITEM pError ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFuncs; + PHB_FILE pFile = NULL; + PHB_ITEM pFileItm; + + s_pushMethod( pIO, IOUSR_OPEN ); + hb_vmPushString( pszName, strlen( pszName ) ); + if( pszDefExt ) + hb_vmPushString( pszDefExt, strlen( pszDefExt ) ); + else + hb_vmPushNil(); + hb_vmPushInteger( uiExFlags ); + if( pPaths ) + hb_vmPushString( pPaths, strlen( pPaths ) ); + else + hb_vmPushNil(); + if( pError ) + hb_vmPush( pError ); + else + hb_vmPushNil(); + + hb_vmDo( 5 ); + + pFileItm = hb_stackReturnItem(); + if( ! HB_IS_NIL( pFileItm ) ) + pFile = s_fileNew( pIO, hb_itemNew( pFileItm ) ); + + return pFile; +} + +static void s_fileClose( PHB_FILE pFile ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_CLOSE ); + hb_vmPush( pFile->pFileItm ); + hb_vmDo( 1 ); +} + +static HB_BOOL s_fileLock( PHB_FILE pFile, HB_FOFFSET nStart, + HB_FOFFSET nLen, int iType ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_LOCK ); + hb_vmPush( pFile->pFileItm ); + hb_vmPushNumInt( ( HB_MAXINT ) nStart ); + hb_vmPushNumInt( ( HB_MAXINT ) nLen ); + hb_vmPushInteger( iType ); + hb_vmDo( 4 ); + + return hb_parl( -1 ); +} + +static int s_fileLockTest( PHB_FILE pFile, HB_FOFFSET nStart, + HB_FOFFSET nLen, int iType ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_LOCKTEST ); + hb_vmPush( pFile->pFileItm ); + hb_vmPushNumInt( ( HB_MAXINT ) nStart ); + hb_vmPushNumInt( ( HB_MAXINT ) nLen ); + hb_vmPushInteger( iType ); + hb_vmDo( 4 ); + + return hb_parni( -1 ); +} + +static HB_SIZE s_fileRead( PHB_FILE pFile, void * data, + HB_SIZE nSize, HB_MAXINT timeout ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + HB_SIZE nResult; + int iOffset; + + iOffset = ( int ) ( hb_stackTopOffset() - hb_stackBaseOffset() ); + memset( data, 0, nSize ); + hb_vmPushString( ( const char * ) data, nSize ); + + s_pushMethod( pIO, IOUSR_READ ); + hb_vmPush( pFile->pFileItm ); + hb_xvmPushLocalByRef( ( HB_SHORT ) iOffset ); + hb_vmPushSize( nSize ); + hb_vmPushNumInt( timeout ); + hb_vmDo( 4 ); + + nResult = hb_parns( -1 ); + if( nResult > 0 ) + { + nSize = hb_itemGetCLen( hb_stackItemFromBase( iOffset ) ); + if( nResult > nSize ) + nResult = nSize; + memcpy( data, hb_itemGetCPtr( hb_stackItemFromBase( iOffset ) ), nSize ); + } + hb_stackPop(); + + return nResult; +} + +static HB_SIZE s_fileWrite( PHB_FILE pFile, const void * data, + HB_SIZE nSize, HB_MAXINT timeout ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_WRITE ); + hb_vmPush( pFile->pFileItm ); + hb_vmPushString( ( const char * ) data, nSize ); + hb_vmPushSize( nSize ); + hb_vmPushNumInt( timeout ); + hb_vmDo( 4 ); + + return hb_parns( -1 ); +} + +static HB_SIZE s_fileReadAt( PHB_FILE pFile, void * buffer, + HB_SIZE nSize, HB_FOFFSET nOffset ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + HB_SIZE nResult; + int iOffset; + + iOffset = ( int ) ( hb_stackTopOffset() - hb_stackBaseOffset() ); + memset( buffer, 0, nSize ); + hb_vmPushString( ( const char * ) buffer, nSize ); + + s_pushMethod( pIO, IOUSR_READAT ); + hb_vmPush( pFile->pFileItm ); + hb_xvmPushLocalByRef( ( HB_SHORT ) iOffset ); + hb_vmPushSize( nSize ); + hb_vmPushNumInt( ( HB_MAXINT ) nOffset ); + hb_vmDo( 4 ); + + nResult = hb_parns( -1 ); + if( nResult > 0 ) + { + nSize = hb_itemGetCLen( hb_stackItemFromBase( iOffset ) ); + if( nResult > nSize ) + nResult = nSize; + memcpy( buffer, hb_itemGetCPtr( hb_stackItemFromBase( iOffset ) ), nSize ); + } + hb_stackPop(); + + return nResult; +} + +static HB_SIZE s_fileWriteAt( PHB_FILE pFile, const void * buffer, + HB_SIZE nSize, HB_FOFFSET nOffset ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_WRITEAT ); + hb_vmPush( pFile->pFileItm ); + hb_vmPushString( ( const char * ) buffer, nSize ); + hb_vmPushSize( nSize ); + hb_vmPushNumInt( ( HB_MAXINT ) nOffset ); + hb_vmDo( 4 ); + + return hb_parns( -1 ); +} + +static HB_BOOL s_fileTruncAt( PHB_FILE pFile, HB_FOFFSET nOffset ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_TRUNCAT ); + hb_vmPush( pFile->pFileItm ); + hb_vmPushNumInt( ( HB_MAXINT ) nOffset ); + hb_vmDo( 2 ); + + return hb_parl( -1 ); +} + +static HB_FOFFSET s_fileSeek( PHB_FILE pFile, HB_FOFFSET nOffset, + HB_USHORT uiFlags ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_SEEK ); + hb_vmPush( pFile->pFileItm ); + hb_vmPushNumInt( ( HB_MAXINT ) nOffset ); + hb_vmPushInteger( uiFlags ); + hb_vmDo( 3 ); + + return ( HB_FOFFSET ) hb_parnint( -1 ); +} + +static HB_FOFFSET s_fileSize( PHB_FILE pFile ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_SIZE ); + hb_vmPush( pFile->pFileItm ); + hb_vmDo( 1 ); + + return ( HB_FOFFSET ) hb_parnint( -1 ); +} + +static HB_BOOL s_fileEof( PHB_FILE pFile ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_EOF ); + hb_vmPush( pFile->pFileItm ); + hb_vmDo( 1 ); + + return hb_parl( -1 ); +} + +static void s_fileFlush( PHB_FILE pFile, HB_BOOL fDirty ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_FLUSH ); + hb_vmPush( pFile->pFileItm ); + hb_vmPushLogical( fDirty ); + hb_vmDo( 2 ); +} + +static void s_fileCommit( PHB_FILE pFile ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_COMMIT ); + hb_vmPush( pFile->pFileItm ); + hb_vmDo( 1 ); +} + +static HB_BOOL s_fileConfigure( PHB_FILE pFile, int iIndex, PHB_ITEM pValue ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_CONFIGURE ); + hb_vmPush( pFile->pFileItm ); + hb_vmPushInteger( iIndex ); + if( pValue != NULL ) + hb_vmPush( pValue ); + hb_vmDo( pValue != NULL ? 3 : 2 ); + + return hb_parl( -1 ); +} + +static HB_FHANDLE s_fileHandle( PHB_FILE pFile ) +{ + PHB_IOUSR pIO = ( PHB_IOUSR ) pFile->pFuncs; + + s_pushMethod( pIO, IOUSR_HANDLE ); + hb_vmPush( pFile->pFileItm ); + hb_vmDo( 1 ); + + return ( HB_FHANDLE ) hb_parns( -1 ); +} + +static const HB_FILE_FUNCS s_fileFuncs = +{ + s_fileAccept, + + s_fileExists, + s_fileDelete, + s_fileRename, + s_fileCopy, + + s_fileDirExists, + s_fileDirMake, + s_fileDirRemove, + s_fileDirSpace, + s_fileDirectory, + + s_fileTimeGet, + s_fileTimeSet, + s_fileAttrGet, + s_fileAttrSet, + + s_fileLink, + s_fileLinkSym, + s_fileLinkRead, + + s_fileOpen, + s_fileClose, + s_fileLock, + s_fileLockTest, + s_fileRead, + s_fileWrite, + s_fileReadAt, + s_fileWriteAt, + s_fileTruncAt, + s_fileSeek, + s_fileSize, + s_fileEof, + s_fileFlush, + s_fileCommit, + s_fileConfigure, + s_fileHandle +}; + +typedef void * ( * HB_FILE_FUNC )( PHB_FILE ); + +/* IOUSR_Register( , ) */ +HB_FUNC( IOUSR_REGISTER ) +{ + PHB_ITEM pMthItm = hb_param( 1, HB_IT_ARRAY ); + const char * pszPrefix = hb_parc( 2 ); + + if( pMthItm && pszPrefix && *pszPrefix ) + { + HB_SIZE nMethods = hb_arrayLen( pMthItm ), nAt; + + if( nMethods > IOUSR_METHODCOUNT ) + nMethods = IOUSR_METHODCOUNT; + + for( nAt = 1; nAt <= nMethods; ++nAt ) + { + PHB_ITEM pSymItm = hb_arrayGetItemPtr( pMthItm, nAt ); + + if( ! HB_IS_NIL( pSymItm ) && ! HB_IS_SYMBOL( pSymItm ) ) + break; + } + + if( nAt > nMethods ) + { + PHB_IOUSR pIO = s_iousrAddNew( pszPrefix ); + + if( pIO != NULL ) + { + const HB_FILE_FUNC * pDummyFunc; + HB_FILE_FUNC * pFunction; + + pDummyFunc = ( const HB_FILE_FUNC * ) &s_fileFuncs; + pFunction = ( HB_FILE_FUNC * ) &pIO->funcs; + for( nAt = 1; nAt <= nMethods; ++nAt, pDummyFunc++, pFunction++ ) + { + pIO->prg_funcs[ nAt - 1 ] = hb_arrayGetSymbol( pMthItm, nAt ); + if( nAt == 1 || pIO->prg_funcs[ nAt - 1 ] != NULL ) + * pFunction = * pDummyFunc; + } + if( ! hb_fileRegisterPart( &pIO->funcs ) ) + pIO = NULL; + } + if( pIO == NULL ) + s_errRT_IOUSR( EG_ARG, 1003, pszPrefix ); + } + else + s_errRT_IOUSR( EG_ARG, 1002, pszPrefix ); + } + else + s_errRT_IOUSR( EG_ARG, 1001, "Argument error" ); +} + +/* IOUSR_SetError( [ [, ]] ) -> */ +HB_FUNC( IOUSR_SETERROR ) +{ + HB_ERRCODE errCode = hb_fsError(); + + if( HB_ISNUM( 1 ) ) + { + HB_ERRCODE errCode = ( HB_ERRCODE ) hb_parni( 1 ); + if( errCode != 0 ) + errCode += ( HB_ERRCODE ) hb_parni( 2 ); + hb_fsSetError( errCode ); + } + + hb_retni( errCode ); +} diff --git a/src/vm/runner.c b/src/vm/runner.c index 9d496214d4..a0407fe5c8 100644 --- a/src/vm/runner.c +++ b/src/vm/runner.c @@ -75,7 +75,7 @@ typedef struct { char * szName; /* Name of the function */ - PHB_PCODEFUNC pCodeFunc; /* Dynamic function info */ + HB_PCODEFUNC pcodeFunc; /* Dynamic function info */ HB_BYTE * pCode; /* P-code */ } HB_DYNF, * PHB_DYNF; @@ -299,17 +299,15 @@ static void hb_hrbUnLoad( PHRB_BODY pHrbBody ) PHB_DYNS pDyn; if( pHrbBody->pDynFunc[ ul ].szName && - pHrbBody->pDynFunc[ ul ].pCodeFunc ) + pHrbBody->pDynFunc[ ul ].pcodeFunc.pCode ) { pDyn = hb_dynsymFind( pHrbBody->pDynFunc[ ul ].szName ); if( pDyn && pDyn->pSymbol->value.pCodeFunc == - pHrbBody->pDynFunc[ ul ].pCodeFunc ) + &pHrbBody->pDynFunc[ ul ].pcodeFunc ) { pDyn->pSymbol->value.pCodeFunc = NULL; } } - if( pHrbBody->pDynFunc[ ul ].pCodeFunc ) - hb_xfree( pHrbBody->pDynFunc[ ul ].pCodeFunc ); if( pHrbBody->pDynFunc[ ul ].pCode ) hb_xfree( pHrbBody->pDynFunc[ ul ].pCode ); if( pHrbBody->pDynFunc[ ul ].szName ) @@ -452,9 +450,8 @@ static PHRB_BODY hb_hrbLoad( const char * szHrbBody, HB_SIZE nBodySize, HB_USHOR memcpy( ( char * ) pDynFunc[ ul ].pCode, szHrbBody + nBodyOffset, nSize ); nBodyOffset += nSize; - pDynFunc[ ul ].pCodeFunc = ( PHB_PCODEFUNC ) hb_xgrab( sizeof( HB_PCODEFUNC ) ); - pDynFunc[ ul ].pCodeFunc->pCode = pDynFunc[ ul ].pCode; - pDynFunc[ ul ].pCodeFunc->pSymbols = pSymRead; + pDynFunc[ ul ].pcodeFunc.pCode = pDynFunc[ ul ].pCode; + pDynFunc[ ul ].pcodeFunc.pSymbols = pSymRead; } if( ul < pHrbBody->ulFuncs ) @@ -479,7 +476,7 @@ static PHRB_BODY hb_hrbLoad( const char * szHrbBody, HB_SIZE nBodySize, HB_USHOR } else { - pSymRead[ ul ].value.pCodeFunc = ( PHB_PCODEFUNC ) pHrbBody->pDynFunc[ nPos ].pCodeFunc; + pSymRead[ ul ].value.pCodeFunc = &pHrbBody->pDynFunc[ nPos ].pcodeFunc; pSymRead[ ul ].scope.value |= HB_FS_PCODEFUNC | HB_FS_LOCAL | ( usBind == HB_HRB_BIND_FORCELOCAL ? HB_FS_STATIC : 0 ); } @@ -556,7 +553,8 @@ static PHRB_BODY hb_hrbLoad( const char * szHrbBody, HB_SIZE nBodySize, HB_USHOR pHrbBody->pSymRead = pHrbBody->pModuleSymbols->pModuleSymbols; hb_xfree( pSymRead ); - pHrbBody->fInit = HB_TRUE; + if( ! pHrbBody->pModuleSymbols->fInitStatics ) + pHrbBody->fInit = HB_TRUE; } else { diff --git a/tests/iotcp.prg b/tests/iotcp.prg new file mode 100644 index 0000000000..6f38d04c29 --- /dev/null +++ b/tests/iotcp.prg @@ -0,0 +1,262 @@ +/* + * Harbour Project source code: + * Harbour FILE IO redirector: IOTCP + * example of IOUSR usage + * + * Copyright 2014 Przemyslaw Czerpak + * www - http://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.txt. 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 "hbiousr.ch" +#include "hbsocket.ch" +#include "fileio.ch" +#include "error.ch" + +ANNOUNCE HB_IOTCP + +#define IOTCP_SCOKET 1 +#define IOTCP_EOF 2 +#define IOTCP_TIMEOUT 3 + +#define IOTCP_ERRORBASE 10000 + +#define IOTCP_NEW( sd, tout ) { sd, .F., tout } + +/* + * NOTE: cDefExt, cPaths and oError can be NIL + */ +STATIC FUNCTION IOTCP_Open( cFile, cDefExt, nFlags, cPaths, oError ) + LOCAL nTimeout := 10000, nError, hSock := NIL, aFile := NIL + LOCAL cAddr, cRest, nPort := 0, aAddr + LOCAL nAt + + HB_SYMBOL_UNUSED( cDefExt ) + HB_SYMBOL_UNUSED( cPaths ) + + /* strip "tcp:" prefix */ + cAddr := substr( cFile, 5 ) + + /* take host, port and timeout from ":[:]" string */ + IF ( nAt := At( ":", cAddr ) ) > 1 + cRest := SubStr( cAddr, nAt + 1 ) + cAddr := Left( cAddr, nAt - 1 ) + IF ( nPort := Val( cRest ) ) > 0 + WHILE IsDigit( cRest ) + cRest := SubStr( cRest, 2 ) + ENDDO + IF cRest = ":" + cRest := SubStr( cRest, 2 ) + IF IsDigit( cRest ) .AND. ( nTimeout := Val( cRest ) ) > 0 + WHILE IsDigit( cRest ) + cRest := SubStr( cRest, 2 ) + ENDDO + IF cRest = ":" + cRest := "" + ENDIF + ENDIF + ENDIF + IF ! cRest == "" + nPort := 0 + ENDIF + ENDIF + ENDIF + + IF nPort != 0 + IF !Empty( aAddr := hb_socketResolveInetAddr( cAddr, nPort ) ) .AND. ; + !Empty( hSock := hb_socketOpen() ) + hb_socketSetKeepAlive( hSock, .T. ) + IF hb_socketConnect( hSock, aAddr, nTimeout ) + IF !Empty( hSock ) + SWITCH hb_bitAnd( nFlags, hb_bitOr( FO_READ, FO_WRITE, FO_READWRITE ) ) + CASE FO_READ + hb_socketShutdown( hSock, HB_SOCKET_SHUT_WR ) + EXIT + CASE FO_WRITE + hb_socketShutdown( hSock, HB_SOCKET_SHUT_RD ) + EXIT + ENDSWITCH + aFile := IOTCP_NEW( hSock, nTimeout ) + ENDIF + ENDIF + IF aFile == NIL + nError := hb_socketGetError() + hb_socketClose( hSock ) + ENDIF + ENDIF + IF nError == 0 .AND. aFile == NIL + nError := hb_socketGetError() + ENDIF + ELSE + nError := HB_SOCKET_ERR_WRONGADDR + ENDIF + + IOUSR_SetError( nError, IOTCP_ERRORBASE ) + + IF oError != NIL + oError:filename := cFile + IF aFile == NIL + oError:osCode := nError + oError:genCode := EG_OPEN + ENDIF + ENDIF + +RETURN aFile /* if aFile == NIL indicates error */ + + +STATIC FUNCTION IOTCP_Close( aFile ) + hb_socketClose( aFile[ IOTCP_SCOKET ] ) + IOUSR_SetError( hb_socketGetError(), IOTCP_ERRORBASE ) +RETURN NIL + + +STATIC FUNCTION IOTCP_Read( aFile, /*@*/ cData, nLen, nTimeout ) + LOCAL nRead := 0, nError + + IF !aFile[ IOTCP_EOF ] + IF nTimeout == -1 + nTimeout := aFile[ IOTCP_TIMEOUT ] + ENDIF + nRead := hb_socketRecv( aFile[ IOTCP_SCOKET ], @cData, nLen, 0, nTimeout ) + nError := hb_socketGetError() + IF nRead <= 0 + SWITCH nError + CASE HB_SOCKET_ERR_TIMEOUT + CASE HB_SOCKET_ERR_AGAIN + CASE HB_SOCKET_ERR_TRYAGAIN + EXIT + OTHERWISE + aFile[ IOTCP_EOF ] := .F. + ENDSWITCH + nRead := 0 + ENDIF + IOUSR_SetError( nError, IOTCP_ERRORBASE ) + ENDIF +RETURN nRead + + +STATIC FUNCTION IOTCP_Write( aFile, cData, nLen, nTimeout ) + IF nTimeout == -1 + nTimeout := aFile[ IOTCP_TIMEOUT ] + ENDIF + nLen := hb_socketSend( aFile[ IOTCP_SCOKET ], cData, nLen, 0, nTimeout ) + IOUSR_SetError( hb_socketGetError(), IOTCP_ERRORBASE ) +RETURN iif( nLen < 0, 0, nLen ) + + +STATIC FUNCTION IOTCP_Eof( aFile ) +RETURN aFile[ IOTCP_EOF ] + + +STATIC FUNCTION IOTCP_Configure( aFile, nIndex, xValue ) + HB_SYMBOL_UNUSED( aFile ) + HB_SYMBOL_UNUSED( nIndex ) + HB_SYMBOL_UNUSED( xValue ) +RETURN .F. + + +STATIC FUNCTION IOTCP_Handle( aFile ) + IOUSR_SetError( 0, IOTCP_ERRORBASE ) +RETURN hb_socketGetFD( aFile[ IOTCP_SCOKET ] ) + + +INIT PROCEDURE CLIPINIT + LOCAL aMethods[ IOUSR_METHODCOUNT ] + + aMethods[ IOUSR_OPEN ] := @IOTCP_Open() + aMethods[ IOUSR_CLOSE ] := @IOTCP_Close() + aMethods[ IOUSR_READ ] := @IOTCP_Read() + aMethods[ IOUSR_WRITE ] := @IOTCP_Write() + aMethods[ IOUSR_EOF ] := @IOTCP_Eof() + aMethods[ IOUSR_CONFIGURE ] := @IOTCP_Configure() + aMethods[ IOUSR_HANDLE ] := @IOTCP_Handle() + + IOUSR_Register( aMethods, "tcp:" ) +RETURN + + + +/* test code */ + +REQUEST HB_IOTCP + +PROCEDURE Main( cAddr ) + LOCAL hFile, cData, cSend, cEOL, nLen + + IF Empty( cAddr ) + cAddr := "tcp:smtp.gmail.com:25:10000" + ENDIF + + ? "open:", cAddr + hFile := hb_vfOpen( cAddr, FO_READWRITE ) + IF Empty( hFile ) + ? "Open error:", FError() + ELSE + cData := Space( 1024 ) + cEOL := e"\r\n" + IF ( nLen := hb_vfRead( hFile, @cData,, 10000 ) ) > 0 + ? "<< " + StrTran( HB_BLeft( cData, nLen ), cEOL, cEOL + "<< " ) + ENDIF + cSend := "EHLO" + cEOL + nLen := hb_vfWrite( hFile, cSend,, 1000 ) + ? ">> " + StrTran( HB_BLeft( cSend, nLen ), cEOL, cEOL + ">> " ) + IF nLen != hb_BLen( cSend ) + ? "WRITE ERROR:", FError() + ENDIF + IF ( nLen := hb_vfRead( hFile, @cData,, 10000 ) ) > 0 + ? "<< " + StrTran( HB_BLeft( cData, nLen ), cEOL, cEOL + "<< " ) + ENDIF + cSend := "QUIT" + cEOL + nLen := hb_vfWrite( hFile, cSend,, 1000 ) + ? ">> " + StrTran( HB_BLeft( cSend, nLen ), cEOL, cEOL + ">> " ) + IF nLen != hb_BLen( cSend ) + ? "WRITE ERROR:", FError() + ENDIF + IF ( nLen := hb_vfRead( hFile, @cData,, 10000 ) ) > 0 + ? "<< " + StrTran( HB_BLeft( cData, nLen ), cEOL, cEOL + "<< " ) + ENDIF + hb_vfClose( hFile ) + ? "closed, error:", FError() + ENDIF + ? + WAIT +RETURN