421 lines
11 KiB
C++
421 lines
11 KiB
C++
|
/***************************************************************************
|
||
|
* Char.h *
|
||
|
* *
|
||
|
* Convenient constants, macros, and inline functions for manipulation of *
|
||
|
* characters and strings. *
|
||
|
* *
|
||
|
* HISTORY *
|
||
|
* Name Date Description *
|
||
|
* *
|
||
|
* arvo 07/01/93 Initial coding. *
|
||
|
* *
|
||
|
*--------------------------------------------------------------------------*
|
||
|
* Copyright (C) 1999, James Arvo *
|
||
|
* *
|
||
|
* 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. See http://www.fsf.org/copyleft/gpl.html *
|
||
|
* *
|
||
|
* This program is distributed in the hope that it will be useful, but *
|
||
|
* WITHOUT EXPRESS OR IMPLIED WARRANTY of merchantability or fitness for *
|
||
|
* any particular purpose. See the GNU General Public License for more *
|
||
|
* details. *
|
||
|
* *
|
||
|
***************************************************************************/
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include "Char.h"
|
||
|
|
||
|
namespace ArvoMath {
|
||
|
|
||
|
typedef char *charPtr;
|
||
|
|
||
|
// Treat "str" as a file name, and return just the directory
|
||
|
// portion -- i.e. strip off the name of the leaf object (but
|
||
|
// leave the final "/".
|
||
|
const char *getPath( const char *str, char *buff )
|
||
|
{
|
||
|
int k;
|
||
|
for( k = strlen( str ) - 1; k >= 0; k-- )
|
||
|
{
|
||
|
if( str[k] == Slash ) break;
|
||
|
}
|
||
|
for( int i = 0; i <= k; i++ ) buff[i] = str[i];
|
||
|
buff[k+1] = NullChar;
|
||
|
return buff;
|
||
|
}
|
||
|
|
||
|
// Treat "str" as a file name, and return just the file name
|
||
|
// portion -- i.e. strip off everything up to and including
|
||
|
// the final "/".
|
||
|
const char *getFile( const char *str, char *buff )
|
||
|
{
|
||
|
int k;
|
||
|
int len = strlen( str );
|
||
|
for( k = len - 1; k >= 0; k-- )
|
||
|
{
|
||
|
if( str[k] == Slash ) break;
|
||
|
}
|
||
|
for( int i = 0; i < len - k; i++ ) buff[i] = str[ i + k + 1 ];
|
||
|
return buff;
|
||
|
}
|
||
|
|
||
|
int getPrefix( const char *str, char *buff )
|
||
|
{
|
||
|
int len = 0;
|
||
|
while( *str != NullChar && *str != Period )
|
||
|
{
|
||
|
*buff++ = *str++;
|
||
|
len++;
|
||
|
}
|
||
|
*buff = NullChar;
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
int getSuffix( const char *str, char *buff )
|
||
|
{
|
||
|
int n = strlen( str );
|
||
|
int k = n - 1;
|
||
|
while( k >= 0 && str[k] != Period ) k--;
|
||
|
for( int i = k + 1; i < n; i++ ) *buff++ = str[i];
|
||
|
*buff = NullChar;
|
||
|
return n - k - 1;
|
||
|
}
|
||
|
|
||
|
const char* toString( int number, char *buff )
|
||
|
{
|
||
|
static char local_buff[32];
|
||
|
char *str = ( buff == NULL ) ? local_buff : buff;
|
||
|
sprintf( str, "%d", number );
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
const char* toString( float number, char *buff )
|
||
|
{
|
||
|
static char local_buff[32];
|
||
|
char *str = ( buff == NULL ) ? local_buff : buff;
|
||
|
sprintf( str, "%g", number );
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
int isInteger( const char *str )
|
||
|
{
|
||
|
int n = strlen( str );
|
||
|
for( int i = 0; i < n; i++ )
|
||
|
{
|
||
|
char c = str[i];
|
||
|
if( isDigit(c) ) continue;
|
||
|
if( c == Plus || c == Minus ) continue;
|
||
|
if( c == Space ) continue;
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// Test to see if a string has a given suffix.
|
||
|
int hasSuffix( const char *string, const char *suffix )
|
||
|
{
|
||
|
if( suffix == NULL ) return 1; // The null suffix always matches.
|
||
|
if( string == NULL ) return 0; // The null string can only have a null suffix.
|
||
|
int m = strlen( string );
|
||
|
int k = strlen( suffix );
|
||
|
if( k <= 0 ) return 1; // Empty suffix always matches.
|
||
|
if( m < k + 1 ) return 0; // String is too short to have this suffix.
|
||
|
|
||
|
// See if the file has the given suffix.
|
||
|
int s = m - k; // Beginning of suffix (if it matches).
|
||
|
for( int i = 0; i < k; i++ )
|
||
|
if( string[ s + i ] != suffix[ i ] ) return 0;
|
||
|
return s; // Always > 0.
|
||
|
}
|
||
|
|
||
|
// Test to see if a string has a given prefix.
|
||
|
int hasPrefix( const char *string, const char *prefix )
|
||
|
{
|
||
|
if( prefix == NULL ) return 1; // The null prefix always matches.
|
||
|
if( string == NULL ) return 0; // The null string can only have a null suffix.
|
||
|
while( *prefix )
|
||
|
{
|
||
|
if( *prefix++ != *string++ ) return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// Test to see if the string contains the given character.
|
||
|
int inString( char c, const char *str )
|
||
|
{
|
||
|
if( str == NULL || str[0] == NullChar ) return 0;
|
||
|
while( *str != '\0' )
|
||
|
if( *str++ == c ) return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int nullString( const char *str )
|
||
|
{
|
||
|
return str == NULL || str[0] == NullChar;
|
||
|
}
|
||
|
|
||
|
const char *stripSuffix( const char *string, const char *suffix, char *buff )
|
||
|
{
|
||
|
static char local_buff[256];
|
||
|
if( buff == NULL ) buff = local_buff;
|
||
|
buff[0] = NullChar;
|
||
|
if( !hasSuffix( string, suffix ) ) return NULL;
|
||
|
int s = strlen( string ) - strlen( suffix );
|
||
|
for( int i = 0; i < s; i++ )
|
||
|
{
|
||
|
buff[i] = string[i];
|
||
|
}
|
||
|
buff[s] = NullChar;
|
||
|
return buff;
|
||
|
}
|
||
|
|
||
|
int getIndex( const char *pat, const char *str )
|
||
|
{
|
||
|
int p_len = strlen( pat );
|
||
|
int s_len = strlen( str );
|
||
|
if( p_len == 0 || s_len == 0 ) return -1;
|
||
|
for( int i = 0; i <= s_len - p_len; i++ )
|
||
|
{
|
||
|
int match = 1;
|
||
|
for( int j = 0; j < p_len; j++ )
|
||
|
{
|
||
|
if( str[ i + j ] != pat[ j ] ) { match = 0; break; }
|
||
|
}
|
||
|
if( match ) return i;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int getSubstringAfter( const char *pat, const char *str, char *buff )
|
||
|
{
|
||
|
int ind = getIndex( pat, str );
|
||
|
if( ind < 0 ) return -1;
|
||
|
int p_len = strlen( pat );
|
||
|
int k = 0;
|
||
|
for( int i = ind + p_len; ; i++ )
|
||
|
{
|
||
|
buff[ k++ ] = str[ i ];
|
||
|
if( str[ i ] == NullChar ) break;
|
||
|
}
|
||
|
return k;
|
||
|
}
|
||
|
|
||
|
const char *SubstringAfter( const char *pat, const char *str, char *user_buff )
|
||
|
{
|
||
|
static char temp[128];
|
||
|
char *buff = ( user_buff != NULL ) ? user_buff : temp;
|
||
|
int k = getSubstringAfter( pat, str, buff );
|
||
|
if( k > 0 ) return buff;
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
const char *metaString( const char *str, char *user_buff )
|
||
|
{
|
||
|
static char temp[128];
|
||
|
char *buff = ( user_buff != NULL ) ? user_buff : temp;
|
||
|
sprintf( buff, "\"%s\"", str );
|
||
|
return buff;
|
||
|
}
|
||
|
|
||
|
// This is the opposite of metaString.
|
||
|
const char *stripQuotes( const char *str, char *user_buff )
|
||
|
{
|
||
|
static char temp[128];
|
||
|
char *buff = ( user_buff != NULL ) ? user_buff : temp;
|
||
|
char *b = buff;
|
||
|
for(;;)
|
||
|
{
|
||
|
if( *str != DoubleQuote ) *b++ = *str;
|
||
|
if( *str == NullChar ) break;
|
||
|
str++;
|
||
|
}
|
||
|
return buff;
|
||
|
}
|
||
|
|
||
|
int getIntFlag( const char *flags, const char *flag, int &value )
|
||
|
{
|
||
|
while( *flags )
|
||
|
{
|
||
|
if( hasPrefix( flags, flag ) )
|
||
|
{
|
||
|
int k = strlen( flag );
|
||
|
if( flags[k] == '=' )
|
||
|
{
|
||
|
value = atoi( flags + k + 1 );
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
flags++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int getFloatFlag( const char *flags, const char *flag, float &value )
|
||
|
{
|
||
|
while( *flags )
|
||
|
{
|
||
|
if( hasPrefix( flags, flag ) )
|
||
|
{
|
||
|
int k = strlen( flag );
|
||
|
if( flags[k] == '=' )
|
||
|
{
|
||
|
value = atof( flags + k + 1 );
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
flags++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
SortedList::SortedList( sort_type type_, int ascend_ )
|
||
|
{
|
||
|
type = type_;
|
||
|
ascend = ascend_;
|
||
|
num_elements = 0;
|
||
|
max_elements = 0;
|
||
|
sorted = 1;
|
||
|
list = NULL;
|
||
|
}
|
||
|
|
||
|
SortedList::~SortedList()
|
||
|
{
|
||
|
Clear();
|
||
|
delete[] list;
|
||
|
}
|
||
|
|
||
|
void SortedList::Clear()
|
||
|
{
|
||
|
// Delete all the private copies of the strings and re-initialize the
|
||
|
// list. Reuse the same list, expanding it when necessary.
|
||
|
for( int i = 0; i < num_elements; i++ )
|
||
|
{
|
||
|
delete list[i];
|
||
|
list[i] = NULL;
|
||
|
}
|
||
|
num_elements = 0;
|
||
|
sorted = 1;
|
||
|
}
|
||
|
|
||
|
SortedList &SortedList::operator<<( const char *str )
|
||
|
{
|
||
|
// Add a new string to the end of the list, expanding the list if necessary.
|
||
|
// Mark the list as unsorted, so that the next reference to an element will
|
||
|
// cause the list to be sorted again.
|
||
|
if( num_elements == max_elements ) Expand();
|
||
|
list[ num_elements++ ] = strdup( str );
|
||
|
sorted = 0;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
const char *SortedList::operator()( int i )
|
||
|
{
|
||
|
// Return the i'th element of the list. Sort first if necessary.
|
||
|
static char *null = "";
|
||
|
if( num_elements == 0 || i < 0 || i >= num_elements ) return null;
|
||
|
if( !sorted ) Sort();
|
||
|
return list[i];
|
||
|
}
|
||
|
|
||
|
void SortedList::Expand()
|
||
|
{
|
||
|
// Create a new list of twice the size and copy the old list into it.
|
||
|
// This doubles "max_elements", but leaves "num_elements" unchanged.
|
||
|
if( max_elements == 0 ) max_elements = 1;
|
||
|
max_elements *= 2;
|
||
|
charPtr *new_list = new charPtr[ max_elements ];
|
||
|
for( int i = 0; i < max_elements; i++ )
|
||
|
new_list[i] = ( i < num_elements ) ? list[i] : NULL;
|
||
|
delete[] list;
|
||
|
list = new_list;
|
||
|
}
|
||
|
|
||
|
void SortedList::Swap( int i, int j )
|
||
|
{
|
||
|
char *temp = list[i];
|
||
|
list[i] = list[j];
|
||
|
list[j] = temp;
|
||
|
}
|
||
|
|
||
|
int SortedList::inOrder( int p, int q ) const
|
||
|
{
|
||
|
int test;
|
||
|
if( type == sort_alphabetic )
|
||
|
test = ( strcmp( list[p], list[q] ) <= 0 );
|
||
|
else
|
||
|
{
|
||
|
int len_p = strlen( list[p] );
|
||
|
int len_q = strlen( list[q] );
|
||
|
test = ( len_p < len_q ) ||
|
||
|
( len_p == len_q && strcmp( list[p], list[q] ) <= 0 );
|
||
|
}
|
||
|
if( ascend ) return test;
|
||
|
return !test;
|
||
|
}
|
||
|
|
||
|
// This is an insertion sort that operates on subsets of the
|
||
|
// input defined by the step length.
|
||
|
void SortedList::InsertionSort( int start, int size, int step )
|
||
|
{
|
||
|
for( int i = 0; i + step < size; i += step )
|
||
|
{
|
||
|
for( int j = i; j >= 0; j -= step )
|
||
|
{
|
||
|
int p = start + j;
|
||
|
int q = p + step;
|
||
|
if( inOrder( p, q ) ) break;
|
||
|
Swap( p, q );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This is a Shell sort.
|
||
|
void SortedList::Sort()
|
||
|
{
|
||
|
for( int step = num_elements / 2; step > 1; step /= 2 )
|
||
|
for( int start = 0; start < step; start++ )
|
||
|
InsertionSort( start, num_elements - start, step );
|
||
|
InsertionSort( 0, num_elements, 1 );
|
||
|
sorted = 1;
|
||
|
}
|
||
|
|
||
|
void SortedList::SetOrder( sort_type type_, int ascend_ )
|
||
|
{
|
||
|
if( type_ != type || ascend_ != ascend )
|
||
|
{
|
||
|
type = type_;
|
||
|
ascend = ascend_;
|
||
|
sorted = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int getstring( std::istream &in, const char *str )
|
||
|
{
|
||
|
char ch;
|
||
|
if( str == NULL ) return 1;
|
||
|
while( *str != NullChar )
|
||
|
{
|
||
|
in >> ch;
|
||
|
if( *str != ch ) return 0;
|
||
|
str++;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
std::istream &skipWhite( std::istream &in )
|
||
|
{
|
||
|
char c;
|
||
|
while( in.get(c) )
|
||
|
{
|
||
|
if( !isWhite( c ) )
|
||
|
{
|
||
|
in.putback(c);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return in;
|
||
|
}
|
||
|
};
|