Files
harbour-core/contrib/hbtpathy/telepath.prg
Viktor Szakats 5a2a287752 2017-09-08 16:00 UTC Viktor Szakats (vszakats users.noreply.github.com)
* *
    * partial sync with the 3.4 fork codebase. These are the things
      synces for the most part:
      - copyright headers
      - grammar/typos in comments and some readmes
      - comment/whitespace/decorations
      - variable scoping in C files
      - DO CASE/SWITCH and some other alternate syntax usage
      - minimal amount of human readable text in strings
      - minor code updates
      - HB_TRACE() void * casts for pointers and few other changes to
        avoid C compiler warnings
      - various other, minor code cleanups
      - only Harbour/C code/headers were touched in src, utils, contrib,
        include. No 3rd party code, no make files, and with just a few
        exceptions, no 'tests' code was touched.
      - certain components were not touched were 3.4 diverged too much
        already, like f.e. hbmk2, hbssl, hbcurl, hbexpat
      - the goal was that no actual program logic should be altered by
        these changes. Except some possible minor exceptions, any such
        change is probably a bug in this patch.
      It's a massive patch, if you find anything broken after it, please
      open an Issue with the details. Build test was done on macOS.
      The goal is make it easier to see what actual code/logic was changed
      in 3.4 compared to 3.2 and to make patches easier to apply in both
      ways.
2017-09-08 16:25:13 +00:00

745 lines
18 KiB
Plaintext

