320 lines
11 KiB
C++
320 lines
11 KiB
C++
/*
|
|
* NIST Utils Class Library
|
|
* clutils/Str.cc
|
|
* April 1997
|
|
* K. C. Morris
|
|
* David Sauder
|
|
|
|
* Development of this software was funded by the United States Government,
|
|
* and is not subject to copyright.
|
|
*/
|
|
|
|
#include "Str.h"
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
/******************************************************************
|
|
** Procedure: string functions
|
|
** Description: These functions take a character or a string and return
|
|
** a temporary copy of the string with the function applied to it.
|
|
** Parameters:
|
|
** Returns: temporary copy of characters
|
|
** Side Effects:
|
|
** Status: complete
|
|
******************************************************************/
|
|
|
|
char ToLower( const char c ) {
|
|
if( isupper( c ) ) {
|
|
return ( tolower( c ) );
|
|
} else {
|
|
return ( c );
|
|
}
|
|
|
|
}
|
|
|
|
char ToUpper( const char c ) {
|
|
if( islower( c ) ) {
|
|
return ( toupper( c ) );
|
|
} else {
|
|
return ( c );
|
|
}
|
|
}
|
|
|
|
// Place in strNew a lowercase version of strOld.
|
|
char * StrToLower( const char * strOld, char * strNew ) {
|
|
int i = 0;
|
|
|
|
while( strOld[i] != '\0' ) {
|
|
strNew[i] = ToLower( strOld[i] );
|
|
i++;
|
|
}
|
|
strNew[i] = '\0';
|
|
return strNew;
|
|
}
|
|
|
|
const char * StrToLower( const char * word, std::string & s ) {
|
|
char newword [BUFSIZ];
|
|
int i = 0;
|
|
|
|
while( word [i] != '\0' ) {
|
|
newword [i] = ToLower( word [i] );
|
|
++i;
|
|
}
|
|
newword [i] = '\0';
|
|
s = newword;
|
|
return const_cast<char *>( s.c_str() );
|
|
}
|
|
|
|
const char * StrToUpper( const char * word, std::string & s ) {
|
|
char newword [BUFSIZ];
|
|
int i = 0;
|
|
|
|
while( word [i] != '\0' ) {
|
|
newword [i] = ToUpper( word [i] );
|
|
++i;
|
|
}
|
|
newword [i] = '\0';
|
|
s = newword;
|
|
return const_cast<char *>( s.c_str() );
|
|
}
|
|
|
|
const char * StrToConstant( const char * word, std::string & s ) {
|
|
char newword [BUFSIZ];
|
|
int i = 0;
|
|
|
|
while( word [i] != '\0' ) {
|
|
if( word [i] == '/' || word [i] == '.' ) {
|
|
newword [i] = '_';
|
|
} else {
|
|
newword [i] = ToUpper( word [i] );
|
|
}
|
|
++i;
|
|
}
|
|
newword [i] = '\0';
|
|
s = newword;
|
|
return const_cast<char *>( s.c_str() );
|
|
}
|
|
|
|
/**************************************************************//**
|
|
** \fn StrCmpIns (const char * str1, const char * str2)
|
|
** \returns Comparison result
|
|
** Compares two strings case insensitive (lowercase).
|
|
** Returns < 0 when str1 less then str2
|
|
** == 0 when str1 equals str2
|
|
** > 0 when str1 greater then str2
|
|
******************************************************************/
|
|
int StrCmpIns( const char * str1, const char * str2 ) {
|
|
char c1, c2;
|
|
while( ( c1 = tolower( *str1 ) ) == ( c2 = tolower( *str2 ) ) && c1 != '\0' ) {
|
|
str1++;
|
|
str2++;
|
|
}
|
|
return c1 - c2;
|
|
}
|
|
|
|
/**
|
|
* Test if a string ends with the given suffix.
|
|
*/
|
|
bool StrEndsWith( const std::string & s, const char * suf ) {
|
|
if( suf == NULL ) {
|
|
return false;
|
|
}
|
|
std::string suffix = suf;
|
|
size_t sLen = s.length();
|
|
size_t suffixLen = suffix.length();
|
|
if( sLen < suffixLen ) {
|
|
return false;
|
|
}
|
|
if( s.substr( sLen - suffixLen ).compare( suffix ) == 0 ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Extract the next delimited string from the istream.
|
|
*/
|
|
std::string GetLiteralStr( istream & in, ErrorDescriptor * err ) {
|
|
std::string s;
|
|
in >> std::ws; // skip whitespace
|
|
|
|
if( in.good() && in.peek() == STRING_DELIM ) {
|
|
s += in.get();
|
|
bool allDelimsEscaped = true;
|
|
while( in.good() ) {
|
|
if( in.peek() == STRING_DELIM ) {
|
|
// A delimiter closes the string unless it's followed by another
|
|
// delimiter, in which case it's escaped. \S\ starts an ISO
|
|
// 8859 character escape sequence, so we ignore delimiters
|
|
// prefixed with \S\.
|
|
if( !StrEndsWith( s, "\\S\\" ) ) {
|
|
allDelimsEscaped = !allDelimsEscaped;
|
|
}
|
|
} else if( !allDelimsEscaped ) {
|
|
// Found normal char after unescaped delim, so last delim
|
|
// that was appended terminated the string.
|
|
break;
|
|
}
|
|
if( !in.eof() ) {
|
|
s += in.get();
|
|
}
|
|
}
|
|
if( allDelimsEscaped ) {
|
|
// Any delimiters found after the opening delimiter were escaped,
|
|
// so the string is unclosed.
|
|
// non-recoverable error
|
|
err->AppendToDetailMsg( "Missing closing quote on string value.\n" );
|
|
err->AppendToUserMsg( "Missing closing quote on string value.\n" );
|
|
err->GreaterSeverity( SEVERITY_INPUT_ERROR );
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/**************************************************************//**
|
|
** \fn PrettyTmpName (char * oldname)
|
|
** \returns a new capitalized name in a static buffer
|
|
** Capitalizes first char of word, rest is lowercase. Removes '_'.
|
|
** Status: OK 7-Oct-1992 kcm
|
|
******************************************************************/
|
|
const char * PrettyTmpName( const char * oldname ) {
|
|
int i = 0;
|
|
static char newname [BUFSIZ];
|
|
newname [0] = '\0';
|
|
while( ( oldname [i] != '\0' ) && ( i < BUFSIZ ) ) {
|
|
newname [i] = ToLower( oldname [i] );
|
|
if( oldname [i] == '_' ) { /* character is '_' */
|
|
++i;
|
|
newname [i] = ToUpper( oldname [i] );
|
|
}
|
|
if( oldname [i] != '\0' ) {
|
|
++i;
|
|
}
|
|
}
|
|
newname [0] = ToUpper( oldname [0] );
|
|
newname [i] = '\0';
|
|
return newname;
|
|
}
|
|
|
|
/**************************************************************//**
|
|
** \fn PrettyNewName (char * oldname)
|
|
** \returns a new capitalized name
|
|
** Capitalizes first char of word, rest is lowercase. Removes '_'.
|
|
** Side Effects: allocates memory for the new name
|
|
** Status: OK 7-Oct-1992 kcm
|
|
******************************************************************/
|
|
char * PrettyNewName( const char * oldname ) {
|
|
char * name = new char [strlen( oldname ) + 1];
|
|
strcpy( name, PrettyTmpName( oldname ) );
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
*** This function is used to check an input stream following a read. It writes
|
|
*** error messages in the 'ErrorDescriptor &err' argument as appropriate.
|
|
*** 'const char *tokenList' argument contains a string made up of delimiters
|
|
*** that are used to move the file pointer in the input stream to the end of
|
|
*** the value you are reading (i.e. the ending marked by the presence of the
|
|
*** delimiter). The file pointer is moved just prior to the delimiter. If the
|
|
*** tokenList argument is a null pointer then this function expects to find EOF.
|
|
***
|
|
*** If input is being read from a stream then a tokenList should be provided so
|
|
*** this function can push the file pointer up to but not past the delimiter
|
|
*** (i.e. not removing the delimiter from the input stream). If you have a
|
|
*** string containing a single value and you expect the whole string to contain
|
|
*** a valid value, you can change the string to an istrstream, read the value
|
|
*** then send the istrstream to this function with tokenList set to null
|
|
*** and this function will set an error for you if any input remains following
|
|
*** the value.
|
|
|
|
*** If the input stream can be readable again then
|
|
*** - any error states set for the the stream are cleared.
|
|
*** - white space skipped in the input stream
|
|
*** - if EOF is encountered it returns
|
|
*** otherwise it peeks at the next character
|
|
*** - if the tokenList argument exists (i.e. is not null)
|
|
*** then if looks to see if the char peeked at is in the tokenList string
|
|
*** if it is then no error is set in the ErrorDescriptor
|
|
*** if the char peeked at is not in the tokenList string that implies
|
|
*** that there is garbage following the value that was successfully
|
|
*** or unsuccessfully read. The garbage is read until EOF or a
|
|
*** delimiter in the tokenList is found.
|
|
*** - EOF is found you did not recover -> SEVERITY_INPUT_ERROR
|
|
*** - delimiter found you recovered successfully => SEVERITY_WARNING
|
|
*** - if tokenList does not exist then it expects to find EOF, if it does
|
|
*** not then it is an error but the bad chars are not read since you have
|
|
*** no way to know when to stop.
|
|
**/
|
|
Severity CheckRemainingInput( istream & in, ErrorDescriptor * err,
|
|
const char * typeName, // used in error message
|
|
const char * delimiterList ) { // e.g. ",)"
|
|
string skipBuf;
|
|
ostringstream errMsg;
|
|
|
|
if( in.eof() ) {
|
|
// no error
|
|
return err->severity();
|
|
} else if( in.bad() ) {
|
|
// Bad bit must have been set during read. Recovery is impossible.
|
|
err->GreaterSeverity( SEVERITY_INPUT_ERROR );
|
|
errMsg << "Invalid " << typeName << " value.\n";
|
|
err->AppendToUserMsg( errMsg.str().c_str() );
|
|
err->AppendToDetailMsg( errMsg.str().c_str() );
|
|
} else {
|
|
// At most the fail bit is set, so stream can still be read.
|
|
// Clear errors and skip whitespace.
|
|
in.clear();
|
|
in >> ws;
|
|
|
|
if( in.eof() ) {
|
|
// no error
|
|
return err->severity();
|
|
}
|
|
|
|
if( delimiterList != NULL ) {
|
|
// If the next char is a delimiter then there's no error.
|
|
char c = in.peek();
|
|
if( strchr( delimiterList, c ) == NULL ) {
|
|
// Error. Extra input is more than just a delimiter and is
|
|
// now considered invalid. We'll try to recover by skipping
|
|
// to the next delimiter.
|
|
for( in.get( c ); in && !strchr( delimiterList, c ); in.get( c ) ) {
|
|
skipBuf += c;
|
|
}
|
|
|
|
if( strchr( delimiterList, c ) != NULL ) {
|
|
// Delimiter found. Recovery succeeded.
|
|
in.putback( c );
|
|
|
|
errMsg << "\tFound invalid " << typeName << " value...\n";
|
|
err->AppendToUserMsg( errMsg.str().c_str() );
|
|
err->AppendToDetailMsg( errMsg.str().c_str() );
|
|
err->AppendToDetailMsg( "\tdata lost looking for end of "
|
|
"attribute: " );
|
|
err->AppendToDetailMsg( skipBuf.c_str() );
|
|
err->AppendToDetailMsg( "\n" );
|
|
|
|
err->GreaterSeverity( SEVERITY_WARNING );
|
|
} else {
|
|
// No delimiter found. Recovery failed.
|
|
errMsg << "Unable to recover from input error while "
|
|
<< "reading " << typeName << " value.\n";
|
|
err->AppendToUserMsg( errMsg.str().c_str() );
|
|
err->AppendToDetailMsg( errMsg.str().c_str() );
|
|
|
|
err->GreaterSeverity( SEVERITY_INPUT_ERROR );
|
|
}
|
|
}
|
|
} else if( in.good() ) {
|
|
// Error. Have more input, but lack of delimiter list means we
|
|
// don't know where we can safely resume. Recovery is impossible.
|
|
err->GreaterSeverity( SEVERITY_WARNING );
|
|
|
|
errMsg << "Invalid " << typeName << " value.\n";
|
|
|
|
err->AppendToUserMsg( errMsg.str().c_str() );
|
|
err->AppendToDetailMsg( errMsg.str().c_str() );
|
|
}
|
|
}
|
|
return err->severity();
|
|
}
|