You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
10 KiB
513 lines
10 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997 - 1997
|
|
//
|
|
// File: bnstr.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
//
|
|
// BNSTR.CPP
|
|
//
|
|
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
|
|
#include "bnstr.h"
|
|
|
|
|
|
SZC BNSTR :: _pmt = "" ;
|
|
|
|
static SZ SzCopy(SZC szc)
|
|
{
|
|
SZ szNew = szc ? new char[::strlen(szc) + 1] : NULL;
|
|
|
|
return szNew ? ::strcpy(szNew, szc) : NULL;
|
|
}
|
|
|
|
BNSTR :: BNSTR ( SZC sz )
|
|
: _cchMax( 0 ),
|
|
_cchStr( 0 ),
|
|
_sz( const_cast<SZ>(_pmt) )
|
|
{
|
|
if ( sz )
|
|
{
|
|
Update( sz ) ;
|
|
}
|
|
}
|
|
|
|
BNSTR :: BNSTR ( const BNSTR & str )
|
|
: _cchMax( str._cchStr ),
|
|
_cchStr( str._cchStr ),
|
|
_sz( const_cast<SZ>(_pmt) )
|
|
{
|
|
if ( str._sz != _pmt )
|
|
{
|
|
_sz = ::SzCopy( str._sz ) ;
|
|
}
|
|
}
|
|
|
|
|
|
BNSTR :: ~ BNSTR ()
|
|
{
|
|
Reset() ;
|
|
}
|
|
|
|
void BNSTR :: Reset ()
|
|
{
|
|
DeleteSz() ;
|
|
_sz = const_cast<SZ>(_pmt) ;
|
|
_cchStr = 0 ;
|
|
_cchMax = 0 ;
|
|
}
|
|
|
|
// Protectively delete either the given string or the
|
|
// private string.
|
|
void BNSTR :: DeleteSz ()
|
|
{
|
|
if ( _sz != NULL && _sz != _pmt )
|
|
{
|
|
delete [] _sz ;
|
|
_sz = NULL ;
|
|
}
|
|
}
|
|
|
|
// Release the current buffer; reset the BNSTR.
|
|
SZ BNSTR::Transfer ()
|
|
{
|
|
SZ sz = _sz ;
|
|
_sz = NULL ;
|
|
Reset() ;
|
|
return sz = _pmt ? NULL : sz ;
|
|
}
|
|
|
|
// Give the current buffer to a new string, reset *this.
|
|
void BNSTR :: Transfer ( BNSTR & str )
|
|
{
|
|
str.Reset() ;
|
|
str._sz = _sz ;
|
|
str._cchMax = _cchMax ;
|
|
str._cchStr = _cchStr ;
|
|
_sz = NULL ;
|
|
Reset() ;
|
|
}
|
|
|
|
void BNSTR :: Trunc ( UINT cchLen )
|
|
{
|
|
if ( _sz == _pmt )
|
|
return ;
|
|
if ( cchLen < _cchStr )
|
|
_sz[cchLen] = 0 ;
|
|
}
|
|
|
|
// Update the pointed string. Since this routine is
|
|
// used by the assignment operator, it's written to allow
|
|
// for the new string being part of the old string.
|
|
bool BNSTR :: Update ( SZC sz )
|
|
{
|
|
bool bResult = true ;
|
|
|
|
UINT cch = sz ? ::strlen( sz ) : 0 ;
|
|
|
|
if ( cch > _cchMax )
|
|
{
|
|
SZ szNew = ::SzCopy( sz ) ;
|
|
if ( bResult = szNew != NULL )
|
|
{
|
|
DeleteSz() ;
|
|
_sz = szNew ;
|
|
_cchMax = _cchStr = cch ;
|
|
}
|
|
}
|
|
else
|
|
if ( cch == 0 )
|
|
{
|
|
Reset() ;
|
|
}
|
|
else
|
|
{
|
|
// REVIEW: this assumes that ::strcpy() handles overlapping regions correctly.
|
|
::strcpy( _sz, sz ) ;
|
|
_cchStr = cch ;
|
|
}
|
|
return bResult ;
|
|
}
|
|
|
|
// Grow the string. if 'cchNewSize' == 0, expand by 50%.
|
|
// If 'ppszNew' is given, store the new string there (for efficiency in
|
|
// Prefix); note that this requires that we reallocate.
|
|
bool BNSTR :: Grow ( UINT cchNewSize, SZ * ppszNew )
|
|
{
|
|
UINT cchNew = cchNewSize == 0
|
|
? (_cchMax + (_cchMax/2))
|
|
: cchNewSize ;
|
|
|
|
bool bResult = true ;
|
|
if ( cchNew > _cchMax || ppszNew )
|
|
{
|
|
SZ sz = new char [cchNew+1] ;
|
|
if ( bResult = sz != NULL )
|
|
{
|
|
_cchMax = cchNew ;
|
|
if ( ppszNew )
|
|
{
|
|
*ppszNew = sz ;
|
|
}
|
|
else
|
|
{
|
|
::strcpy( sz, _sz ) ;
|
|
DeleteSz() ;
|
|
_sz = sz ;
|
|
}
|
|
}
|
|
}
|
|
return bResult ;
|
|
}
|
|
|
|
// Expand the string to the given length; make it a blank, null terminated
|
|
// string.
|
|
bool BNSTR :: Pad ( UINT cchLength )
|
|
{
|
|
// Expand as necessary
|
|
if ( ! Grow( cchLength + 1 ) )
|
|
return false ;
|
|
// If expanding, pad the string with spaces.
|
|
while ( _cchStr < cchLength )
|
|
{
|
|
_sz[_cchStr++] = ' ' ;
|
|
}
|
|
// Truncate to proper length
|
|
_sz[_cchStr = cchLength] = 0 ;
|
|
return true ;
|
|
}
|
|
|
|
bool BNSTR :: Assign ( SZC szcData, UINT cchLen )
|
|
{
|
|
if ( ! Grow( cchLen + 1 ) )
|
|
return false ;
|
|
::memcpy( _sz, szcData, cchLen ) ;
|
|
_sz[cchLen] = 0 ;
|
|
_cchMax = _cchStr = cchLen ;
|
|
return true ;
|
|
}
|
|
|
|
SZC BNSTR :: Prefix ( SZC szPrefix )
|
|
{
|
|
assert( szPrefix != NULL ) ;
|
|
UINT cch = ::strlen( szPrefix ) ;
|
|
SZ sz ;
|
|
if ( ! Grow( _cchStr + cch + 1, & sz ) )
|
|
return NULL ;
|
|
::strcpy( sz, szPrefix ) ;
|
|
::strcpy( sz + cch, _sz ) ;
|
|
DeleteSz();
|
|
_cchStr += cch ;
|
|
return _sz = sz ;
|
|
}
|
|
|
|
SZC BNSTR :: Suffix ( SZC szSuffix )
|
|
{
|
|
if ( szSuffix )
|
|
{
|
|
UINT cch = ::strlen( szSuffix ) ;
|
|
|
|
if ( ! Grow( _cchStr + cch + 1 ) )
|
|
return NULL ;
|
|
|
|
::strcpy( _sz + _cchStr, szSuffix ) ;
|
|
_cchStr += cch ;
|
|
}
|
|
|
|
return *this ;
|
|
}
|
|
|
|
SZC BNSTR :: Suffix ( char chSuffix )
|
|
{
|
|
char rgch[2] ;
|
|
rgch[0] = chSuffix ;
|
|
rgch[1] = 0 ;
|
|
return Suffix( rgch );
|
|
}
|
|
|
|
INT BNSTR :: Compare ( SZC szSource, bool bIgnoreCase ) const
|
|
{
|
|
return bIgnoreCase
|
|
? ::stricmp( _sz, szSource )
|
|
: ::strcmp( _sz, szSource );
|
|
}
|
|
|
|
// Comparison
|
|
bool BNSTR :: operator == ( SZC szcSource ) const
|
|
{
|
|
return Compare( szcSource ) == 0 ;
|
|
}
|
|
|
|
bool BNSTR :: operator != ( SZC szSource ) const
|
|
{
|
|
return ! ((*this) == szSource) ;
|
|
}
|
|
|
|
char BNSTR :: operator [] ( UINT iChar ) const
|
|
{
|
|
assert( iChar < Length() ) ;
|
|
return _sz[iChar] ;
|
|
}
|
|
|
|
bool BNSTR :: Vsprintf ( SZC szcFmt, va_list valist )
|
|
{
|
|
// Attempt to "sprintf" the buffer. If it fails, reallocate
|
|
// a larger buffer and try again.
|
|
UINT cbMaxNew = ( _cchMax < 50
|
|
? 50
|
|
: _cchMax ) + 1 ;
|
|
do {
|
|
if ( ! Grow( cbMaxNew ) )
|
|
{
|
|
Reset() ;
|
|
return false ;
|
|
}
|
|
// Cause buffer to grow by 50% on the next cycle (if necessary)
|
|
cbMaxNew = 0 ;
|
|
|
|
// Problem: If the buffer is not big enough, _sz may not have a '\0', and Grow()
|
|
// will subsequently barf on the ::strcpy(). Quick fix:
|
|
|
|
_sz[_cchMax] = '\0';
|
|
|
|
} while ( ::_vsnprintf( _sz, _cchMax, szcFmt, valist ) < 0 ) ;
|
|
|
|
_sz[ _cchMax ] = '\0' ; // 'cause _vsnprintf, like _strncpy, doesn't always append this
|
|
|
|
// Update the string length member
|
|
_cchStr = ::strlen( _sz ) ;
|
|
return true ;
|
|
}
|
|
|
|
bool BNSTR :: Sprintf ( SZC szcFmt, ... )
|
|
{
|
|
va_list valist;
|
|
va_start( valist, szcFmt );
|
|
|
|
bool bOk = Vsprintf( szcFmt, valist ) ;
|
|
|
|
va_end( valist );
|
|
return bOk ;
|
|
}
|
|
|
|
bool BNSTR :: SprintfAppend ( SZC szcFmt, ... )
|
|
{
|
|
BNSTR strTemp ;
|
|
va_list valist;
|
|
va_start( valist, szcFmt );
|
|
|
|
bool bOk = strTemp.Vsprintf( szcFmt, valist ) ;
|
|
va_end( valist );
|
|
|
|
if ( bOk )
|
|
bOk = Suffix( strTemp ) != NULL ;
|
|
return bOk ;
|
|
}
|
|
|
|
// Cr/Lf expansion or contraction
|
|
bool BNSTR :: ExpandNl ()
|
|
{
|
|
UINT iCh ;
|
|
BNSTR str ;
|
|
Transfer( str ) ;
|
|
|
|
for ( iCh = 0 ; iCh < str.Length() ; iCh++ )
|
|
{
|
|
char ch = str[iCh];
|
|
if ( ch == '\n' )
|
|
{
|
|
if ( Suffix( '\r' ) == NULL )
|
|
return false ;
|
|
}
|
|
if ( Suffix( ch ) == NULL )
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
bool BNSTR :: ContractNl ()
|
|
{
|
|
UINT iCh ;
|
|
BNSTR str ;
|
|
Transfer( str ) ;
|
|
|
|
for ( iCh = 0 ; iCh < str.Length() ; iCh++ )
|
|
{
|
|
char ch = str[iCh];
|
|
if ( ch != '\r' )
|
|
{
|
|
if ( Suffix( ch ) == NULL )
|
|
return false ;
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
static char rgchEsc [][2] =
|
|
{
|
|
{ '\a', 'a' },
|
|
{ '\b', 'b' },
|
|
{ '\f', 'f' },
|
|
{ '\n', 'n' },
|
|
{ '\r', 'r' },
|
|
{ '\t', 't' },
|
|
{ '\v', 'v' },
|
|
{ '\'', '\'' },
|
|
{ '\"', '\"' },
|
|
{ '\?', '\?' },
|
|
{ '\\', '\\' },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
bool BNSTR :: ContractEscaped ()
|
|
{
|
|
UINT iCh ;
|
|
BNSTR str ;
|
|
Transfer( str ) ;
|
|
|
|
for ( iCh = 0 ; iCh < str.Length() ; iCh++ )
|
|
{
|
|
char ch = str[iCh];
|
|
if ( ch == '\\' && str.Length() - iCh > 1 )
|
|
{
|
|
char chEsc = 0;
|
|
for ( UINT ie = 0 ; rgchEsc[ie][0] ; ie++ )
|
|
{
|
|
if ( rgchEsc[ie][1] == ch )
|
|
break;
|
|
}
|
|
if ( chEsc = rgchEsc[ie][0] )
|
|
{
|
|
iCh++;
|
|
ch = chEsc;
|
|
}
|
|
}
|
|
if ( Suffix( ch ) == NULL )
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
// Convert unprintable characters to their escaped versions
|
|
bool BNSTR :: ExpandEscaped ()
|
|
{
|
|
UINT iCh ;
|
|
BNSTR str ;
|
|
Transfer( str ) ;
|
|
|
|
for ( iCh = 0 ; iCh < str.Length() ; iCh++ )
|
|
{
|
|
char ch = str[iCh];
|
|
if ( ! isalnum(ch) )
|
|
{
|
|
char chEsc = 0;
|
|
for ( UINT ie = 0 ; rgchEsc[ie][0] ; ie++ )
|
|
{
|
|
if ( rgchEsc[ie][0] == ch )
|
|
break;
|
|
}
|
|
if ( chEsc = rgchEsc[ie][1] )
|
|
{
|
|
if ( Suffix('\\') == NULL )
|
|
return false;
|
|
ch = chEsc;
|
|
}
|
|
}
|
|
if ( Suffix( ch ) == NULL )
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
// Change all alphabetic characters to the given case
|
|
void BNSTR :: UpCase ( bool bToUpper )
|
|
{
|
|
if ( bToUpper )
|
|
::strupr( _sz );
|
|
else
|
|
::strlwr( _sz );
|
|
}
|
|
|
|
//
|
|
// If the given expression string contains the symbolic name,
|
|
// reconstruct it with the replacement name.
|
|
bool BNSTR :: ReplaceSymName (
|
|
SZC szcSymName,
|
|
SZC szcSymNameNew,
|
|
bool bCaseInsensitive )
|
|
{
|
|
SZC szcFound ;
|
|
int cFound = 0 ;
|
|
UINT cchOffset = 0 ;
|
|
// Make a working copy of the sought symbolic name
|
|
BNSTR strSym( szcSymName );
|
|
if ( bCaseInsensitive )
|
|
strSym.UpCase();
|
|
|
|
do
|
|
{
|
|
BNSTR strTemp( Szc() );
|
|
if ( bCaseInsensitive )
|
|
strTemp.UpCase() ;
|
|
// Locate the symbolic name in the temporary copy.
|
|
szcFound = ::strstr( strTemp.Szc()+cchOffset, strSym ) ;
|
|
// If not found, we're done
|
|
if ( szcFound == NULL )
|
|
break ;
|
|
// Check to see if it's really a valid token; i.e., it's delimited.
|
|
if ( ( szcFound == strTemp.Szc()
|
|
|| ! iscsym(*(szcFound-1)) )
|
|
&& ( szcFound >= strTemp.Szc()+strTemp.Length()-strSym.Length()
|
|
|| ! iscsym(*(szcFound+strSym.Length())) )
|
|
)
|
|
{
|
|
// Build new string from preceding characters, the new sym name
|
|
// and trailing chars.
|
|
BNSTR strExprNew ;
|
|
UINT cchFound = szcFound - strTemp.Szc() ;
|
|
strExprNew.Assign( Szc(), cchFound );
|
|
strExprNew += szcSymNameNew ;
|
|
cchOffset = strExprNew.Length();
|
|
strExprNew += Szc() + cchFound + strSym.Length() ;
|
|
Assign( strExprNew );
|
|
cFound++ ;
|
|
}
|
|
else
|
|
{
|
|
// It was imbedded in another token. Skip over it.
|
|
cchOffset = szcFound - strTemp.Szc() + strSym.Length() ;
|
|
}
|
|
} while ( true );
|
|
|
|
return cFound > 0 ;
|
|
}
|
|
|
|
// Find the next occurrence of the given character in the string;
|
|
// Return -1 if not found.
|
|
INT BNSTR :: Index ( char chFind, UINT uiOffset ) const
|
|
{
|
|
if ( uiOffset >= _cchStr )
|
|
return -1 ;
|
|
|
|
SZC szcFound = ::strchr( _sz, chFind );
|
|
return szcFound
|
|
? szcFound - _sz
|
|
: -1 ;
|
|
}
|
|
|
|
// Convert the string to a floating-point number.
|
|
double BNSTR :: Atof ( UINT uiOffset ) const
|
|
{
|
|
return uiOffset < _cchStr
|
|
? ::atof( _sz + uiOffset )
|
|
: -1 ;
|
|
}
|
|
|
|
|
|
// End of BNSTR.CXX
|