/*
* Telepathy emulation library
*
* Copyright 2000-2001 Dan Levitt <dan@boba-fett.net>
* Copyright 2004-2005 Maurilio Longo <maurilio.longo@libero.it>
* Copyright 2010 Viktor Szakats (vszakats.net/harbour)
*
* 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 program; see the file LICENSE.txt. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
*
* 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 is based upon a library originally made by Dan Levitt <dan@boba-fett.net>
The original files have been committed as v1.0. So you can always retrieve them
(see VCS docs on how to) */
#include "telepath.ch"
#include "hbcom.ch"
#define TPFP_NAME 1 /* Structure of ports array */
#define TPFP_HANDLE 2
#define TPFP_BAUD 3
#define TPFP_DBITS 4
#define TPFP_PARITY 5
#define TPFP_SBITS 6
#define TPFP_OC 7 /* Open/Close Flag */
#define TPFP_INBUF 8
#define TPFP_INBUF_SIZE 9 /* Size of input buffer */
THREAD STATIC t_aPorts // Array with port info
THREAD STATIC t_nErrorCode := 0 // Error code from last operation, 0 if no error
FUNCTION tp_baud( nPort, nNewBaud )
hb_default( @nNewBaud, 0 )
IF ! isport( nPort ) .OR. Empty( t_aPorts[ nPort, TPFP_NAME ] )
RETURN TE_NOPORT
ENDIF
IF ! isopenport( nPort )
RETURN 0
ENDIF
IF nNewBaud > 0
IF hb_comInit( t_aPorts[ nPort, TPFP_HANDLE ], nNewBaud, t_aPorts[ nPort, TPFP_PARITY ], t_aPorts[ nPort, TPFP_DBITS ], t_aPorts[ nPort, TPFP_SBITS ] )
t_aPorts[ nPort, TPFP_BAUD ] := nNewBaud
ELSE
// set error code
ENDIF
ENDIF
RETURN t_aPorts[ nPort, TPFP_BAUD ]
FUNCTION tp_inkey( ... )
RETURN Inkey( ... )
PROCEDURE tp_delay( nTime )
hb_default( @nTime, 0 )
IF nTime < 0
RETURN
ELSEIF nTime > 1800
nTime := 1800
ENDIF
hb_idleSleep( nTime )
RETURN
FUNCTION tp_close( nPort, nTimeout )
hb_default( @nTimeout, 0 )
/* Clipper returns 0 even if a port is not open */
IF ! isopenport( nPort )
RETURN 0
ENDIF
IF nTimeout > 0
tp_flush( nPort, nTimeout )
ENDIF
IF t_aPorts[ nPort, TPFP_HANDLE ] >= 0
hb_comClose( t_aPorts[ nPort, TPFP_HANDLE ] )
/* Port parameters should stay the same for the case the port
gets reopened
*/
t_aPorts[ nPort, TPFP_OC ] := .F.
t_aPorts[ nPort, TPFP_INBUF ] := ""
t_aPorts[ nPort, TPFP_HANDLE ] := -1
ENDIF
RETURN 0
FUNCTION tp_reopen( nPort, nInSize, nOutSize )
LOCAL nBaud, nData, cParity, nStop, cPortName
IF ! isport( nPort ) .OR. Empty( t_aPorts[ nPort, TPFP_NAME ] )
RETURN TE_NOPORT
ENDIF
cPortname := t_aPorts[ nPort, TPFP_NAME ]
nBaud := t_aPorts[ nPort, TPFP_BAUD ]
nData := t_aPorts[ nPort, TPFP_DBITS ]
cParity := t_aPorts[ nPort, TPFP_PARITY ]
nStop := t_aPorts[ nPort, TPFP_SBITS ]
RETURN tp_open( nPort, nInSize, nOutSize, nBaud, nData, cParity, nStop, cPortName )
FUNCTION tp_open( nPort, nInSize, nOutSize, nBaud, nData, cParity, nStop, cPortname )
IF ! isport( nPort )
RETURN TE_NOPORT
ENDIF
hb_default( @nInSize, 1536 )
hb_default( @nOutSize, 1536 )
hb_default( @nBaud, 1200 )
hb_default( @nData, 8 )
hb_default( @cParity, "N" )
hb_default( @nStop, 1 )
IF HB_ISSTRING( cPortname )
hb_comSetDevice( nPort, cPortname )
ENDIF
t_aPorts[ nPort, TPFP_NAME ] := cPortname
t_aPorts[ nPort, TPFP_BAUD ] := nBaud
t_aPorts[ nPort, TPFP_DBITS ] := nData
t_aPorts[ nPort, TPFP_PARITY ] := cParity
t_aPorts[ nPort, TPFP_SBITS ] := nStop
t_aPorts[ nPort, TPFP_OC ] := .F.
t_aPorts[ nPort, TPFP_INBUF ] := ""
t_aPorts[ nPort, TPFP_INBUF_SIZE ] := nInSize
t_aPorts[ nPort, TPFP_HANDLE ] := -1
IF hb_comOpen( nPort )
t_aPorts[ nPort, TPFP_HANDLE ] := nPort
IF hb_comInit( t_aPorts[ nPort, TPFP_HANDLE ], t_aPorts[ nPort, TPFP_BAUD ], t_aPorts[ nPort, TPFP_PARITY ], t_aPorts[ nPort, TPFP_DBITS ], t_aPorts[ nPort, TPFP_SBITS ] )
t_aPorts[ nPort, TPFP_OC ] := .T.
RETURN 0
ELSE
tp_close( t_aPorts[ nPort, TPFP_HANDLE ] )
RETURN TE_PARAM
ENDIF
ENDIF
t_aPorts[ nPort, TPFP_NAME ] := ""
t_aPorts[ nPort, TPFP_HANDLE ] := -1
t_aPorts[ nPort, TPFP_BAUD ] := 1200
t_aPorts[ nPort, TPFP_DBITS ] := 8
t_aPorts[ nPort, TPFP_PARITY ] := "N"
t_aPorts[ nPort, TPFP_SBITS ] := 1
t_aPorts[ nPort, TPFP_OC ] := .F.
t_aPorts[ nPort, TPFP_INBUF ] := ""
t_aPorts[ nPort, TPFP_INBUF_SIZE ] := 0
RETURN TE_CONFL /* maybe should return something different? */
FUNCTION tp_recv( nPort, nLength, nTimeout )
LOCAL nDone
LOCAL cRet
IF ! HB_ISNUMERIC( nLength )
nLength := t_aPorts[ nPort, TPFP_INBUF_SIZE ]
ENDIF
hb_default( @nTimeout, 0 )
FetchChars( nPort )
nDone := Seconds() + iif( nTimeout >= 0, nTimeout, 0 )
DO WHILE Len( t_aPorts[ nPort, TPFP_INBUF ] ) < nLength .AND. ;
( nTimeout < 0 .OR. Seconds() < nDone )
IF ! tp_idle()
FetchChars( nPort )
ELSE
EXIT
ENDIF
ENDDO
IF nLength > Len( t_aPorts[ nPort, TPFP_INBUF ] )
cRet := t_aPorts[ nPort, TPFP_INBUF ]
t_aPorts[ nPort, TPFP_INBUF ] := ""
ELSE
cRet := SubStr( t_aPorts[ nPort, TPFP_INBUF ], 1, nLength )
t_aPorts[ nPort, TPFP_INBUF ] := SubStr( t_aPorts[ nPort, TPFP_INBUF ], nLength + 1 )
ENDIF
RETURN cRet
FUNCTION tp_send( nPort, cString, nTimeout )
hb_default( @cString, "" )
hb_default( @nTimeout, 0 )
IF ! isopenport( nPort )
RETURN 0
ENDIF
IF Len( cString ) == 0
RETURN 0
ENDIF
RETURN hb_comSend( t_aPorts[ nPort, TPFP_HANDLE ], cString,, nTimeout )
FUNCTION tp_sendsub( nPort, cString, nStart, nLength, nTimeout )
hb_default( @nStart, 1 )
IF ! HB_ISNUMERIC( nLength )
nLength := Len( cString )
ENDIF
RETURN tp_send( nPort, SubStr( cString, nStart, nLength ), nTimeout )
FUNCTION tp_recvto( nPort, cDelim, nMaxlen, nTimeout )
LOCAL cChar
LOCAL nAt
LOCAL nStartPos := 1, nFirst := 0
LOCAL nDone, cRet := ""
IF ! isopenport( nPort )
RETURN ""
ENDIF
IF ! HB_ISSTRING( cDelim ) .OR. Len( cDelim ) == 0
RETURN ""
ENDIF
hb_default( @nMaxLen, 64999 ) /* dos telepathy def. on xharbour could be higher */
hb_default( @nTimeout, 0 )
FetchChars( nPort )
/* Telepathy ng: [...] If nTimeout is omitted or zero, reads until finding the
delimiter or the input buffer is empty. */
IF nTimeout == 0 .AND. Len( t_aPorts[ nPort, TPFP_INBUF ] ) == 0
RETURN ""
ENDIF
nDone := Seconds() + iif( nTimeout >= 0, nTimeout, 0 )
DO WHILE ( nTimeout < 0 .OR. Seconds() < nDone )
IF Len( cDelim ) == 1
nAt := hb_At( cDelim, t_aPorts[ nPort, TPFP_INBUF ], nStartPos )
IF nAt > 0 .AND. iif( nFirst > 0, nAt < nFirst, .T. )
nFirst := nAt
ENDIF
ELSE
FOR EACH cChar IN cDelim
nAt := hb_At( cChar, t_aPorts[ nPort, TPFP_INBUF ], nStartPos )
IF nAt > 0 .AND. iif( nFirst > 0, nAt < nFirst, .T. )
nFirst := nAt
ENDIF
NEXT
ENDIF
// I've found it
IF nFirst > 0
EXIT
ELSE
// Next loop I don't need to search that part of the input buffer that
// I've already just searched for
nStartPos := Max( Len( t_aPorts[ nPort, TPFP_INBUF ] ), 1 )
// I've read more characters than I'm allowed to, so I exit
IF nStartPos >= nMaxLen
EXIT
ENDIF
IF ! tp_idle()
FetchChars( nPort )
ELSE
EXIT
ENDIF
ENDIF
ENDDO
IF nFirst > 0
cRet := Left( t_aPorts[ nPort, TPFP_INBUF ], nFirst )
t_aPorts[ nPort, TPFP_INBUF ] := SubStr( t_aPorts[ nPort, TPFP_INBUF ], nFirst + 1 )
ENDIF
RETURN cRet
/*
here's an improvement over original TP... you can "lookfor" a string
here rather than just a char. yay me.
of course, if you're using clipper/tp code and you search for a single char it will work
the same.
*/
FUNCTION tp_lookfor( nPort, cLookfor )
IF ! isopenport( nPort )
RETURN 0
ENDIF
FetchChars( nPort )
RETURN At( cLookfor, t_aPorts[ nPort, TPFP_INBUF ] )
FUNCTION tp_inchrs( nPort )
IF ! isopenport( nPort )
RETURN 0
ENDIF
FetchChars( nPort )
RETURN Len( t_aPorts[ nPort, TPFP_INBUF ] )
FUNCTION tp_infree( nPort )
IF ! isopenport( nPort )
RETURN 0
ENDIF
RETURN __tp_infree( t_aPorts[ nPort, TPFP_HANDLE ] )
FUNCTION tp_outfree( nPort )
IF ! isopenport( nPort )
RETURN 0
ENDIF
RETURN __tp_outfree( t_aPorts[ nPort, TPFP_HANDLE ] )
PROCEDURE tp_clearin( nPort )
IF isopenport( nPort )
FetchChars( nPort )
t_aPorts[ nPort, TPFP_INBUF ] := ""
ENDIF
RETURN
PROCEDURE tp_clrkbd()
CLEAR TYPEAHEAD
RETURN
FUNCTION tp_crc16( cString )
RETURN hb_ByteSwapW( hb_CRCCT( cString ) ) /* swap lo and hi bytes */
FUNCTION tp_crc32( cString )
RETURN hb_CRC32( cString )
FUNCTION tp_waitfor( ... ) /* nPort, nTimeout, acList|cString..., lIgnorecase */
LOCAL aParam := hb_AParams()
LOCAL nPort // , nTimeout, lIgnorecase
nPort := aParam[ 1 ]
// nTimeout := aParam[ 2 ]
// lIgnorecase := aParam[ Len( aParam ) ]
IF ! isopenport( nPort )
RETURN 0
ENDIF
// hb_default( @nTimeout, -1 )
// hb_default( @lIgnorecase, .F. )
#if 0
IF ntimeout < 0
nDone := _clock() + 999999
ELSEIF ntimeout == 0
nDone := 4
ELSE
nDone := _clock() + nTimeout
ENDIF
DO WHILE ( nDone > _clock() .OR. nFirst == 100000 ) .AND. ! tp_idle()
IF nFirst == 100000
nFirst := 99999
ENDIF
FetchChars( nPort )
FOR x := 1 TO Len( acList )
IF lIgnorecase
nAt := At( Upper( acList[ x ] ), Upper( t_aPorts[ nPort, TPFP_INBUF ] ) )
ELSE
nAt := At( acList[ x ], t_aPorts[ nPort, TPFP_INBUF ] )
ENDIF
IF nAt > 0 .AND. nAt < nFirst
nFirst := nAt
nRet := x
ENDIF
NEXT
IF nFirst < 64000
EXIT
ENDIF
#if 0
sched_yield() // C level function
#endif
ENDDO
IF nFirst < 64000
tp_recv( nPort, nAt + Len( acList[ nRet ] ) )
RETURN nRet
ENDIF
#endif
RETURN 0
/* We cannot set, well, _I_ think we cannot, CTS without setting RTS flowcontrol, so this
function and tp_ctrlrts() do the same thing, that is set/reset CRTSCTS flowcontol */
FUNCTION tp_ctrlcts( nPort, nNewCtrl )
LOCAL nCurValue
LOCAL nFlag
IF ! isopenport( nPort )
RETURN 0
ENDIF
IF hb_comFlowControl( t_aPorts[ nPort, TPFP_HANDLE ], @nCurValue )
nFlag := hb_bitOr( HB_COM_FLOW_IRTSCTS, HB_COM_FLOW_ORTSCTS )
IF HB_ISNUMERIC( nNewCtrl )
IF nNewCtrl == 0
nNewCtrl := hb_bitAnd( nCurValue, hb_bitNot( nFlag ) )
ELSE
nNewCtrl := hb_bitOr( nCurValue, nFlag )
ENDIF
hb_comFlowControl( t_aPorts[ nPort, TPFP_HANDLE ], NIL, nNewCtrl )
ENDIF
nCurValue := iif( hb_bitAnd( nCurValue, nFlag ) != 0, 1, 0 )
ENDIF
RETURN nCurValue
// Simply calls tp_ctrlcts()
FUNCTION tp_ctrlrts( nPort, nNewCtrl )
RETURN tp_ctrlcts( nPort, nNewCtrl )
// telepathy says...
// returns old dtr value 0,1,2
// sets to 0 = dtr off, 1 dtr on, 2 = dtr flow control autotoggle
// I don't support 2. who uses dtr for flow control anyway...
FUNCTION tp_ctrldtr( nPort, nNewCtrl )
LOCAL nCurValue
LOCAL nFlag
IF ! isopenport( nPort )
RETURN 0
ENDIF
IF hb_comFlowControl( t_aPorts[ nPort, TPFP_HANDLE ], @nCurValue )
nFlag := hb_bitOr( HB_COM_FLOW_IDTRDSR, HB_COM_FLOW_ODTRDSR )
IF HB_ISNUMERIC( nNewCtrl )
IF nNewCtrl == 0
nNewCtrl := hb_bitAnd( nCurValue, hb_bitNot( nFlag ) )
ELSE
nNewCtrl := hb_bitOr( nCurValue, nFlag )
ENDIF
hb_comFlowControl( t_aPorts[ nPort, TPFP_HANDLE ], NIL, nNewCtrl )
ENDIF
nCurValue := iif( hb_bitAnd( nCurValue, nFlag ) != 0, 1, 0 )
ENDIF
RETURN nCurValue
FUNCTION tp_isdcd( nPort )
LOCAL nValue
IF ! isopenport( nPort )
RETURN .F.
ENDIF
hb_comMSR( nPort, @nValue )
RETURN hb_bitAnd( nValue, HB_COM_MSR_DCD ) != 0
FUNCTION tp_isri( nPort )
LOCAL nValue
IF ! isopenport( nPort )
RETURN .F.
ENDIF
hb_comMSR( nPort, @nValue )
RETURN hb_bitAnd( nValue, HB_COM_MSR_RI ) != 0
FUNCTION tp_isdsr( nPort )
LOCAL nValue
IF ! isopenport( nPort )
RETURN .F.
ENDIF
hb_comMSR( nPort, @nValue )
RETURN hb_bitAnd( nValue, HB_COM_MSR_DSR ) != 0
FUNCTION tp_iscts( nPort )
LOCAL nValue
IF ! isopenport( nPort )
RETURN .F.
ENDIF
hb_comMSR( nPort, @nValue )
RETURN hb_bitAnd( nValue, HB_COM_MSR_CTS ) != 0
FUNCTION tp_flush( nPort, nTimeout )
LOCAL nDone
hb_default( @nTimeout, -1 )
IF ! isopenport( nPort )
RETURN TE_CLOSED
ENDIF
IF nTimeout > 1800
nTimeout := 1800
ENDIF
nDone := Seconds() + iif( nTimeout >= 0, nTimeout, 0 )
DO WHILE tp_outfree( nPort ) > 0 .AND. ;
( nTimeout < 0 .OR. Seconds() < nDone )
hb_idleState()
ENDDO
RETURN iif( tp_outfree( nPort ) > 0, TE_TMOUT, 0 )
#if 0
// / sorry, but ctrldsr and ctrlcts will act like isdsr and iscts... if you want
// / flow control, talk to the system.
FUNCTION tp_ctrldsr( nPort )
RETURN tp_isdsr( nPort )
// / you can't do these things. try rc.serial
FUNCTION tp_shared()
RETURN 0
FUNCTION tp_setport()
RETURN 0
#endif
// internal (static) functions ---------------------------------------------------
STATIC FUNCTION isopenport( nPort )
IF ! isport( nPort )
RETURN .F.
ENDIF
RETURN t_aPorts[ nPort, TPFP_OC ]
STATIC FUNCTION isport( nPort )
IF ! HB_ISNUMERIC( nPort ) .OR. nPort < 1 .OR. nPort > TP_MAXPORTS
RETURN .F.
ENDIF
RETURN .T.
STATIC FUNCTION FetchChars( nPort )
LOCAL cStr
IF ! isopenport( nPort )
RETURN 0
ENDIF
cStr := Space( hb_comInputCount( t_aPorts[ nPort, TPFP_HANDLE ] ) )
hb_comRecv( t_aPorts[ nPort, TPFP_HANDLE ], @cStr )
t_aPorts[ nPort, TPFP_INBUF ] += cStr
RETURN Len( cStr )
INIT PROCEDURE _tpinit()
LOCAL x
IF t_aPorts == NIL
t_aPorts := Array( TP_MAXPORTS )
FOR x := 1 TO Len( t_aPorts )
// / port name, file handle, baud, data bits, parity, stop bits, Open?, input buffer, input buff.size
t_aPorts[ x ] := { "", -1, 1200, 8, "N", 1, .F., "", 0 }
NEXT
ENDIF
RETURN
#if 0
// you can uncomment the following section for compatability with TP code... I figured
// you'd probably want them commented so it won't compile so that you would see where
// you have potential incomplete port problems
FUNCTION tp_mstat()
RETURN ""
FUNCTION tp_szmodem()
RETURN 0
FUNCTION tp_noteoff()
RETURN 0
FUNCTION tp_ontime()
RETURN 0
FUNCTION tp_rzmodem()
RETURN 0
FUNCTION tp_error()
RETURN 0
FUNCTION tp_errmsg()
RETURN ""
FUNCTION tp_fifo()
RETURN 0
FUNCTION tp_outchrs()
RETURN 0
FUNCTION tp_keybd()
RETURN 0
// / tp_debug is not a real TP function. I included it so you can define your own debug
// / output function.
// / the point of the first parameter is a "debug level". I keep a system variable for how
// / much debuggning output is wanted and if the tp_debug parameter is a LOWER number than
// / the global debug level I print the message. Since I don't have your system globals,
// / I will ignore the first parameter and always print it.
// / I recommend you modify this function to suit your own debugging needs
FUNCTION tp_debug( nDebugLevel, cString )
? cString
RETURN NIL
#endif
PROCEDURE tp_uninstall()
/* NOTE: dummy function, solely for compatibility. */
RETURN
STATIC FUNCTION __TP_INFREE()
RETURN -1
STATIC FUNCTION __TP_OUTFREE()
RETURN -1
FUNCTION bin_and( ... )
RETURN hb_bitAnd( ... )
FUNCTION bin_or( ... )
RETURN hb_bitOr( ... )
FUNCTION bin_xor( ... )
RETURN hb_bitXor( ... )
FUNCTION bin_not( ... )
RETURN hb_bitNot( ... )