1523 lines
42 KiB
C
1523 lines
42 KiB
C
/*
|
|
* $Id$
|
|
|
|
Copyright(C) 1999 by Antonio Linares.
|
|
|
|
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 of the License, 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; if not, write to:
|
|
|
|
The Free Software Foundation, Inc.,
|
|
675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
You can contact me at: alinares@fivetech.com
|
|
*/
|
|
|
|
/* Harbour Project source code
|
|
http://www.Harbour-Project.org/
|
|
The following functions are Copyright 1999 Victor Szel <info@szelvesz.hu>:
|
|
hb_strEmpty().
|
|
hb_strMatchDOS().
|
|
hb_STRZERO().
|
|
See doc/hdr_tpl.txt, Version 1.2 or later, for licensing terms.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include "extend.h"
|
|
#include "dates.h"
|
|
#include "itemapi.h"
|
|
#include "errorapi.h"
|
|
#include "set.h"
|
|
|
|
#define HB_ISSPACE( c ) ( ( c ) == 9 || ( c ) == 10 || ( c ) == 13 || ( c ) == 32 )
|
|
|
|
/* DJGPP can sprintf a float that is almost 320 digits long */
|
|
#define HB_MAX_DOUBLE_LENGTH 320
|
|
|
|
#ifdef HARBOUR_STRICT_CLIPPER_COMPATIBILITY
|
|
|
|
#include "init.h"
|
|
|
|
static double s_dInfinity = 0;
|
|
|
|
HB_CALL_ON_STARTUP_BEGIN( Strings_InitInfinity )
|
|
s_dInfinity = -log( 0 );
|
|
HB_CALL_ON_STARTUP_END( Strings_InitInfinity )
|
|
#if ! defined(__GNUC__)
|
|
#pragma startup Strings_InitInfinity
|
|
#endif
|
|
|
|
#endif
|
|
|
|
BOOL hb_strEmpty( char * szText, ULONG ulLen )
|
|
{
|
|
BOOL bRetVal = TRUE;
|
|
|
|
while( ulLen-- )
|
|
{
|
|
char c = szText[ ulLen ];
|
|
|
|
if( !HB_ISSPACE( c ) )
|
|
{
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
/* Harbour Project source code
|
|
http://www.Harbour-Project.org/
|
|
The following function is Copyright 1999 David G. Holm <dholm@jsd-llc.com>:
|
|
hb_stricmp().
|
|
See doc/hdr_tpl.txt, Version 1.2 or later, for licensing terms.
|
|
*/
|
|
|
|
int hb_stricmp( const char *s1, const char *s2 )
|
|
{
|
|
int rc = 0, c1, c2;
|
|
ULONG l1, l2, count;
|
|
|
|
l1 = strlen( s1 );
|
|
l2 = strlen( s2 );
|
|
if( l1 < l2 ) count = l1;
|
|
else count = l2;
|
|
while( rc == 0 && count > 0 )
|
|
{
|
|
count--;
|
|
c1 = toupper( *s1++ );
|
|
c2 = toupper( *s2++ );
|
|
if( c1 != c2 ) rc = ( c1 < c2 ? -1 : 1 );
|
|
}
|
|
if( rc == 0 && l1 != l2 )
|
|
{
|
|
if( l1 < l2 ) rc = -1;
|
|
else rc = 1;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static BOOL hb_strMatchDOS( char *pszString, char *pszMask )
|
|
{
|
|
while( *pszMask && *pszString )
|
|
{
|
|
if( *pszMask == '*' )
|
|
{
|
|
while( *pszMask == '*' )
|
|
pszMask++;
|
|
|
|
if( ! ( *pszMask ) )
|
|
return TRUE;
|
|
else
|
|
if( *pszMask == '?' )
|
|
pszString++;
|
|
else
|
|
{
|
|
while( toupper( *pszString ) != toupper( *pszMask ) )
|
|
{
|
|
if( ! ( *( ++pszString ) ) )
|
|
return FALSE;
|
|
}
|
|
while( toupper( *pszString ) == toupper( *pszMask ) )
|
|
{
|
|
if( ! ( *( ++pszString ) ) )
|
|
break;
|
|
}
|
|
pszMask++;
|
|
}
|
|
}
|
|
else
|
|
if( toupper( *pszMask ) != toupper( *pszString ) && *pszMask != '?' )
|
|
return FALSE;
|
|
else
|
|
{
|
|
pszMask++;
|
|
pszString++;
|
|
}
|
|
}
|
|
|
|
return ! ( ( ! ( *pszString ) && *pszMask && *pszMask != '*') ||
|
|
( ! ( *pszMask ) && *pszString ) );
|
|
}
|
|
|
|
/* TODO: Replace it with a code that supports real regular expressions
|
|
*
|
|
*/
|
|
BOOL hb_strMatchRegExp( char *szString, char *szMask )
|
|
{
|
|
return hb_strMatchDOS( szString, szMask );
|
|
}
|
|
|
|
|
|
/* determines if first char of string is letter */
|
|
/* TEST: QOUT( "isalpha( 'hello' ) = ", isalpha( 'hello' ) ) */
|
|
/* TEST: QOUT( "isalpha( '12345' ) = ", isalpha( '12345' ) ) */
|
|
HARBOUR HB_ISALPHA( void )
|
|
{
|
|
hb_retl( isalpha( *hb_parc( 1 ) ) );
|
|
}
|
|
|
|
/* determines if first char of string is digit */
|
|
/* TEST: QOUT( "isdigit( '12345' ) = ", isdigit( '12345' ) ) */
|
|
/* TEST: QOUT( "isdigit( 'abcde' ) = ", isdigit( 'abcde' ) ) */
|
|
HARBOUR HB_ISDIGIT( void )
|
|
{
|
|
hb_retl( isdigit( *hb_parc( 1 ) ) );
|
|
}
|
|
|
|
/* determines if first char of string is upper-case */
|
|
/* TEST: QOUT( "isupper( 'Abcde' ) = ", isupper( 'Abcde' ) ) */
|
|
/* TEST: QOUT( "isupper( 'abcde' ) = ", isupper( 'abcde' ) ) */
|
|
HARBOUR HB_ISUPPER( void )
|
|
{
|
|
hb_retl( isupper( *hb_parc( 1 ) ) );
|
|
}
|
|
|
|
/* determines if first char of string is lower-case */
|
|
/* TEST: QOUT( "islower( 'abcde' ) = ", islower( 'abcde' ) ) */
|
|
/* TEST: QOUT( "islower( 'Abcde' ) = ", islower( 'Abcde' ) ) */
|
|
HARBOUR HB_ISLOWER( void )
|
|
{
|
|
hb_retl( islower( *hb_parc( 1 ) ) );
|
|
}
|
|
|
|
/* trims from the left, and returns a new pointer to szText */
|
|
/* also returns the new length in lLen */
|
|
char *hb_strLTrim( char *szText, ULONG *lLen )
|
|
{
|
|
while( *lLen && HB_ISSPACE( *szText ) )
|
|
{
|
|
szText++;
|
|
( *lLen )--;
|
|
}
|
|
|
|
return szText;
|
|
}
|
|
|
|
/* trims leading spaces from a string */
|
|
/* TEST: QOUT( "ltrim( ' hello world ' ) = '" + ltrim( ' hello world ' ) + "'" ) */
|
|
HARBOUR HB_LTRIM( void )
|
|
{
|
|
if( hb_pcount() == 1 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText )
|
|
{
|
|
ULONG lLen = pText->item.asString.length;
|
|
char *szText = hb_strLTrim( pText->item.asString.value, &lLen );
|
|
|
|
hb_retclen( szText, lLen );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1101, NULL, "LTRIM" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "LTRIM" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* returns szText and the new length in lLen */
|
|
ULONG hb_strRTrimLen( char *szText, ULONG lLen, BOOL bAnySpace )
|
|
{
|
|
if( bAnySpace )
|
|
{
|
|
while( lLen && HB_ISSPACE( szText[ lLen - 1 ] ) )
|
|
lLen--;
|
|
}
|
|
else
|
|
{
|
|
while( lLen && szText[ lLen - 1 ] == ' ' )
|
|
lLen--;
|
|
}
|
|
|
|
return lLen;
|
|
}
|
|
|
|
/* NOTE: The second parameter is a Harbour extension */
|
|
|
|
/* trims trailing spaces from a string */
|
|
/* TEST: QOUT( "rtrim( ' hello world ' ) = '" + rtrim( ' hello world ' ) + "'" ) */
|
|
HARBOUR HB_RTRIM( void )
|
|
{
|
|
if( hb_pcount() >= 1 && hb_pcount() <= 2 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText )
|
|
{
|
|
BOOL bAnySpace = ( ISLOG( 2 ) ? hb_parl( 2 ) : FALSE );
|
|
hb_retclen( pText->item.asString.value, hb_strRTrimLen( pText->item.asString.value, pText->item.asString.length, bAnySpace ) );
|
|
}
|
|
else
|
|
#ifdef HARBOUR_STRICT_CLIPPER_COMPATIBILITY
|
|
/* Clipper doesn't error, but only in RTRIM. TRIM() throws an error, though */
|
|
hb_retc( "" );
|
|
#else
|
|
hb_errRT_BASE( EG_ARG, 1100, NULL, "RTRIM" );
|
|
#endif
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "RTRIM" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* NOTE: The second parameter is a Harbour extension */
|
|
|
|
/* synonymn for RTRIM */
|
|
HARBOUR HB_TRIM( void )
|
|
{
|
|
if( hb_pcount() >= 1 && hb_pcount() <= 2 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText )
|
|
{
|
|
BOOL bAnySpace = ( ISLOG( 2 ) ? hb_parl( 2 ) : FALSE );
|
|
hb_retclen( pText->item.asString.value, hb_strRTrimLen( pText->item.asString.value, pText->item.asString.length, bAnySpace ) );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1100, NULL, "TRIM" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "TRIM" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* NOTE: The second parameter is a Harbour extension */
|
|
|
|
/* trims leading and trailing spaces from a string */
|
|
/* TEST: QOUT( "alltrim( ' hello world ' ) = '" + alltrim( ' hello world ' ) + "'" ) */
|
|
HARBOUR HB_ALLTRIM( void )
|
|
{
|
|
if( ISCHAR( 1 ) )
|
|
{
|
|
char *szText = hb_parc( 1 );
|
|
BOOL bAnySpace = ( ISLOG( 2 ) ? hb_parl( 2 ) : FALSE );
|
|
ULONG lLen;
|
|
|
|
lLen = hb_strRTrimLen( szText, hb_parclen( 1 ), bAnySpace );
|
|
|
|
szText = hb_strLTrim( szText, &lLen );
|
|
|
|
hb_retclen( szText, lLen );
|
|
}
|
|
else
|
|
#ifdef HB_COMPATIBILITY_CLIPPER_53
|
|
hb_errRT_BASE( EG_ARG, 2022, NULL, "ALLTRIM" );
|
|
#else
|
|
hb_retc( "" );
|
|
#endif
|
|
}
|
|
|
|
/* This function is used by all of the PAD functions to prepare the argument
|
|
being padded. If date, convert to string using hb_dtoc(). If numeric,
|
|
convert to unpadded string. Return pointer to string and set string length */
|
|
static char * hb_itemPadConv( PHB_ITEM pItem, char * buffer, ULONG * pulSize )
|
|
{
|
|
char * szText;
|
|
|
|
if( pItem )
|
|
{
|
|
if( IS_STRING( pItem ) )
|
|
{
|
|
szText = hb_itemGetCPtr( pItem );
|
|
*pulSize = hb_itemGetCLen( pItem );
|
|
}
|
|
else if( IS_DATE( pItem ) )
|
|
{
|
|
szText = hb_dtoc( hb_pards( 1 ), buffer, hb_set.HB_SET_DATEFORMAT );
|
|
*pulSize = strlen( szText );
|
|
}
|
|
else if( IS_INTEGER( pItem ) )
|
|
{
|
|
sprintf( buffer, "%d", hb_itemGetNI( pItem ) );
|
|
szText = buffer;
|
|
*pulSize = strlen( szText );
|
|
}
|
|
else if( IS_LONG( pItem ) )
|
|
{
|
|
sprintf( buffer, "%ld", hb_itemGetNL( pItem ) );
|
|
szText = buffer;
|
|
*pulSize = strlen( szText );
|
|
}
|
|
else if( IS_DOUBLE( pItem ) )
|
|
{
|
|
sprintf( buffer, "%.*f", pItem->item.asDouble.decimal, hb_itemGetND( pItem ) );
|
|
szText = buffer;
|
|
*pulSize = strlen( szText );
|
|
}
|
|
else
|
|
szText = NULL;
|
|
}
|
|
else
|
|
szText = NULL;
|
|
|
|
return szText;
|
|
}
|
|
|
|
/* right-pads a date, number, or string with spaces or supplied character */
|
|
/* TEST: QOUT( "padr( 'hello', 10 ) = '" + padr( 'hello', 10 ) + "'" ) */
|
|
HARBOUR HB_PADR( void )
|
|
{
|
|
ULONG ulSize;
|
|
char buffer[ 128 ];
|
|
char *szText = hb_itemPadConv( hb_param( 1, IT_ANY ), buffer, &ulSize );
|
|
|
|
if( szText && ISNUM( 2 ) )
|
|
{
|
|
LONG lLen = hb_parnl( 2 );
|
|
|
|
if( lLen > ( LONG ) ulSize )
|
|
{
|
|
char *szResult = ( char * ) hb_xgrab( lLen + 1 );
|
|
LONG lPos;
|
|
char cPad;
|
|
|
|
memcpy( szResult, szText, ( LONG ) ulSize );
|
|
|
|
cPad = ( ISCHAR( 3 ) ? *( hb_parc( 3 ) ) : ' ' );
|
|
|
|
for( lPos = ( LONG ) ulSize; lPos < lLen; lPos++ )
|
|
szResult[ lPos ] = cPad;
|
|
|
|
hb_retclen( szResult , lLen );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
{
|
|
if( lLen < 0 )
|
|
lLen = 0;
|
|
|
|
hb_retclen( szText, lLen );
|
|
}
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
|
|
/* synonymn for PADR */
|
|
HARBOUR HB_PAD( void )
|
|
{
|
|
HB_PADR();
|
|
}
|
|
|
|
/* left-pads a date, number, or string with spaces or supplied character */
|
|
/* TEST: QOUT( "padl( 'hello', 10 ) = '" + padl( 'hello', 10 ) + "'" ) */
|
|
HARBOUR HB_PADL( void )
|
|
{
|
|
ULONG ulSize;
|
|
char buffer[ 128 ];
|
|
char * szText = hb_itemPadConv( hb_param( 1, IT_ANY ), buffer, &ulSize );
|
|
|
|
if( szText && ISNUM( 2 ) )
|
|
{
|
|
LONG lLen = hb_parnl( 2 );
|
|
|
|
if( lLen > ( LONG ) ulSize )
|
|
{
|
|
char *szResult = ( char * ) hb_xgrab( lLen + 1 );
|
|
LONG lPos = lLen - ( LONG ) ulSize;
|
|
char cPad;
|
|
|
|
memcpy( szResult + lPos, szText, ( LONG ) ulSize );
|
|
|
|
cPad = ( ISCHAR( 3 ) ? *( hb_parc( 3 ) ) : ' ');
|
|
|
|
for(; lPos > 0; lPos-- )
|
|
{
|
|
szResult[ lPos - 1 ] = cPad;
|
|
}
|
|
|
|
hb_retclen( szResult, lLen );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
{
|
|
if( lLen < 0 )
|
|
lLen = 0;
|
|
|
|
hb_retclen( szText, lLen );
|
|
}
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
|
|
/* centre-pads a date, number, or string with spaces or supplied character */
|
|
/* TEST: QOUT( "padc( 'hello', 10 ) = '" + padc( 'hello', 10 ) + "'" ) */
|
|
HARBOUR HB_PADC( void )
|
|
{
|
|
ULONG ulSize;
|
|
char buffer[ 128 ];
|
|
char *szText = hb_itemPadConv( hb_param( 1, IT_ANY ), buffer, &ulSize );
|
|
|
|
if( szText && ISNUM( 2 ) )
|
|
{
|
|
LONG lLen = hb_parnl( 2 );
|
|
|
|
if( lLen > ( LONG ) ulSize )
|
|
{
|
|
char *szResult = ( char * ) hb_xgrab( lLen + 1 );
|
|
char cPad;
|
|
LONG w, lPos = ( lLen - ( LONG ) ulSize ) / 2;
|
|
|
|
memcpy( szResult + lPos, szText, ( LONG ) ulSize + 1 );
|
|
|
|
cPad = ( ISCHAR( 3 ) ? *hb_parc( 3 ) : ' ' );
|
|
|
|
for( w = 0; w < lPos; w++ )
|
|
szResult[ w ] = cPad;
|
|
|
|
for( w = ( LONG ) ulSize + lPos; w < lLen; w++ )
|
|
szResult[ w ] = cPad;
|
|
|
|
szResult[ lLen ] = '\0';
|
|
|
|
hb_retclen( szResult, lLen );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
{
|
|
if( lLen < 0 )
|
|
lLen = 0;
|
|
|
|
hb_retclen( szText, lLen );
|
|
}
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
|
|
ULONG hb_strAt( char *szSub, ULONG ulSubLen, char *szText, ULONG ulLen )
|
|
{
|
|
if( ulSubLen )
|
|
{
|
|
if( ulLen >= ulSubLen )
|
|
{
|
|
ULONG ulPos = 0, ulSubPos = 0;
|
|
|
|
while( ulPos < ulLen && ulSubPos < ulSubLen )
|
|
{
|
|
if( *( szText + ulPos ) == *( szSub + ulSubPos ) )
|
|
{
|
|
ulSubPos++;
|
|
ulPos++;
|
|
}
|
|
else if( ulSubPos )
|
|
ulSubPos = 0;
|
|
else
|
|
ulPos++;
|
|
}
|
|
return ( ulSubPos < ulSubLen ) ? 0 : ( ulPos - ulSubLen + 1 );
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
/* locates a substring in a string */
|
|
/* TEST: QOUT( "at( 'cde', 'abcdefgfedcba' ) = '" + at( 'cde', 'abcsefgfedcba' ) + "'" ) */
|
|
HARBOUR HB_AT( void )
|
|
{
|
|
if( hb_pcount() == 2 )
|
|
{
|
|
PHB_ITEM pSub = hb_param( 1, IT_STRING );
|
|
PHB_ITEM pText = hb_param( 2, IT_STRING );
|
|
|
|
if( IS_STRING( pText ) && IS_STRING( pSub ) )
|
|
{
|
|
hb_retnl( hb_strAt( pSub->item.asString.value, pSub->item.asString.length, pText->item.asString.value, pText->item.asString.length ) );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1108, NULL, "AT" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "AT" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* locates a substring in a string starting at the end */
|
|
/* TEST: QOUT( "rat( 'cde', 'abcdefgfedcba' ) = '" + rat( 'cde', 'abcdefgfedcba' ) + "'" ) */
|
|
HARBOUR HB_RAT( void )
|
|
{
|
|
ULONG ulSubLen = hb_parclen( 1 );
|
|
|
|
if( ulSubLen )
|
|
{
|
|
long lPos = hb_parclen( 2 ) - ulSubLen;
|
|
|
|
if( lPos >= 0 )
|
|
{
|
|
char *szSub = hb_parc( 1 );
|
|
char *szText = hb_parc( 2 );
|
|
BOOL bFound = FALSE;
|
|
|
|
while( lPos >= 0 && !bFound )
|
|
{
|
|
if( *( szText + lPos ) == *szSub )
|
|
bFound = ( memcmp( szSub, szText + lPos, ulSubLen ) == 0 );
|
|
lPos--;
|
|
}
|
|
|
|
hb_retnl( bFound ? lPos + 2 : 0 );
|
|
}
|
|
else
|
|
hb_retni( 0 );
|
|
}
|
|
else
|
|
/* This function never seems to raise an error */
|
|
hb_retni( 0 );
|
|
}
|
|
|
|
/* converts an ASCII code to a character value */
|
|
HARBOUR HB_CHR( void )
|
|
{
|
|
if( hb_pcount() == 1 )
|
|
{
|
|
if( ISNUM( 1 ) )
|
|
{
|
|
char chr[ 2 ];
|
|
|
|
/* Believe it or not, clipper does this! */
|
|
chr[ 0 ] = hb_parnl( 1 ) % 256;
|
|
chr[ 1 ] = '\0';
|
|
hb_retclen( chr, 1 );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1104, NULL, "CHR" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "CHR" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* converts a character value to an ASCII code */
|
|
HARBOUR HB_ASC( void )
|
|
{
|
|
if( hb_pcount() == 1 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText )
|
|
{
|
|
if( pText->item.asString.length > 0 )
|
|
hb_retni( ( BYTE ) * ( pText->item.asString.value ) );
|
|
else
|
|
hb_retni( 0 );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1107, NULL, "ASC" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "ASC" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* returns the left-most n characters in string */
|
|
HARBOUR HB_LEFT( void )
|
|
{
|
|
if( hb_pcount() == 2 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText && ISNUM( 2 ) )
|
|
{
|
|
LONG lLen = hb_parnl( 2 );
|
|
|
|
if( lLen > ( LONG ) pText->item.asString.length )
|
|
lLen = ( LONG ) pText->item.asString.length;
|
|
|
|
else if( lLen < 0 )
|
|
lLen = 0;
|
|
|
|
hb_retclen( pText->item.asString.value, lLen );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1124, NULL, "LEFT" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "LEFT" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* returns the right-most n characters in string */
|
|
HARBOUR HB_RIGHT( void )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText && ISNUM( 2 ) )
|
|
{
|
|
LONG lLen = hb_parnl( 2 );
|
|
|
|
if( lLen > ( LONG ) pText->item.asString.length )
|
|
lLen = ( LONG ) pText->item.asString.length;
|
|
|
|
else if( lLen < 0 )
|
|
lLen = 0;
|
|
|
|
hb_retclen( pText->item.asString.value + pText->item.asString.length - lLen, lLen );
|
|
}
|
|
else
|
|
{
|
|
/* Clipper doesn't error */
|
|
hb_retc( "" );
|
|
}
|
|
}
|
|
|
|
/* returns l characters from n characters into string */
|
|
HARBOUR HB_SUBSTR( void )
|
|
{
|
|
if( hb_pcount() >= 2 && hb_pcount() <= 3 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText && ISNUM( 2 ) )
|
|
{
|
|
LONG lPos = hb_parnl( 2 );
|
|
|
|
if( lPos < 0 )
|
|
{
|
|
lPos += ( LONG ) pText->item.asString.length;
|
|
if( lPos < 0 )
|
|
lPos = 0;
|
|
}
|
|
else if( lPos )
|
|
{
|
|
lPos--;
|
|
}
|
|
|
|
if( lPos < ( LONG ) pText->item.asString.length )
|
|
{
|
|
LONG lLen;
|
|
|
|
if( ISNUM( 3 ) )
|
|
{
|
|
lLen = hb_parnl( 3 );
|
|
|
|
if( lLen > ( LONG ) pText->item.asString.length - lPos )
|
|
lLen = ( LONG ) pText->item.asString.length - lPos;
|
|
}
|
|
else
|
|
lLen = ( LONG ) pText->item.asString.length - lPos;
|
|
|
|
if( lLen > 0 )
|
|
hb_retclen( pText->item.asString.value + lPos, lLen );
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1110, NULL, "SUBSTR" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "SUBSTR" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* converts szText to lower case. Does not create a new string! */
|
|
char *hb_strLower( char *szText, ULONG ulLen )
|
|
{
|
|
ULONG i;
|
|
|
|
for( i = 0; i < ulLen; i++ )
|
|
szText[ i ] = tolower( szText[ i ] );
|
|
|
|
return szText;
|
|
}
|
|
|
|
/* converts string to lower case */
|
|
HARBOUR HB_LOWER( void )
|
|
{
|
|
if( hb_pcount() == 1 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText )
|
|
{
|
|
ULONG ulLen = pText->item.asString.length;
|
|
|
|
hb_retclen( hb_strLower( pText->item.asString.value, ulLen ), ulLen );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1103, NULL, "LOWER" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "LOWER" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
void hb_strupr( char * szText )
|
|
{
|
|
char *p;
|
|
|
|
for( p = szText; *p; p++ )
|
|
*p = toupper( *p );
|
|
}
|
|
|
|
/* converts szText to upper case. Does not create a new string! */
|
|
char *hb_strUpper( char *szText, ULONG ulLen )
|
|
{
|
|
ULONG i;
|
|
|
|
for( i = 0; i < ulLen; i++ )
|
|
szText[ i ] = toupper( szText[ i ] );
|
|
|
|
return szText;
|
|
}
|
|
|
|
/* converts string to upper case */
|
|
HARBOUR HB_UPPER( void )
|
|
{
|
|
if( hb_pcount() == 1 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText )
|
|
{
|
|
ULONG ulLen = pText->item.asString.length;
|
|
|
|
hb_retclen( hb_strUpper( pText->item.asString.value, ulLen ), ulLen );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1102, NULL, "UPPER" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "UPPER" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* returns n copies of given string */
|
|
/* TEST: QOUT( "replicate( 'abc', 5 ) = " + replicate( 'abc', 5 ) ) */
|
|
HARBOUR HB_REPLICATE( void )
|
|
{
|
|
if( hb_pcount() == 2 )
|
|
{
|
|
if( ISCHAR( 1 ) && ISNUM( 2 ) )
|
|
{
|
|
LONG lTimes = hb_parnl( 2 );
|
|
|
|
if( lTimes > 0 )
|
|
{
|
|
ULONG ulLen = hb_parclen( 1 );
|
|
|
|
if( ( double ) ( ( double ) ulLen * ( double ) lTimes ) < ( double ) ULONG_MAX )
|
|
{
|
|
char *szText = hb_parc( 1 );
|
|
char *szResult = ( char * ) hb_xgrab( ( ulLen * lTimes ) + 1 );
|
|
char *szPtr = szResult;
|
|
LONG i;
|
|
|
|
for( i = 0; i < lTimes; i++ )
|
|
{
|
|
memcpy( szPtr, szText, ulLen );
|
|
szPtr += ulLen;
|
|
}
|
|
|
|
hb_retclen( szResult, ulLen * lTimes );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
{
|
|
PHB_ITEM pResult = hb_errRT_BASE_Subst( EG_STROVERFLOW, 1234, NULL, "REPLICATE" );
|
|
|
|
if( pResult )
|
|
{
|
|
hb_itemReturn( pResult );
|
|
hb_itemRelease( pResult );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1106, NULL, "REPLICATE" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "REPLICATE" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* returns n copies of a single space */
|
|
/* TEST: QOUT( "space( 5 ) = '" + space( 5 ) + "'" ) */
|
|
HARBOUR HB_SPACE( void )
|
|
{
|
|
if( hb_pcount() == 1 )
|
|
{
|
|
if( ISNUM( 1 ) )
|
|
{
|
|
LONG lLen = hb_parnl( 1 );
|
|
|
|
if( lLen > 0 )
|
|
{
|
|
char *szResult = ( char * ) hb_xgrab( lLen + 1 );
|
|
|
|
/* NOTE: String overflow could never occure since a string can */
|
|
/* be as large as ULONG_MAX, and the maximum length that */
|
|
/* can be specified is LONG_MAX here. */
|
|
/* hb_errRT_BASE( EG_STROVERFLOW, 1233, NULL, "SPACE" ); */
|
|
|
|
memset( szResult, ' ', lLen );
|
|
hb_retclen( szResult, lLen );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1105, NULL, "SPACE" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "SPACE" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* replaces characters in a string */
|
|
HARBOUR HB_STUFF( void )
|
|
{
|
|
if( ISCHAR( 1 ) && ISNUM( 2 ) && ISNUM( 3 ) && ISCHAR( 4 ) )
|
|
{
|
|
char *szText = hb_parc( 1 );
|
|
ULONG ulText = hb_parclen( 1 );
|
|
ULONG ulPos = hb_parnl( 2 );
|
|
ULONG ulDel = hb_parnl( 3 );
|
|
ULONG ulInsert = hb_parclen( 4 );
|
|
|
|
ULONG ulTotalLen;
|
|
|
|
if( ulPos > 0 )
|
|
ulPos--;
|
|
|
|
if( ulPos > ulText )
|
|
ulPos = ulText;
|
|
|
|
if( ulDel > ulText - ulPos )
|
|
ulDel = ulText - ulPos;
|
|
|
|
if( ( ulTotalLen = ulText + ulInsert - ulDel ) > 0 )
|
|
{
|
|
char * szResult = ( char * ) hb_xgrab( ulTotalLen + 1 );
|
|
|
|
memcpy( szResult, szText, ulPos );
|
|
memcpy( szResult + ulPos, hb_parc( 4 ), ulInsert );
|
|
memcpy( szResult + ulPos + ulInsert, szText + ulPos + ulDel, ulText - ( ulPos + ulDel ) );
|
|
|
|
szResult[ ulTotalLen ] = '\0';
|
|
hb_retclen( szResult, ulTotalLen );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
|
|
/* TODO: Check for string overflow, Clipper can crash if the resulting
|
|
string is too large. Example:
|
|
StrTran( "...", ".", Replicate( "A", 32000 ) ) */
|
|
|
|
/* replaces lots of characters in a string */
|
|
HARBOUR HB_STRTRAN( void )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText )
|
|
{
|
|
PHB_ITEM pSeek = hb_param( 2, IT_STRING );
|
|
|
|
if( pSeek )
|
|
{
|
|
char *szText = pText->item.asString.value;
|
|
|
|
if( pSeek->item.asString.length && pSeek->item.asString.length <= pText->item.asString.length )
|
|
{
|
|
char *szSeek = pSeek->item.asString.value;
|
|
char *szReplace;
|
|
ULONG ulStart;
|
|
|
|
ulStart = ( ISNUM( 4 ) ? hb_parnl( 4 ) : 1 );
|
|
|
|
if( !ulStart )
|
|
{
|
|
/* Clipper seems to work this way */
|
|
hb_retc( "" );
|
|
}
|
|
else if( ulStart > 0 )
|
|
{
|
|
PHB_ITEM pReplace = hb_param( 3, IT_STRING );
|
|
ULONG ulReplace;
|
|
ULONG ulCount;
|
|
BOOL bAll;
|
|
|
|
if( pReplace )
|
|
{
|
|
szReplace = pReplace->item.asString.value;
|
|
ulReplace = pReplace->item.asString.length;
|
|
}
|
|
else
|
|
{
|
|
szReplace = ""; /* shouldn't matter that we don't allocate */
|
|
ulReplace = 0;
|
|
}
|
|
|
|
if( ISNUM( 5 ) )
|
|
{
|
|
ulCount = hb_parnl( 5 );
|
|
bAll = FALSE;
|
|
}
|
|
else
|
|
{
|
|
ulCount = 0;
|
|
bAll = TRUE;
|
|
}
|
|
|
|
if( bAll || ulCount > 0 )
|
|
{
|
|
ULONG ulFound = 0;
|
|
LONG lReplaced = 0;
|
|
ULONG i = 0;
|
|
ULONG ulLength = pText->item.asString.length;
|
|
|
|
while( i < pText->item.asString.length )
|
|
{
|
|
if( ( bAll || lReplaced < ( LONG ) ulCount ) && ! memcmp( szText + i, szSeek, pSeek->item.asString.length ) )
|
|
{
|
|
ulFound++;
|
|
if( ulFound >= ulStart )
|
|
{
|
|
lReplaced++;
|
|
ulLength = ulLength - pSeek->item.asString.length + ulReplace;
|
|
i += pSeek->item.asString.length;
|
|
}
|
|
else
|
|
i++;
|
|
}
|
|
else
|
|
i++;
|
|
}
|
|
|
|
if( ulFound )
|
|
{
|
|
char *szResult = ( char * ) hb_xgrab( ulLength + 1 );
|
|
char *szPtr = szResult;
|
|
|
|
ulFound = 0;
|
|
i = 0;
|
|
while( i < pText->item.asString.length )
|
|
{
|
|
if( lReplaced && ! memcmp( szText + i, szSeek, pSeek->item.asString.length ) )
|
|
{
|
|
ulFound++;
|
|
if( ulFound >= ulStart )
|
|
{
|
|
lReplaced--;
|
|
memcpy( szPtr, szReplace, ulReplace );
|
|
szPtr += ulReplace;
|
|
i += pSeek->item.asString.length;
|
|
}
|
|
else
|
|
{
|
|
*szPtr = szText[ i ];
|
|
szPtr++;
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*szPtr = szText[ i ];
|
|
szPtr++;
|
|
i++;
|
|
}
|
|
}
|
|
hb_retclen( szResult, ulLength );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
hb_retclen( szText, pText->item.asString.length );
|
|
}
|
|
else
|
|
hb_retclen( szText, pText->item.asString.length );
|
|
}
|
|
else
|
|
hb_retclen( szText, pText->item.asString.length );
|
|
}
|
|
else
|
|
hb_retclen( szText, pText->item.asString.length );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1126, NULL, "STRTRAN" ); /* NOTE: Undocumented but existing Clipper Run-time error */
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1126, NULL, "STRTRAN" ); /* NOTE: Undocumented but existing Clipper Run-time error */
|
|
}
|
|
|
|
/* returns the numeric value of a character string representation of a number */
|
|
double hb_strVal( char *szText )
|
|
{
|
|
return atof( szText );
|
|
}
|
|
|
|
/* returns the numeric value of a character string representation of a number */
|
|
HARBOUR HB_VAL( void )
|
|
{
|
|
if( hb_pcount() == 1 )
|
|
{
|
|
PHB_ITEM pText = hb_param( 1, IT_STRING );
|
|
|
|
if( pText )
|
|
{
|
|
int iWidth;
|
|
int iDec;
|
|
double dNumber = hb_strVal( pText->item.asString.value );
|
|
char * ptr = strchr( pText->item.asString.value, '.' );
|
|
|
|
if( ptr )
|
|
{
|
|
iWidth = ptr - pText->item.asString.value;
|
|
iDec = strlen( ptr + 1 );
|
|
}
|
|
else
|
|
{
|
|
iWidth = strlen( pText->item.asString.value );
|
|
iDec = 0;
|
|
}
|
|
|
|
if( iDec )
|
|
hb_retndlen( dNumber, iWidth, iDec );
|
|
|
|
else if( SHRT_MIN <= dNumber && dNumber <= SHRT_MAX )
|
|
hb_retnilen( ( int ) dNumber, iWidth );
|
|
|
|
else if( LONG_MIN <= dNumber && dNumber <= LONG_MAX )
|
|
hb_retnllen( ( long ) dNumber, iWidth );
|
|
|
|
else
|
|
hb_retndlen( dNumber, iWidth, ( WORD ) -1 );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 1098, NULL, "VAL" );
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "VAL" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* converts a numeric to a string with optional width & precision.
|
|
This function should be used by any function that wants to format numeric
|
|
data for displaying, printing, or putting in a database.
|
|
|
|
Note: The caller is responsible for calling hb_xfree to free the results buffer,
|
|
but ONLY if the return value is not a NULL pointer!
|
|
*/
|
|
/* TODO: Move it to itemapi.c */
|
|
char * hb_itemStr( PHB_ITEM pNumber, PHB_ITEM pWidth, PHB_ITEM pDec )
|
|
{
|
|
char * szResult = NULL;
|
|
|
|
if( pNumber )
|
|
{
|
|
/* Default to the width and number of decimals specified by the item,
|
|
with a limit of 20 integer places and 9 decimal places */
|
|
WORD wWidth;
|
|
WORD wDec;
|
|
|
|
hb_itemGetNLen( pNumber, &wWidth, &wDec );
|
|
|
|
if( wWidth > 20 )
|
|
wWidth = 20;
|
|
if( wDec > 9 )
|
|
wDec = 9;
|
|
if( hb_set.HB_SET_FIXED )
|
|
wDec = hb_set.HB_SET_DECIMALS;
|
|
|
|
if( pWidth )
|
|
{
|
|
/* If the width parameter is specified, override the default value
|
|
and set the number of decimals to zero */
|
|
int iWidth = ( int ) hb_itemGetNL( pWidth );
|
|
|
|
if( iWidth < 1 )
|
|
wWidth = 10; /* If 0 or negative, use default */
|
|
else
|
|
wWidth = ( WORD ) iWidth;
|
|
|
|
wDec = 0;
|
|
}
|
|
|
|
if( pDec )
|
|
{
|
|
/* This function does not include the decimal places in the width,
|
|
so the width must be adjusted downwards, if the decimal places
|
|
parameter is greater than 0 */
|
|
int iDec = ( int ) hb_itemGetNL( pDec );
|
|
|
|
if( iDec < 0 )
|
|
wDec = 0;
|
|
else if( iDec > 0 )
|
|
{
|
|
wDec = ( WORD ) iDec;
|
|
wWidth -= ( wDec + 1 );
|
|
}
|
|
}
|
|
|
|
if( wWidth )
|
|
{
|
|
/* We at least have a width value */
|
|
int iBytes;
|
|
int iSize = ( wDec ? wWidth + 1 + wDec : wWidth );
|
|
|
|
/* Be paranoid and use a large amount of padding */
|
|
szResult = ( char * ) hb_xgrab( HB_MAX_DOUBLE_LENGTH );
|
|
|
|
if( IS_DOUBLE( pNumber ) || wDec != 0 )
|
|
{
|
|
double dNumber = hb_itemGetND( pNumber );
|
|
|
|
#ifdef HARBOUR_STRICT_CLIPPER_COMPATIBILITY
|
|
if( pNumber->item.asDouble.length == 99 || dNumber == s_dInfinity || dNumber == -s_dInfinity )
|
|
/* Numeric overflow */
|
|
iBytes = iSize + 1;
|
|
else
|
|
#endif
|
|
{
|
|
if( wDec < IS_DOUBLE( pNumber ) ? pNumber->item.asDouble.decimal : 0 )
|
|
dNumber = hb_numRound( dNumber, wDec );
|
|
|
|
if( wDec == 0 )
|
|
iBytes = sprintf( szResult, "%*.0f", iSize, dNumber );
|
|
else
|
|
iBytes = sprintf( szResult, "%*.*f", iSize, wDec, dNumber );
|
|
}
|
|
}
|
|
else switch( pNumber->type & ~IT_BYREF )
|
|
{
|
|
case IT_INTEGER:
|
|
iBytes = sprintf( szResult, "%*i", wWidth, pNumber->item.asInteger.value );
|
|
break;
|
|
|
|
case IT_LONG:
|
|
iBytes = sprintf( szResult, "%*li", wWidth, pNumber->item.asLong.value );
|
|
break;
|
|
|
|
default:
|
|
iBytes = 0;
|
|
szResult[ 0 ] = '\0'; /* null string */
|
|
break;
|
|
}
|
|
|
|
/* Set to asterisks in case of overflow */
|
|
if( iBytes > iSize )
|
|
{
|
|
memset( szResult, '*', iSize );
|
|
szResult[ iSize ] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
return szResult;
|
|
}
|
|
|
|
|
|
/* $DOC$
|
|
* $FUNCNAME$
|
|
* STR
|
|
* $CATEGORY$
|
|
* Run-time Library, Strings
|
|
* $ONELINER$
|
|
* Convert a numeric expression to a character string.
|
|
* $SYNTAX$
|
|
* STR(<nNumber>, [<nLength>], [<nDecimals>]) --> cNumber
|
|
* $ARGUMENTS$
|
|
* <nNumber> is the numeric expression to be converted to a character
|
|
* string.
|
|
* <nLength> is the length of the character string to return, including
|
|
* decimal digits, decimal point, and sign.
|
|
* <nDecimals> is the number of decimal places to return.
|
|
* $RETURNS$
|
|
* STR() returns <nNumber> formatted as a character string. If the
|
|
* optional length and decimal arguments are not specified, STR()
|
|
* returns the character string according to the following rules:
|
|
*
|
|
* Results of STR() with No Optional Arguments
|
|
* ---------------------------------------------------------------
|
|
* Expression Return Value Length
|
|
* ---------------------------------------------------------------
|
|
* Field Variable Field length plus decimals
|
|
* Expressions/constants Minimum of 10 digits plus decimals
|
|
* VAL() Minimum of 3 digits
|
|
* MONTH()/DAY() 3 digits
|
|
* YEAR() 5 digits
|
|
* RECNO() 7 digits
|
|
* ---------------------------------------------------------------
|
|
* $DESCRIPTION$
|
|
* STR() is a numeric conversion function that converts numeric values
|
|
* to character strings. It is commonly used to concatenate numeric values
|
|
* to character strings. STR() has applications displaying numbers,
|
|
* creating codes such as part numbers from numeric values, and creating
|
|
* index keys that combine numeric and character data.
|
|
*
|
|
* STR() is like TRANSFORM(), which formats numeric values as character
|
|
* strings using a mask instead of length and decimal specifications.
|
|
*
|
|
* The inverse of STR() is VAL(), which converts character numbers to
|
|
* numerics.
|
|
*
|
|
* * If <nLength> is less than the number of whole number digits in
|
|
* <nNumber>, STR() returns asterisks instead of the number.
|
|
*
|
|
* * If <nLength> is less than the number of decimal digits
|
|
* required for the decimal portion of the returned string, Harbour
|
|
* rounds the number to the available number of decimal places.
|
|
*
|
|
* * If <nLength> is specified but <nDecimals> is omitted (no
|
|
* decimal places), the return value is rounded to an integer.
|
|
* $EXAMPLES$
|
|
* ? STR( 10, 6, 2 ) // " 10.00"
|
|
* ? STR( -10, 8, 2 ) // " -10.00"
|
|
* $TESTS$
|
|
* see in rtl_test.prg for a comprehensive regression test suit.
|
|
* $STATUS$
|
|
* R
|
|
* $COMPLIANCE$
|
|
* CA-Clipper compatible.
|
|
* $SEEALSO$
|
|
* STRZERO()
|
|
* VAL()
|
|
* $END$
|
|
*/
|
|
|
|
HARBOUR HB_STR( void )
|
|
{
|
|
if( hb_pcount() >= 1 && hb_pcount() <= 3 )
|
|
{
|
|
BOOL bValid = TRUE;
|
|
PHB_ITEM pNumber = hb_param( 1, IT_NUMERIC );
|
|
PHB_ITEM pWidth = NULL;
|
|
PHB_ITEM pDec = NULL;
|
|
|
|
if( !pNumber )
|
|
bValid = FALSE;
|
|
else
|
|
{
|
|
if( hb_pcount() >= 2 )
|
|
{
|
|
pWidth = hb_param( 2, IT_NUMERIC );
|
|
if( !pWidth )
|
|
bValid = FALSE;
|
|
}
|
|
if( hb_pcount() >= 3 )
|
|
{
|
|
pDec = hb_param( 3, IT_NUMERIC );
|
|
if( !pDec )
|
|
bValid = FALSE;
|
|
}
|
|
}
|
|
if( bValid )
|
|
{
|
|
char * szResult = hb_itemStr( pNumber, pWidth, pDec );
|
|
|
|
if( szResult )
|
|
{
|
|
hb_retc( szResult );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
else
|
|
{
|
|
PHB_ITEM pResult = hb_errRT_BASE_Subst( EG_ARG, 1099, NULL, "STR" );
|
|
|
|
if( pResult )
|
|
{
|
|
hb_itemReturn( pResult );
|
|
hb_itemRelease( pResult );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hb_errRT_BASE( EG_ARGCOUNT, 3000, NULL, "STR" ); /* NOTE: Clipper catches this at compile time! */
|
|
}
|
|
|
|
/* ------------------------------------------------- */
|
|
/* Copyright (C) 1999 Victor Szel <info@szelvesz.hu> */
|
|
/* ------------------------------------------------- */
|
|
|
|
/* $DOC$
|
|
* $FUNCNAME$
|
|
* STRZERO
|
|
* $CATEGORY$
|
|
* Run-time Library, Strings
|
|
* $ONELINER$
|
|
* Convert a numeric expression to a character string, zero padded.
|
|
* $SYNTAX$
|
|
* STRZERO(<nNumber>, [<nLength>], [<nDecimals>]) --> cNumber
|
|
* $ARGUMENTS$
|
|
* <nNumber> is the numeric expression to be converted to a character
|
|
* string.
|
|
* <nLength> is the length of the character string to return, including
|
|
* decimal digits, decimal point, and sign.
|
|
* <nDecimals> is the number of decimal places to return.
|
|
* $RETURNS$
|
|
* STRZERO() returns <nNumber> formatted as a character string. If the
|
|
* optional length and decimal arguments are not specified, STRZERO()
|
|
* returns the character string according to the following rules:
|
|
*
|
|
* Results of STRZERO() with No Optional Arguments
|
|
* ---------------------------------------------------------------
|
|
* Expression Return Value Length
|
|
* ---------------------------------------------------------------
|
|
* Field Variable Field length plus decimals
|
|
* Expressions/constants Minimum of 10 digits plus decimals
|
|
* VAL() Minimum of 3 digits
|
|
* MONTH()/DAY() 3 digits
|
|
* YEAR() 5 digits
|
|
* RECNO() 7 digits
|
|
* ---------------------------------------------------------------
|
|
* $DESCRIPTION$
|
|
* STRZERO() is a numeric conversion function that converts numeric values
|
|
* to character strings. It is commonly used to concatenate numeric values
|
|
* to character strings. STRZERO() has applications displaying numbers,
|
|
* creating codes such as part numbers from numeric values, and creating
|
|
* index keys that combine numeric and character data.
|
|
*
|
|
* STRZERO() is like TRANSFORM(), which formats numeric values as character
|
|
* strings using a mask instead of length and decimal specifications.
|
|
*
|
|
* The inverse of STRZERO() is VAL(), which converts character numbers to
|
|
* numerics.
|
|
*
|
|
* * If <nLength> is less than the number of whole number digits in
|
|
* <nNumber>, STR() returns asterisks instead of the number.
|
|
*
|
|
* * If <nLength> is less than the number of decimal digits
|
|
* required for the decimal portion of the returned string, Harbour
|
|
* rounds the number to the available number of decimal places.
|
|
*
|
|
* * If <nLength> is specified but <nDecimals> is omitted (no
|
|
* decimal places), the return value is rounded to an integer.
|
|
* $EXAMPLES$
|
|
* ? STRZERO( 10, 6, 2 ) // "010.00"
|
|
* ? STRZERO( -10, 8, 2 ) // "-0010.00"
|
|
* $TESTS$
|
|
* see in rtl_test.prg for a comprehensive regression test suit.
|
|
* $STATUS$
|
|
* R
|
|
* $COMPLIANCE$
|
|
* CA-Clipper compatible (it was not mentioned in the docs though).
|
|
* $SEEALSO$
|
|
* STR()
|
|
* VAL()
|
|
* $END$
|
|
*/
|
|
|
|
HARBOUR HB_STRZERO( void )
|
|
{
|
|
if( hb_pcount() >= 1 && hb_pcount() <= 3 )
|
|
{
|
|
BOOL bValid = TRUE;
|
|
PHB_ITEM pNumber = hb_param( 1, IT_NUMERIC );
|
|
PHB_ITEM pWidth = NULL;
|
|
PHB_ITEM pDec = NULL;
|
|
|
|
if( !pNumber )
|
|
bValid = FALSE;
|
|
else
|
|
{
|
|
if( hb_pcount() >= 2 )
|
|
{
|
|
pWidth = hb_param( 2, IT_NUMERIC );
|
|
if( !pWidth )
|
|
bValid = FALSE;
|
|
}
|
|
if( hb_pcount() >= 3 )
|
|
{
|
|
pDec = hb_param( 3, IT_NUMERIC );
|
|
if( !pDec )
|
|
bValid = FALSE;
|
|
}
|
|
}
|
|
if( bValid )
|
|
{
|
|
char * szResult = hb_itemStr( pNumber, pWidth, pDec );
|
|
|
|
if( szResult )
|
|
{
|
|
ULONG ulPos = 0;
|
|
|
|
while( szResult[ ulPos ] != '\0' && szResult[ ulPos ] != '-' )
|
|
ulPos++;
|
|
|
|
if( szResult[ ulPos ] == '-' )
|
|
{
|
|
/* Negative sign found, put the negative sign to the first */
|
|
/* position */
|
|
|
|
szResult[ ulPos ] = ' ';
|
|
|
|
ulPos = 0;
|
|
while( szResult[ ulPos ] != '\0' && szResult[ ulPos ] == ' ' )
|
|
szResult[ ulPos++ ] = '0';
|
|
|
|
szResult[ 0 ] = '-';
|
|
}
|
|
else
|
|
{
|
|
/* Negative sign not found */
|
|
|
|
ulPos = 0;
|
|
while( szResult[ ulPos ] != '\0' && szResult[ ulPos ] == ' ' )
|
|
szResult[ ulPos++ ] = '0';
|
|
}
|
|
|
|
hb_retc( szResult );
|
|
hb_xfree( szResult );
|
|
}
|
|
else
|
|
hb_retc( "" );
|
|
}
|
|
else
|
|
{
|
|
#ifdef HARBOUR_STRICT_CLIPPER_COMPATIBILITY
|
|
/* NOTE: In CA-Cl*pper STRZERO() is writtin in Clipper, and will call
|
|
STR() to do the job, the error (if any) will also be thrown
|
|
by STR(). */
|
|
PHB_ITEM pResult = hb_errRT_BASE_Subst( EG_ARG, 1099, NULL, "STR" );
|
|
#else
|
|
PHB_ITEM pResult = hb_errRT_BASE_Subst( EG_ARG, 9999, NULL, "STRZERO" );
|
|
#endif
|
|
|
|
if( pResult )
|
|
{
|
|
hb_itemReturn( pResult );
|
|
hb_itemRelease( pResult );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Values returned : HB_STRGREATER_EQUAL, HB_STRGREATER_LEFT, HB_STRGREATER_RIGHT */
|
|
|
|
int hb_strgreater( char * szText1, char * szText2 )
|
|
{
|
|
while( *( szText1 ) && *( szText2 ) && *( szText1 ) == *( szText2 ) )
|
|
{
|
|
szText1++;
|
|
szText2++;
|
|
}
|
|
if( ( *( szText1 ) == '\0' && *( szText2 ) != '\0' ) ||
|
|
( *( szText2 ) > *( szText1 ) ) )
|
|
return HB_STRGREATER_RIGHT;
|
|
|
|
if( ( *( szText1 ) != '\0' && *( szText2 ) == '\0' ) ||
|
|
( *( szText1 ) > *( szText2 ) ) )
|
|
return HB_STRGREATER_LEFT;
|
|
|
|
return HB_STRGREATER_EQUAL;
|
|
}
|