2013-04-18 14:58 UTC+0200 Przemysław Czerpak (druzus/at/poczta.onet.pl)
* .gitignore
! removed executable file permission
* package/mpkg_src.sh
! added executable file permission
* contrib/hbct/screen2.c
% use STR API for parameters instead of using local conversions
* contrib/hbct/token1.c
% small simplification
* contrib/hbnetio/netiosrv.c
+ respect timeout parameter also in send operation
* contrib/make.hb
! moved project name normalization and directory verification to
AddProject() function. It fixes HB_BUILD_ADDONS envvar functionality.
* doc/xhb-diff.txt
+ added new paragraph: DECLARATION AND INITIALIZATION OF VARIABLES
* src/rtl/hbcom.c
* added 3-rd parameter to TIOCEXCL and TIOCNXCL ioctl() codes
to pacify valgrind warnings
* include/hbexpra.c
! fixed compilation with HB_USE_ENUM_FUNCTIONS macro
* include/hbapicls.h
* include/hbcompdf.h
* include/hbexpra.c
* src/compiler/harbour.y
* src/vm/classes.c
+ added support for :__enumIsFirst() iterator message. It's opposite
to recently added :__enumIsLast()
+ added support for overloading :__enumIsFirst() and :__enumIsLast()
functionality in custom FOR EACH implementations
* include/hbcompdf.h
* include/hbexpra.c
* src/compiler/harbour.y
+ added support for reverting :__enumIsFirst() and :__enumIsLast()
messages in descendant FOR EACH loops. It's disabled now by 2 #if 0
but I think it should be discussed. Should we keep it enable it?
The answer is not trivial when FOR EACH is used to iterate some
objects. In general such names are confusing.
* src/compiler/harbour.yyc
* src/compiler/harbour.yyh
* regenerated using bison 2.5
* tests/foreach.prg
+ added :__enumIsFirst() to test code
* tests/foreach2.prg
! typo in comment
This commit is contained in:
0
.gitignore
vendored
Executable file → Normal file
0
.gitignore
vendored
Executable file → Normal file
@@ -10,6 +10,65 @@
|
||||
* Change, ! Fix, % Optimization, + Addition, - Removal, ; Comment
|
||||
*/
|
||||
|
||||
2013-04-18 14:58 UTC+0200 Przemysław Czerpak (druzus/at/poczta.onet.pl)
|
||||
* .gitignore
|
||||
! removed executable file permission
|
||||
|
||||
* package/mpkg_src.sh
|
||||
! added executable file permission
|
||||
|
||||
* contrib/hbct/screen2.c
|
||||
% use STR API for parameters instead of using local conversions
|
||||
|
||||
* contrib/hbct/token1.c
|
||||
% small simplification
|
||||
|
||||
* contrib/hbnetio/netiosrv.c
|
||||
+ respect timeout parameter also in send operation
|
||||
|
||||
* contrib/make.hb
|
||||
! moved project name normalization and directory verification to
|
||||
AddProject() function. It fixes HB_BUILD_ADDONS envvar functionality.
|
||||
|
||||
* doc/xhb-diff.txt
|
||||
+ added new paragraph: DECLARATION AND INITIALIZATION OF VARIABLES
|
||||
|
||||
* src/rtl/hbcom.c
|
||||
* added 3-rd parameter to TIOCEXCL and TIOCNXCL ioctl() codes
|
||||
to pacify valgrind warnings
|
||||
|
||||
* include/hbexpra.c
|
||||
! fixed compilation with HB_USE_ENUM_FUNCTIONS macro
|
||||
|
||||
* include/hbapicls.h
|
||||
* include/hbcompdf.h
|
||||
* include/hbexpra.c
|
||||
* src/compiler/harbour.y
|
||||
* src/vm/classes.c
|
||||
+ added support for :__enumIsFirst() iterator message. It's opposite
|
||||
to recently added :__enumIsLast()
|
||||
+ added support for overloading :__enumIsFirst() and :__enumIsLast()
|
||||
functionality in custom FOR EACH implementations
|
||||
|
||||
* include/hbcompdf.h
|
||||
* include/hbexpra.c
|
||||
* src/compiler/harbour.y
|
||||
+ added support for reverting :__enumIsFirst() and :__enumIsLast()
|
||||
messages in descendant FOR EACH loops. It's disabled now by 2 #if 0
|
||||
but I think it should be discussed. Should we keep it enable it?
|
||||
The answer is not trivial when FOR EACH is used to iterate some
|
||||
objects. In general such names are confusing.
|
||||
|
||||
* src/compiler/harbour.yyc
|
||||
* src/compiler/harbour.yyh
|
||||
* regenerated using bison 2.5
|
||||
|
||||
* tests/foreach.prg
|
||||
+ added :__enumIsFirst() to test code
|
||||
|
||||
* tests/foreach2.prg
|
||||
! typo in comment
|
||||
|
||||
2013-04-18 00:15 UTC+0300 Mindaugas Kavaliauskas (dbtopas/at/dbtopas.lt)
|
||||
* contrib/sddfb/core.c
|
||||
* removed unused assignment
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
*/
|
||||
|
||||
#include "hbapigt.h"
|
||||
#include "hbapistr.h"
|
||||
#include "hbdate.h"
|
||||
|
||||
HB_FUNC( SAYDOWN )
|
||||
@@ -107,23 +108,11 @@ HB_FUNC( SAYDOWN )
|
||||
hb_retc_null();
|
||||
}
|
||||
|
||||
static HB_WCHAR * ct_TextToWChar( const char * szText, HB_SIZE * pnLen )
|
||||
{
|
||||
HB_WCHAR wc;
|
||||
PHB_CODEPAGE cdp = hb_gtHostCP();
|
||||
HB_SIZE nIndex = 0, nI = 0;
|
||||
HB_WCHAR * pwc = ( HB_WCHAR * ) hb_xgrab( *pnLen * sizeof( HB_WCHAR ) );
|
||||
|
||||
while( HB_CDPCHAR_GET( cdp, szText, *pnLen, &nIndex, &wc ) )
|
||||
pwc[ nI ++ ] = wc;
|
||||
*pnLen = nI;
|
||||
|
||||
return pwc;
|
||||
}
|
||||
|
||||
HB_FUNC( SAYSPREAD )
|
||||
{
|
||||
HB_SIZE nLen = hb_parclen( 1 );
|
||||
HB_SIZE nLen;
|
||||
void * hText;
|
||||
const HB_WCHAR * pwText = hb_parstr_u16( 1, HB_CDP_ENDIAN_NATIVE, &hText, &nLen );
|
||||
|
||||
if( nLen )
|
||||
{
|
||||
@@ -144,9 +133,6 @@ HB_FUNC( SAYSPREAD )
|
||||
|
||||
if( iRow >= 0 && iCol >= 0 && iRow <= iMaxRow && iCol <= iMaxCol )
|
||||
{
|
||||
const char * szText = hb_parc( 1 );
|
||||
HB_WCHAR * pwc = ct_TextToWChar( szText, &nLen );
|
||||
|
||||
int iColor = hb_gtGetCurrColor();
|
||||
|
||||
nPos = nLen >> 1;
|
||||
@@ -161,7 +147,7 @@ HB_FUNC( SAYSPREAD )
|
||||
do
|
||||
{
|
||||
for( ul = 0; ul < nLen && iCol + ( int ) ul <= iMaxCol; ++ul )
|
||||
hb_gtPutChar( iRow, iCol + ( int ) ul, iColor, 0, pwc[ nPos + ul ] );
|
||||
hb_gtPutChar( iRow, iCol + ( int ) ul, iColor, 0, pwText[ nPos + ul ] );
|
||||
nLen += 2;
|
||||
if( lDelay )
|
||||
{
|
||||
@@ -173,16 +159,18 @@ HB_FUNC( SAYSPREAD )
|
||||
while( nPos-- && iCol-- );
|
||||
/* CT3 does not respect iCol in the above condition */
|
||||
hb_gtEndWrite();
|
||||
hb_xfree( pwc );
|
||||
}
|
||||
}
|
||||
hb_strfree( hText );
|
||||
|
||||
hb_retc_null();
|
||||
}
|
||||
|
||||
HB_FUNC( SAYMOVEIN )
|
||||
{
|
||||
HB_SIZE nLen = hb_parclen( 1 );
|
||||
HB_SIZE nLen;
|
||||
void * hText;
|
||||
const HB_WCHAR * pwText = hb_parstr_u16( 1, HB_CDP_ENDIAN_NATIVE, &hText, &nLen );
|
||||
|
||||
if( nLen )
|
||||
{
|
||||
@@ -204,17 +192,13 @@ HB_FUNC( SAYMOVEIN )
|
||||
|
||||
if( iRow >= 0 && iCol >= 0 && iRow <= iMaxRow && iCol <= iMaxCol )
|
||||
{
|
||||
const char * szText = hb_parc( 1 );
|
||||
HB_WCHAR * pwc = ct_TextToWChar( szText, &nLen );
|
||||
HB_WCHAR * pText = pwc;
|
||||
|
||||
int iColor = hb_gtGetCurrColor();
|
||||
|
||||
iNewCol = iCol + nLen;
|
||||
iNewCol = iCol + ( int ) nLen;
|
||||
if( fBack )
|
||||
iCol += nLen - 1;
|
||||
iCol += ( int ) nLen - 1;
|
||||
else
|
||||
pText += nLen - 1;
|
||||
pwText += ( int ) nLen - 1;
|
||||
nChars = 1;
|
||||
|
||||
hb_gtBeginWrite();
|
||||
@@ -225,15 +209,15 @@ HB_FUNC( SAYMOVEIN )
|
||||
if( iCol <= iMaxCol )
|
||||
{
|
||||
for( ul = 0; ul < nChars; ++ul )
|
||||
hb_gtPutChar( iRow, iCol + ( int ) ul, iColor, 0, pText[ ul ] );
|
||||
hb_gtPutChar( iRow, iCol + ( int ) ul, iColor, 0, pwText[ ul ] );
|
||||
}
|
||||
--iCol;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( ul = 0; ul < nChars; ++ul )
|
||||
hb_gtPutChar( iRow, iCol + ( int ) ul, iColor, 0, pText[ ul ] );
|
||||
--pText;
|
||||
hb_gtPutChar( iRow, iCol + ( int ) ul, iColor, 0, pwText[ ul ] );
|
||||
--pwText;
|
||||
}
|
||||
if( ( int ) nChars + iCol <= iMaxCol )
|
||||
++nChars;
|
||||
@@ -248,9 +232,9 @@ HB_FUNC( SAYMOVEIN )
|
||||
while( --nLen );
|
||||
hb_gtSetPos( iRow, iNewCol );
|
||||
hb_gtEndWrite();
|
||||
hb_xfree( pwc );
|
||||
}
|
||||
}
|
||||
hb_strfree( hText );
|
||||
|
||||
hb_retc_null();
|
||||
}
|
||||
@@ -435,8 +419,7 @@ HB_FUNC( STRSCREEN ) /* TODO: Unicode support */
|
||||
HB_FUNC( __HBCT_DSPTIME )
|
||||
{
|
||||
int iRow, iCol;
|
||||
int iColor;
|
||||
HB_SIZE nLen;
|
||||
int iColor, iLen;
|
||||
char szTime[ 10 ];
|
||||
|
||||
iRow = hb_parni( 1 );
|
||||
@@ -453,17 +436,17 @@ HB_FUNC( __HBCT_DSPTIME )
|
||||
iColor = hb_gtGetClearColor();
|
||||
|
||||
hb_dateTimeStr( szTime );
|
||||
nLen = 8;
|
||||
iLen = 8;
|
||||
|
||||
if( hb_parl( 3 ) )
|
||||
nLen -= 3;
|
||||
iLen -= 3;
|
||||
|
||||
if( hb_parl( 5 ) )
|
||||
{
|
||||
int iHour = ( szTime[ 0 ] - '0' ) * 10 + ( szTime[ 1 ] - '0' );
|
||||
|
||||
if( hb_parl( 6 ) )
|
||||
szTime[ nLen++ ] = iHour >= 12 ? 'p' : 'a';
|
||||
szTime[ iLen++ ] = iHour >= 12 ? 'p' : 'a';
|
||||
if( iHour > 12 )
|
||||
iHour -= 12;
|
||||
else if( iHour == 0 )
|
||||
@@ -475,5 +458,5 @@ HB_FUNC( __HBCT_DSPTIME )
|
||||
if( szTime[ 0 ] == '0' )
|
||||
szTime[ 0 ] = ' ';
|
||||
|
||||
hb_gtPutText( iRow, iCol, szTime, nLen, iColor );
|
||||
hb_gtPutText( iRow, iCol, szTime, iLen, iColor );
|
||||
}
|
||||
|
||||
@@ -136,27 +136,23 @@ static void do_token1( int iSwitch )
|
||||
sSeparatorStrLen = sc_sSeparatorStrLen;
|
||||
}
|
||||
|
||||
/* token counter */
|
||||
if( iSwitch != DO_TOKEN1_NUMTOKEN )
|
||||
nTokenCounter = hb_parns( 3 );
|
||||
if( nTokenCounter == 0 )
|
||||
nTokenCounter = HB_SIZE_MAX;
|
||||
|
||||
/* skip width */
|
||||
if( iSwitch == DO_TOKEN1_NUMTOKEN )
|
||||
{
|
||||
if( HB_ISNUM( 3 ) )
|
||||
/* token counter */
|
||||
nTokenCounter = HB_SIZE_MAX;
|
||||
/* skip width */
|
||||
nSkip = hb_parns( 3 );
|
||||
else
|
||||
nSkip = HB_SIZE_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( HB_ISNUM( 4 ) ) /* HB_EXTENSION for AtToken()/TokenLower()/TokenUpper() */
|
||||
nSkip = hb_parns( 4 );
|
||||
else
|
||||
nSkip = HB_SIZE_MAX;
|
||||
/* token counter */
|
||||
nTokenCounter = hb_parns( 3 );
|
||||
/* skip width */
|
||||
nSkip = hb_parns( 4 ); /* HB_EXTENSION for AtToken()/TokenLower()/TokenUpper() */
|
||||
}
|
||||
|
||||
if( nTokenCounter == 0 )
|
||||
nTokenCounter = HB_SIZE_MAX;
|
||||
if( nSkip == 0 )
|
||||
nSkip = HB_SIZE_MAX;
|
||||
|
||||
|
||||
@@ -358,8 +358,8 @@ static PHB_CONSRV s_consrvNew( HB_SOCKET connsd, const char * szRootPath, HB_BOO
|
||||
static long s_srvRecvAll( PHB_CONSRV conn, void * buffer, long len )
|
||||
{
|
||||
HB_BYTE * ptr = ( HB_BYTE * ) buffer;
|
||||
HB_MAXUINT end_timer;
|
||||
long lRead = 0, l;
|
||||
HB_MAXUINT end_timer;
|
||||
|
||||
end_timer = conn->timeout > 0 ? hb_dateMilliSeconds() + conn->timeout : 0;
|
||||
|
||||
@@ -390,15 +390,18 @@ static long s_srvSendAll( PHB_CONSRV conn, void * buffer, long len )
|
||||
{
|
||||
HB_BYTE * ptr = ( HB_BYTE * ) buffer;
|
||||
long lSent = 0, lLast = 1, l;
|
||||
HB_MAXUINT end_timer;
|
||||
|
||||
if( ! conn->mutex || hb_threadMutexLock( conn->mutex ) )
|
||||
{
|
||||
end_timer = conn->timeout > 0 ? hb_dateMilliSeconds() + conn->timeout : 0;
|
||||
|
||||
while( lSent < len && ! conn->stop )
|
||||
{
|
||||
if( conn->zstream )
|
||||
l = hb_znetWrite( conn->zstream, conn->sd, ptr + lSent, len - lSent, -1, &lLast );
|
||||
l = hb_znetWrite( conn->zstream, conn->sd, ptr + lSent, len - lSent, 1000, &lLast );
|
||||
else
|
||||
l = lLast = hb_socketSend( conn->sd, ptr + lSent, len - lSent, 0, -1 );
|
||||
l = lLast = hb_socketSend( conn->sd, ptr + lSent, len - lSent, 0, 1000 );
|
||||
if( l > 0 )
|
||||
{
|
||||
lSent += l;
|
||||
@@ -407,13 +410,15 @@ static long s_srvSendAll( PHB_CONSRV conn, void * buffer, long len )
|
||||
if( lLast <= 0 )
|
||||
{
|
||||
if( hb_socketGetError() != HB_SOCKET_ERR_TIMEOUT ||
|
||||
hb_vmRequestQuery() != 0 )
|
||||
hb_vmRequestQuery() != 0 ||
|
||||
( end_timer != 0 && end_timer <= hb_dateMilliSeconds() ) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( conn->zstream && lLast > 0 && ! conn->stop )
|
||||
{
|
||||
if( hb_znetFlush( conn->zstream, conn->sd, -1 ) != 0 )
|
||||
if( hb_znetFlush( conn->zstream, conn->sd,
|
||||
conn->timeout > 0 ? conn->timeout : -1 ) != 0 )
|
||||
lSent = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -399,13 +399,10 @@ STATIC PROCEDURE build_projects( nAction, hProjectList, hProjectReqList, cOption
|
||||
|
||||
/* Add referenced project not present in our list and featuring an .hbp file */
|
||||
FOR EACH cProject IN aSortedList
|
||||
IF !( cProject $ hProjectList )
|
||||
IF hb_FileExists( s_cBase + s_cHome + cProject )
|
||||
AddProject( hProjectList, cProject )
|
||||
IF AddProject( hProjectList, @cProject )
|
||||
call_hbmk2_hbinfo( s_cBase + s_cHome + cProject, hProjectList[ cProject ] )
|
||||
hProjectList[ cProject ][ "lFromContainer" ] := NIL
|
||||
ENDIF
|
||||
ENDIF
|
||||
NEXT
|
||||
|
||||
/* Load project information for dependencies too
|
||||
@@ -725,7 +722,7 @@ STATIC FUNCTION TopoSort( aEdgeList )
|
||||
|
||||
RETURN aList
|
||||
|
||||
PROCEDURE AddProject( hProjectList, cFileName )
|
||||
FUNCTION AddProject( hProjectList, cFileName )
|
||||
|
||||
LOCAL cDir
|
||||
LOCAL cName
|
||||
@@ -737,11 +734,10 @@ PROCEDURE AddProject( hProjectList, cFileName )
|
||||
|
||||
hb_FNameSplit( cFileName, @cDir, @cName, @cExt )
|
||||
|
||||
IF ! Empty( cName ) .AND. Empty( cDir )
|
||||
cDir := cName
|
||||
ENDIF
|
||||
IF Empty( cName )
|
||||
cName := DirGetName( cDir )
|
||||
ELSEIF Empty( cDir )
|
||||
cDir := cName
|
||||
ENDIF
|
||||
IF Empty( cExt )
|
||||
cExt := ".hbp"
|
||||
@@ -749,10 +745,17 @@ PROCEDURE AddProject( hProjectList, cFileName )
|
||||
|
||||
cFileName := hb_FNameMerge( cDir, cName, cExt )
|
||||
|
||||
hProjectList[ StrTran( cFileName, "\", "/" ) ] := { => }
|
||||
IF hb_FileExists( s_cBase + s_cHome + cFileName )
|
||||
cFileName := StrTran( cFileName, "\", "/" )
|
||||
IF ! cFileName $ hProjectList
|
||||
hProjectList[ cFileName ] := { => }
|
||||
RETURN .T.
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
RETURN
|
||||
ENDIF
|
||||
|
||||
RETURN .F.
|
||||
|
||||
PROCEDURE LoadProjectListFromFile( hProjectList, cFileName )
|
||||
|
||||
@@ -762,9 +765,7 @@ PROCEDURE LoadProjectListFromFile( hProjectList, cFileName )
|
||||
IF "#" $ cItem
|
||||
cItem := Left( cItem, At( "#", cItem ) - 1 )
|
||||
ENDIF
|
||||
IF hb_FileExists( s_cBase + s_cHome + hb_DirSepToOS( AllTrim( cItem ) ) )
|
||||
AddProject( hProjectList, cItem )
|
||||
ENDIF
|
||||
NEXT
|
||||
|
||||
RETURN
|
||||
@@ -774,9 +775,7 @@ PROCEDURE LoadProjectListFromString( hProjectList, cString )
|
||||
LOCAL cItem
|
||||
|
||||
FOR EACH cItem IN hb_ATokens( cString,, .T. )
|
||||
IF hb_FileExists( s_cBase + s_cHome + cItem )
|
||||
AddProject( hProjectList, cItem )
|
||||
ENDIF
|
||||
NEXT
|
||||
|
||||
RETURN
|
||||
|
||||
@@ -521,6 +521,105 @@ references by detached locals and add workarounds for it if necessary.
|
||||
|
||||
|
||||
|
||||
### DECLARATION AND INITIALIZATION OF VARIABLES ###
|
||||
=========================================================
|
||||
Clipper parses variable declaration in a little bit different way then
|
||||
Harbour and xHarbour. It makes it in two passes. In first pass it collects
|
||||
names and scope of all declared variables and then in second pass this
|
||||
information is available during variable initialization. This can be
|
||||
illustrated by the following example:
|
||||
|
||||
/*** tst.prg ***/
|
||||
proc main()
|
||||
local cb := {|| qout( n + 5 ), qout( f ) } // (*)
|
||||
field f in table
|
||||
local n := 10
|
||||
eval( cb )
|
||||
return
|
||||
|
||||
In the line which initialize cb code (*) we are using local variable n
|
||||
and field f. Both are declared below the line in which codeblock is
|
||||
initialized anyhow Clipper does not recognize it as undeclared variables
|
||||
and use their later declarations. If you compile above code using Clipper
|
||||
with -n -w -es2 switches, i.e.
|
||||
cl tst -n -w -es2
|
||||
then it's compiled without any compile time warnings or errors. Then
|
||||
during execution it shows 15 for the first QOUT() function call and
|
||||
generate runtime error
|
||||
Error BASE/1002 Alias does not exist: TABLE
|
||||
for the second QOUT() call. It means that it correctly recognized scope
|
||||
of both variables and also bound alias TABLE with field F though it was
|
||||
declared two lines below codeblock initialization.
|
||||
|
||||
In fact Clipper probably does not make two passes but parsing declarations
|
||||
which have to be at the beginning of function or module it stores names of
|
||||
variables which should be initialized with the initialization expressions.
|
||||
Then when all declarations are processed for each line with declared and
|
||||
initialized variables it generates code which pushes on VM stack results
|
||||
of initialization expressions and then code which pops it initializing
|
||||
variables. As result in Clipper this code cannot work:
|
||||
local x := 10, y := x + 2
|
||||
because Clipper generate PCODE like:
|
||||
push 10
|
||||
push x
|
||||
push 2
|
||||
add
|
||||
pop y
|
||||
pop x
|
||||
but this code:
|
||||
local x := 10
|
||||
local y := x + 2
|
||||
works correctly because declarations were in separated lines and in such
|
||||
case Clipper generates PCODE like:
|
||||
push 10
|
||||
pop x
|
||||
push x
|
||||
push 2
|
||||
add
|
||||
pop y
|
||||
|
||||
In Harbour and xHarbour all variables are declared in the moment when they
|
||||
are processed. It means that during compilation of above example using
|
||||
harbour tst -n -w -es2
|
||||
both compilers generate compile time warnings:
|
||||
tst.prg(2) Warning W0001 Ambiguous reference 'N'
|
||||
tst.prg(2) Warning W0001 Ambiguous reference 'F'
|
||||
but it also means that in Harbour and xHarbour it's possible to write code
|
||||
like:
|
||||
proc main()
|
||||
local x := 10, y := x + 2
|
||||
? x, y
|
||||
return
|
||||
and unlike Clipper both compilers generates correct PCODE which shows
|
||||
10 12
|
||||
Maybe in the future we add support for Clipper compatible local variable
|
||||
initialization covered by -kc Harbor compiler switch.
|
||||
|
||||
xBase++ uses mixed behavior. Just like Clipper it stores variables with
|
||||
initialization expressions but then it generates slightly different code
|
||||
initializing variables one by one without line groping like in Clipper.
|
||||
|
||||
Please also note that in Clipper PRIVATE and PUBLIC declarations are
|
||||
executable statements so they are not used used as declarations by
|
||||
Clipper compiler even if -a compiler switch is used. So when we talk
|
||||
about initialization then it means that we are talking about LOCAL
|
||||
variables. STATIC variables are initialized in different way at
|
||||
application startup so cannot use local variables though due to but
|
||||
in Clipper in some cases compiler can accept local variables and then
|
||||
it may cause VM crash or runtime error, i.e. this code:
|
||||
|
||||
proc main()
|
||||
local n
|
||||
static s := {|| n }
|
||||
eval( s )
|
||||
return
|
||||
|
||||
is cleanly compiled by Clipper and xBase++ but it causes RTE in
|
||||
Clipper and FATAL ERROR LOG in xBase++.
|
||||
Harbour and xHarbour correctly report compile time error for it.
|
||||
|
||||
|
||||
|
||||
### FUNCTIONS WITH VARIABLE NUMBER OF PARAMETERS ###
|
||||
==========================================================
|
||||
Both compilers supports them though xHarbour is limited to all parameters
|
||||
|
||||
@@ -83,8 +83,10 @@ HB_EXTERN_BEGIN
|
||||
#define HB_OO_OP_ENUMSTART 25
|
||||
#define HB_OO_OP_ENUMSKIP 26
|
||||
#define HB_OO_OP_ENUMSTOP 27
|
||||
#define HB_OO_OP_ENUMISFIRST 28
|
||||
#define HB_OO_OP_ENUMISLAST 29
|
||||
|
||||
#define HB_OO_MAX_OPERATOR 27
|
||||
#define HB_OO_MAX_OPERATOR 29
|
||||
|
||||
extern void hb_clsInit( void ); /* initialize Classy/OO system at HVM startup */
|
||||
extern void hb_clsDoInit( void ); /* initialize Classy/OO system .prg functions */
|
||||
|
||||
@@ -437,7 +437,7 @@ typedef struct HB_EXPR_
|
||||
typedef struct HB_ENUMERATOR_
|
||||
{
|
||||
const char * szName;
|
||||
HB_BOOL bForEach;
|
||||
int iForEachDir; /* 0 - standard FOR/NEXT, 1(-1) FOR EACH(descendant) */
|
||||
struct HB_ENUMERATOR_ *pNext;
|
||||
} HB_ENUMERATOR, * PHB_ENUMERATOR; /* support structure for FOR EACH statements */
|
||||
|
||||
@@ -822,6 +822,7 @@ typedef struct _HB_COMP
|
||||
HB_BOOL fBuildInfo; /* print build info */
|
||||
HB_BOOL fLogo; /* print logo */
|
||||
HB_BOOL fSwitchCase; /* generate PCODE for CASE value of SWITCH statement */
|
||||
HB_BOOL fDescend; /* add descendant FOR EACH iterators */
|
||||
HB_BOOL fSingleModule; /* do not automatically compile DO...[WITH...] external modules (-m) */
|
||||
HB_BOOL fError; /* error appeared during compilation */
|
||||
HB_BOOL fNoArchDefs; /* do not define architecture dependent macros: __PLATFORM__*, __ARCH??BIT__, __*_ENDIAN__ */
|
||||
|
||||
@@ -144,7 +144,7 @@ PHB_EXPR hb_compExprNewFunCall( PHB_EXPR pName, PHB_EXPR pParms, HB_COMP_DECL )
|
||||
#if ! defined( HB_MACRO_SUPPORT ) && defined( HB_USE_ENUM_FUNCTIONS )
|
||||
{
|
||||
int iLen = strlen( pName->value.asSymbol.name );
|
||||
if( iLen >= 10 && i <= 12 && memcmp( "HB_ENUM", pName->value.asSymbol.name, 7 ) == 0 )
|
||||
if( iLen >= 10 && iLen <= 14 && memcmp( "HB_ENUM", pName->value.asSymbol.name, 7 ) == 0 )
|
||||
{
|
||||
const char * szMessage = pName->value.asSymbol.name + 7;
|
||||
|
||||
@@ -156,6 +156,8 @@ PHB_EXPR hb_compExprNewFunCall( PHB_EXPR pName, PHB_EXPR pParms, HB_COMP_DECL )
|
||||
szMessage = "__ENUMBASE";
|
||||
else if( iLen == 10 && memcmp( "KEY", szMessage, 3 ) == 0 )
|
||||
szMessage = "__ENUMKEY";
|
||||
else if( iLen == 14 && memcmp( "ISFIRST", szMessage, 7 ) == 0 )
|
||||
szMessage = "__ENUMISFIRST";
|
||||
else if( iLen == 13 && memcmp( "ISLAST", szMessage, 6 ) == 0 )
|
||||
szMessage = "__ENUMISLAST";
|
||||
else
|
||||
@@ -164,42 +166,59 @@ PHB_EXPR hb_compExprNewFunCall( PHB_EXPR pName, PHB_EXPR pParms, HB_COMP_DECL )
|
||||
if( szMessage )
|
||||
{
|
||||
int iCount = ( int ) hb_compExprParamListLen( pParms );
|
||||
const char * szName = NULL;
|
||||
PHB_ENUMERATOR pForVar, pEnumVar = NULL;
|
||||
|
||||
pForVar = HB_COMP_PARAM->functions.pLast->pEnum;
|
||||
|
||||
if( iCount == 0 )
|
||||
{
|
||||
PHB_ENUMERATOR pForVar, pEnumVar = NULL;
|
||||
pForVar = HB_COMP_PARAM->functions.pLast->pEnum;
|
||||
if( pForVar )
|
||||
{
|
||||
while( pForVar )
|
||||
{
|
||||
if( pForVar->bForEach )
|
||||
if( pForVar->iForEachDir != 0 )
|
||||
pEnumVar = pForVar;
|
||||
pForVar = pForVar->pNext;
|
||||
}
|
||||
if( pEnumVar )
|
||||
szName = pEnumVar->szName;
|
||||
}
|
||||
}
|
||||
else if( iCount == 1 )
|
||||
{
|
||||
if( pParms->value.asList.pExprList->ExprType == HB_ET_VARIABLE ||
|
||||
pParms->value.asList.pExprList->ExprType == HB_ET_VARREF )
|
||||
szName = pParms->value.asList.pExprList->value.asSymbol.name;
|
||||
}
|
||||
if( szName )
|
||||
{
|
||||
const char * szName = pParms->value.asList.pExprList->value.asSymbol.name;
|
||||
|
||||
while( pForVar )
|
||||
{
|
||||
if( pForVar->iForEachDir != 0 &&
|
||||
strcmp( pEnumVar->szName, szName ) == 0 )
|
||||
{
|
||||
pEnumVar = pForVar;
|
||||
break;
|
||||
}
|
||||
pForVar = pForVar->pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pEnumVar )
|
||||
{
|
||||
#if 0
|
||||
if( pEnumVar->iForEachDir < 0 )
|
||||
{
|
||||
if( strcmp( "__ENUMISFIRST", szMessage ) == 0 )
|
||||
szMessage = "__ENUMISLAST";
|
||||
else if( strcmp( "__ENUMISLAST", szMessage ) == 0 )
|
||||
szMessage = "__ENUMISFIRST";
|
||||
}
|
||||
#endif
|
||||
if( pParms )
|
||||
HB_COMP_EXPR_FREE( pParms );
|
||||
HB_COMP_EXPR_FREE( pName );
|
||||
return hb_compExprNewMethodObject(
|
||||
hb_compExprNewSend( szMessage, HB_COMP_PARAM ),
|
||||
hb_compExprNewVar( szName, HB_COMP_PARAM ) );
|
||||
hb_compExprNewVar( pEnumVar->szName, HB_COMP_PARAM ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if( pName->value.asSymbol.funcid == HB_F_EVAL &&
|
||||
hb_compExprParamListLen( pParms ) != 0 )
|
||||
|
||||
0
package/mpkg_src.sh
Normal file → Executable file
0
package/mpkg_src.sh
Normal file → Executable file
@@ -91,7 +91,7 @@ static void hb_compRTVariableGen( HB_COMP_DECL, const char * );
|
||||
static PHB_EXPR hb_compArrayDimPush( PHB_EXPR pInitValue, HB_COMP_DECL );
|
||||
static void hb_compVariableDim( const char *, PHB_EXPR, HB_COMP_DECL );
|
||||
|
||||
static void hb_compForStart( HB_COMP_DECL, const char *szVarName, HB_BOOL bForEach );
|
||||
static void hb_compForStart( HB_COMP_DECL, const char *szVarName, int iForEachDir );
|
||||
static void hb_compForEnd( HB_COMP_DECL, const char *szVarName );
|
||||
static void hb_compEnumStart( HB_COMP_DECL, PHB_EXPR pVars, PHB_EXPR pExprs, int descend );
|
||||
static void hb_compEnumNext( HB_COMP_DECL, PHB_EXPR pExpr, int descend );
|
||||
@@ -1551,7 +1551,7 @@ ForNext : FOR LValue ForAssign Expression /* 1 2 3 4 */
|
||||
$<asExpr>$ = hb_compExprGenPush( hb_compExprAssign( $2, $4, HB_COMP_PARAM ), HB_COMP_PARAM );
|
||||
if( hb_compExprAsSymbol( $2 ) )
|
||||
{
|
||||
hb_compForStart( HB_COMP_PARAM, hb_compExprAsSymbol( $2 ), HB_FALSE );
|
||||
hb_compForStart( HB_COMP_PARAM, hb_compExprAsSymbol( $2 ), 0 );
|
||||
}
|
||||
}
|
||||
TO ExpList StepExpr /* 6 7 8 */
|
||||
@@ -2416,7 +2416,7 @@ static void hb_compVariableDim( const char * szName, PHB_EXPR pInitValue, HB_COM
|
||||
}
|
||||
}
|
||||
|
||||
static void hb_compForStart( HB_COMP_DECL, const char *szVarName, HB_BOOL bForEach )
|
||||
static void hb_compForStart( HB_COMP_DECL, const char *szVarName, int iForEachDir )
|
||||
{
|
||||
PHB_ENUMERATOR pEnumVar;
|
||||
|
||||
@@ -2449,11 +2449,11 @@ static void hb_compForStart( HB_COMP_DECL, const char *szVarName, HB_BOOL bForEa
|
||||
pEnumVar = pLast->pNext;
|
||||
}
|
||||
pEnumVar->szName = szVarName;
|
||||
pEnumVar->bForEach = bForEach;
|
||||
pEnumVar->iForEachDir = iForEachDir;
|
||||
pEnumVar->pNext = NULL;
|
||||
}
|
||||
|
||||
static HB_BOOL hb_compForEachVarError( HB_COMP_DECL, const char *szVarName )
|
||||
static HB_BOOL hb_compForEachVarError( HB_COMP_DECL, const char *szVarName, int * piDir )
|
||||
{
|
||||
PHB_ENUMERATOR pEnumVar;
|
||||
|
||||
@@ -2464,7 +2464,8 @@ static HB_BOOL hb_compForEachVarError( HB_COMP_DECL, const char *szVarName )
|
||||
{
|
||||
if( strcmp( pEnumVar->szName, szVarName ) == 0 )
|
||||
{
|
||||
if( pEnumVar->bForEach )
|
||||
* piDir = pEnumVar->iForEachDir;
|
||||
if( * piDir != 0 )
|
||||
{
|
||||
/* only if it is FOR EACH enumerator
|
||||
* generate warning if it is FOR/NEXT loop
|
||||
@@ -2502,7 +2503,7 @@ static HB_COMP_CARGO2_FUNC( hb_compEnumEvalStart )
|
||||
const char * szName = hb_compExprAsSymbol( ( PHB_EXPR ) cargo );
|
||||
|
||||
if( szName )
|
||||
hb_compForStart( HB_COMP_PARAM, szName, HB_TRUE );
|
||||
hb_compForStart( HB_COMP_PARAM, szName, HB_COMP_PARAM->fDescend ? -1 : 1 );
|
||||
|
||||
hb_compExprGenPush( ( PHB_EXPR ) dummy, HB_COMP_PARAM ); /* expression */
|
||||
hb_compExprGenPush( ( PHB_EXPR ) cargo, HB_COMP_PARAM ); /* variable */
|
||||
@@ -2517,6 +2518,7 @@ static void hb_compEnumStart( HB_COMP_DECL, PHB_EXPR pVars, PHB_EXPR pExprs, int
|
||||
hb_compGenError( HB_COMP_PARAM, hb_comp_szErrors, 'E', HB_COMP_ERR_FORVAR_DIFF, NULL, NULL );
|
||||
}
|
||||
|
||||
HB_COMP_PARAM->fDescend = descend < 0;
|
||||
ulLen = hb_compExprListEval2( HB_COMP_PARAM, pVars, pExprs, hb_compEnumEvalStart );
|
||||
|
||||
if( ulLen > 255 )
|
||||
@@ -2815,10 +2817,23 @@ static PHB_EXPR hb_compCheckMethod( HB_COMP_DECL, PHB_EXPR pExpr )
|
||||
strcmp( "KEY", szMessage ) == 0 ||
|
||||
strcmp( "BASE", szMessage ) == 0 ||
|
||||
strcmp( "VALUE", szMessage ) == 0 ||
|
||||
strcmp( "ISFIRST", szMessage ) == 0 ||
|
||||
strcmp( "ISLAST", szMessage ) == 0 )
|
||||
{
|
||||
if( ! hb_compForEachVarError( HB_COMP_PARAM, pExpr->value.asMessage.pObject->value.asSymbol.name ) )
|
||||
int iDir = 0;
|
||||
if( ! hb_compForEachVarError( HB_COMP_PARAM, pExpr->value.asMessage.pObject->value.asSymbol.name, &iDir ) )
|
||||
{
|
||||
pExpr->value.asMessage.pObject->ExprType = HB_ET_VARREF;
|
||||
#if 0
|
||||
if( iDir < 0 )
|
||||
{
|
||||
if( strcmp( "ISFIRST", szMessage ) == 0 )
|
||||
pExpr->value.asMessage.szMessage = "__ENUMISLAST";
|
||||
else if( strcmp( "ISLAST", szMessage ) == 0 )
|
||||
pExpr->value.asMessage.szMessage = "__ENUMISFIRST";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -156,7 +156,7 @@ typedef union YYSTYPE
|
||||
{
|
||||
|
||||
/* Line 2068 of yacc.c */
|
||||
#line 125 "harbour.y"
|
||||
#line 121 "harbour.y"
|
||||
|
||||
const char * string; /* to hold a string returned by lex */
|
||||
int iNumber; /* to hold a temporary integer number */
|
||||
|
||||
@@ -1272,7 +1272,7 @@ int hb_comClose( int iPort )
|
||||
{
|
||||
hb_vmUnlock();
|
||||
#if defined( TIOCNXCL )
|
||||
ioctl( pCom->fd, TIOCNXCL );
|
||||
ioctl( pCom->fd, TIOCNXCL, 0 );
|
||||
#endif
|
||||
do
|
||||
{
|
||||
@@ -1314,7 +1314,7 @@ int hb_comOpen( int iPort )
|
||||
if( pCom->fd != -1 )
|
||||
{
|
||||
#if defined( TIOCEXCL ) /* TIOCNXCL */
|
||||
iResult = ioctl( pCom->fd, TIOCEXCL );
|
||||
iResult = ioctl( pCom->fd, TIOCEXCL, 0 );
|
||||
if( iResult != 0 )
|
||||
{
|
||||
close( pCom->fd );
|
||||
|
||||
@@ -304,7 +304,9 @@ static HB_SYMB s_opSymbols[ HB_OO_MAX_OPERATOR + 1 ] = {
|
||||
{ "__ENUMVALUE", {HB_FS_MESSAGE}, {NULL}, NULL }, /* 24 */
|
||||
{ "__ENUMSTART", {HB_FS_MESSAGE}, {NULL}, NULL }, /* 25 */
|
||||
{ "__ENUMSKIP", {HB_FS_MESSAGE}, {NULL}, NULL }, /* 26 */
|
||||
{ "__ENUMSTOP", {HB_FS_MESSAGE}, {NULL}, NULL } /* 27 */
|
||||
{ "__ENUMSTOP", {HB_FS_MESSAGE}, {NULL}, NULL }, /* 27 */
|
||||
{ "__ENUMISFIRST", {HB_FS_MESSAGE}, {NULL}, NULL }, /* 28 */
|
||||
{ "__ENUMISLAST", {HB_FS_MESSAGE}, {NULL}, NULL }, /* 29 */
|
||||
};
|
||||
|
||||
static HB_SYMB s___msgDestructor = { "__msgDestructor", {HB_FS_MESSAGE}, {NULL}, NULL };
|
||||
@@ -348,6 +350,7 @@ static HB_SYMB s___msgEnumIndex = { "__ENUMINDEX", {HB_FS_MESSAGE}, {HB_FUN
|
||||
static HB_SYMB s___msgEnumBase = { "__ENUMBASE", {HB_FS_MESSAGE}, {HB_FUNCNAME( msgNull )}, NULL };
|
||||
static HB_SYMB s___msgEnumKey = { "__ENUMKEY", {HB_FS_MESSAGE}, {HB_FUNCNAME( msgNull )}, NULL };
|
||||
static HB_SYMB s___msgEnumValue = { "__ENUMVALUE", {HB_FS_MESSAGE}, {HB_FUNCNAME( msgNull )}, NULL };
|
||||
static HB_SYMB s___msgEnumIsFirst = { "__ENUMISFIRST", {HB_FS_MESSAGE}, {HB_FUNCNAME( msgNull )}, NULL };
|
||||
static HB_SYMB s___msgEnumIsLast = { "__ENUMISLAST", {HB_FS_MESSAGE}, {HB_FUNCNAME( msgNull )}, NULL };
|
||||
|
||||
/* WITH OBJECT base value access/asign methods (:__withobject) */
|
||||
@@ -1180,6 +1183,7 @@ void hb_clsInit( void )
|
||||
s___msgEnumBase.pDynSym = hb_dynsymGetCase( s___msgEnumBase.szName );
|
||||
s___msgEnumKey.pDynSym = hb_dynsymGetCase( s___msgEnumKey.szName );
|
||||
s___msgEnumValue.pDynSym = hb_dynsymGetCase( s___msgEnumValue.szName );
|
||||
s___msgEnumIsFirst.pDynSym = hb_dynsymGetCase( s___msgEnumIsFirst.szName );
|
||||
s___msgEnumIsLast.pDynSym = hb_dynsymGetCase( s___msgEnumIsLast.szName );
|
||||
|
||||
s___msgWithObjectPush.pDynSym = hb_dynsymGetCase( s___msgWithObjectPush.szName );
|
||||
@@ -2010,14 +2014,34 @@ PHB_SYMB hb_objGetMethod( PHB_ITEM pObject, PHB_SYMB pMessage,
|
||||
hb_itemCopy( pEnum, hb_itemUnRef( hb_stackItemFromBase( 1 ) ) );
|
||||
return &s___msgEnumValue;
|
||||
}
|
||||
else if( pMsg == s___msgEnumIsFirst.pDynSym )
|
||||
{
|
||||
PHB_ITEM pBase = HB_IS_BYREF( pEnum->item.asEnum.basePtr ) ?
|
||||
hb_itemUnRef( pEnum->item.asEnum.basePtr ) :
|
||||
pEnum->item.asEnum.basePtr;
|
||||
if( HB_IS_OBJECT( pBase ) &&
|
||||
hb_objHasOperator( pBase, HB_OO_OP_ENUMISFIRST ) )
|
||||
return hb_objGetMethod( pBase, pMessage, pStack );
|
||||
hb_itemPutL( hb_stackReturnItem(), ( HB_SIZE ) pEnum->item.asEnum.offset <= 1 );
|
||||
return &s___msgEnumIsFirst;
|
||||
}
|
||||
else if( pMsg == s___msgEnumIsLast.pDynSym )
|
||||
{
|
||||
if( HB_IS_ARRAY( pEnum->item.asEnum.basePtr ) )
|
||||
hb_itemPutL( hb_stackReturnItem(), ( HB_SIZE ) pEnum->item.asEnum.offset >= hb_arrayLen( pEnum->item.asEnum.basePtr ) );
|
||||
else if( HB_IS_HASH( pEnum->item.asEnum.basePtr ) )
|
||||
hb_itemPutL( hb_stackReturnItem(), ( HB_SIZE ) pEnum->item.asEnum.offset >= hb_hashLen( pEnum->item.asEnum.basePtr ) );
|
||||
else if( HB_IS_STRING( pEnum->item.asEnum.basePtr ) )
|
||||
hb_itemPutL( hb_stackReturnItem(), ( HB_SIZE ) pEnum->item.asEnum.offset >= hb_itemGetCLen( pEnum->item.asEnum.basePtr ) );
|
||||
PHB_ITEM pBase = HB_IS_BYREF( pEnum->item.asEnum.basePtr ) ?
|
||||
hb_itemUnRef( pEnum->item.asEnum.basePtr ) :
|
||||
pEnum->item.asEnum.basePtr;
|
||||
if( HB_IS_ARRAY( pBase ) )
|
||||
{
|
||||
if( HB_IS_OBJECT( pBase ) &&
|
||||
hb_objHasOperator( pBase, HB_OO_OP_ENUMISLAST ) )
|
||||
return hb_objGetMethod( pBase, pMessage, pStack );
|
||||
else
|
||||
hb_itemPutL( hb_stackReturnItem(), ( HB_SIZE ) pEnum->item.asEnum.offset >= hb_arrayLen( pBase ) );
|
||||
}
|
||||
else if( HB_IS_HASH( pBase ) )
|
||||
hb_itemPutL( hb_stackReturnItem(), ( HB_SIZE ) pEnum->item.asEnum.offset >= hb_hashLen( pBase ) );
|
||||
else if( HB_IS_STRING( pBase ) )
|
||||
hb_itemPutL( hb_stackReturnItem(), ( HB_SIZE ) pEnum->item.asEnum.offset >= hb_itemGetCLen( pBase ) );
|
||||
|
||||
return &s___msgEnumIsLast;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ PROCEDURE Main()
|
||||
"| index:", enum:__enumIndex(), ;
|
||||
"| value:", enum:__enumValue(), ;
|
||||
"| base:", ValType( enum:__enumBase() ), ;
|
||||
"| isfirst:", enum:__enumIsFirst(), ;
|
||||
"| islast:", enum:__enumIsLast()
|
||||
NEXT
|
||||
? "after loop ENUM=", enum
|
||||
@@ -47,12 +48,14 @@ PROCEDURE Main()
|
||||
"| index:", enum:__enumIndex(), ;
|
||||
"| value:", enum:__enumValue(), ;
|
||||
"| base:", ValType( enum:__enumBase() ), ;
|
||||
"| isfirst:", enum:__enumIsFirst(), ;
|
||||
"| islast:", enum:__enumIsLast()
|
||||
testBYREF( @enum )
|
||||
? " after passing by @ | ENUM=", enum, ;
|
||||
"| index:", enum:__enumIndex(), ;
|
||||
"| value:", enum:__enumValue(), ;
|
||||
"| base:", ValType( enum:__enumBase() ), ;
|
||||
"| isfirst:", enum:__enumIsFirst(), ;
|
||||
"| islast:", enum:__enumIsLast()
|
||||
ENDIF
|
||||
NEXT
|
||||
@@ -70,6 +73,7 @@ PROCEDURE Main()
|
||||
"| index:", enum:__enumIndex(), ;
|
||||
"| value:", enum:__enumValue(), ;
|
||||
"| base:", ValType( enum:__enumBase() ), ;
|
||||
"| isfirst:", enum:__enumIsFirst(), ;
|
||||
"| islast:", enum:__enumIsLast()
|
||||
TESTbreak( enum )
|
||||
NEXT
|
||||
@@ -95,6 +99,7 @@ PROCEDURE Main()
|
||||
"| index:", enum:__enumIndex(), ;
|
||||
"| value:", enum:__enumValue(), ;
|
||||
"| base:", ValType( enum:__enumBase() ), ;
|
||||
"| isfirst:", enum:__enumIsFirst(), ;
|
||||
"| islast:", enum:__enumIsLast()
|
||||
NEXT
|
||||
RECOVER USING i
|
||||
|
||||
@@ -97,7 +97,7 @@ METHOD __enumSkip( enum, lDescend ) CLASS myclass2
|
||||
/* set enumerator value */
|
||||
(@enum):__enumValue( ::value / 10.0 )
|
||||
/* the index is updated automatically but if we want some noncontinuous
|
||||
* indexes then here we van set it using (@enum):__enumIndex( nNeIndex )
|
||||
* indexes then here we can set it using (@enum):__enumIndex( nNeIndex )
|
||||
* message
|
||||
*/
|
||||
return .T. /* continue iteration */
|
||||
|
||||
Reference in New Issue
Block a user