aboutsummaryrefslogtreecommitdiff
path: root/core/bgdrtm/src/strings.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/bgdrtm/src/strings.c')
-rw-r--r--core/bgdrtm/src/strings.c1037
1 files changed, 1037 insertions, 0 deletions
diff --git a/core/bgdrtm/src/strings.c b/core/bgdrtm/src/strings.c
new file mode 100644
index 0000000..f7d3641
--- /dev/null
+++ b/core/bgdrtm/src/strings.c
@@ -0,0 +1,1037 @@
+/*
+ * Copyright © 2006-2016 SplinterGU (Fenix/Bennugd)
+ * Copyright © 2002-2006 Fenix Team (Fenix)
+ * Copyright © 1999-2002 José Luis Cebrián Pagüe (Fenix)
+ *
+ * This file is part of Bennu - Game Development
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+
+/****************************************************************************/
+/* FILE : strings.c */
+/* DESCRIPTION : Strings management. Includes any function related to */
+/* variable-length strings. Those strings are allocated */
+/* in dynamic memory with reference counting. */
+/****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef TARGET_BEOS
+#include <posix/assert.h>
+#else
+#include <assert.h>
+#endif
+
+#include "files.h"
+#include "xctype.h"
+
+/****************************************************************************/
+
+#define BLOCK_INCR 1024
+
+#define bit_set(m,b) (((uint32_t *)(m))[(b)>>5] |= 1<<((b)&0x1F))
+#define bit_clr(m,b) (((uint32_t *)(m))[(b)>>5] &= ~(1<<((b)&0x1F)))
+#define bit_tst(m,b) (((uint32_t *)(m))[(b)>>5] & (1<<((b)&0x1F)))
+
+/****************************************************************************/
+/* STATIC VARIABLES : */
+/****************************************************************************/
+
+/* Fixed string memory. The DCB fixed strings are stored here */
+static char * string_mem = NULL ;
+
+static int string_reserved = 0; /* Last fixed string */
+
+static char ** string_ptr = NULL ; /* Pointers to each string's text. Every string is allocated using strdup() or malloc().
+ A pointer of a unused slot is 0.
+ Exception: "fixed" strings are stored in a separate memory block and should not be freed */
+static uint32_t * string_uct = NULL ; /* Usage count for each string. An unused slot has a count of 0 */
+
+static uint32_t * string_bmp = NULL ; /* Bitmap for speed up string creation, and reused freed slots */
+
+static int string_allocated = 0 ; /* How many string slots are available in the ptr, uct and dontfree arrays */
+
+static int string_bmp_start = 0 ; /* Offset of assignable string for reused (32bits each one) */
+
+static int string_last_id = 1 ; /* How many strings slots are used. This is only the bigger id in use + 1.
+ There may be unused slots in this many positions */
+
+/* --------------------------------------------------------------------------- */
+
+void _string_ptoa( char *t, void * ptr )
+{
+ unsigned char c ;
+ int p = ( int ) ptr;
+
+ c = ((( p ) & 0xf0000000 ) >> 28 );
+ *t++ = ( c > 9 ? '7' : '0' ) + c; /* '7' + 10 = 'A' */
+
+ c = ((( p ) & 0x0f000000 ) >> 24 );
+ *t++ = ( c > 9 ? '7' : '0' ) + c; /* '7' + 10 = 'A' */
+
+ c = ((( p ) & 0x00f00000 ) >> 20 );
+ *t++ = ( c > 9 ? '7' : '0' ) + c; /* '7' + 10 = 'A' */
+
+ c = ((( p ) & 0x000f0000 ) >> 16 );
+ *t++ = ( c > 9 ? '7' : '0' ) + c; /* '7' + 10 = 'A' */
+
+ c = ((( p ) & 0x0000f000 ) >> 12 );
+ *t++ = ( c > 9 ? '7' : '0' ) + c; /* '7' + 10 = 'A' */
+
+ c = ((( p ) & 0x00000f00 ) >> 8 );
+ *t++ = ( c > 9 ? '7' : '0' ) + c; /* '7' + 10 = 'A' */
+
+ c = ((( p ) & 0x000000f0 ) >> 4 );
+ *t++ = ( c > 9 ? '7' : '0' ) + c; /* '7' + 10 = 'A' */
+
+ c = ( p ) & 0x0000000f;
+ *t++ = ( c > 9 ? '7' : '0' ) + c; /* '7' + 10 = 'A' */
+
+ *t = '\0';
+}
+
+/* --------------------------------------------------------------------------- */
+
+void _string_ntoa( char *p, unsigned long n )
+{
+ char * i = p ;
+
+ p += 10;
+ if (( long ) n < 0 )
+ {
+ * i++ = '-';
+ p++ ;
+ n = ( unsigned long )( -( long )n ) ;
+ }
+
+ * p = '\0';
+ do
+ {
+ * --p = '0' + ( n % 10 );
+ }
+ while ( n /= 10 ) ;
+
+ if ( p > i ) while (( *i++ = *p++ ) ) ;
+}
+
+/* --------------------------------------------------------------------------- */
+
+void _string_utoa( char *p, unsigned long n )
+{
+ char * i = p ;
+
+ p += 10;
+
+ * p = '\0';
+ do
+ {
+ * --p = '0' + ( n % 10 );
+ }
+ while ( n /= 10 ) ;
+
+ if ( p > i ) while (( *i++ = *p++ ) ) ;
+}
+
+/* --------------------------------------------------------------------------- */
+
+/****************************************************************************/
+/* FUNCTION : string_alloc */
+/****************************************************************************/
+/* int bytes: how many new strings we could need */
+/****************************************************************************/
+/* Increase the size of the internal string arrays. This limits how many */
+/* strings you can have in memory at the same time, and this should be */
+/* called when every identifier slot available is already used. */
+/****************************************************************************/
+
+static void string_alloc( int count )
+{
+ int lim = ( string_allocated >> 5 ) ;
+
+ count = (( count >> 5 ) + 1 ) << 5 ;
+
+ string_allocated += count ;
+
+ string_ptr = ( char ** ) realloc( string_ptr, string_allocated * sizeof( char * ) ) ;
+ string_uct = ( uint32_t * ) realloc( string_uct, string_allocated * sizeof( uint32_t ) ) ;
+ string_bmp = ( uint32_t * ) realloc( string_bmp, ( string_allocated >> 5 ) * sizeof( uint32_t ) );
+
+ if ( !string_ptr || !string_uct || !string_bmp )
+ {
+ fprintf( stderr, "ERROR: Runtime error - string_alloc: out of memory\n" ) ;
+ exit( 0 );
+ }
+
+ memset( &string_bmp[ lim ], '\0', ( count >> 5 ) * sizeof ( uint32_t ) );
+}
+
+/****************************************************************************/
+/* FUNCTION : string_init */
+/****************************************************************************/
+/* Allocate memory for the dynamic arrays. You should call this function */
+/* before anything else in this file. There is enough space for about */
+/* BLOCK_INCR short strings, that should be enough for simple programs. */
+/* More space is allocated as needed. */
+/****************************************************************************/
+
+void string_init()
+{
+ string_alloc( BLOCK_INCR );
+
+ /* Create an empty string with ID 0 */
+
+ string_last_id = 0;
+ string_reserved = 0;
+ string_bmp_start = 0;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_dump */
+/****************************************************************************/
+/* Shows all the strings in memory in the console, including the reference */
+/* count (usage count) of each string. */
+/****************************************************************************/
+
+void string_dump( void ( *wlog )( const char *fmt, ... ) )
+{
+ int i ;
+ int used = 0;
+
+ if ( wlog )
+ wlog( "[STRING] ---- Dumping MaxID=%d strings ----\n", string_allocated ) ;
+ else
+ printf( "[STRING] ---- Dumping MaxID=%d strings ----\n", string_allocated ) ;
+
+ for ( i = 0; i < string_allocated; i++ )
+ {
+ if ( string_ptr[i] )
+ {
+ if ( !string_uct[i] )
+ {
+ if ( i >= string_reserved )
+ {
+ free( string_ptr[i] ) ;
+ string_ptr[i] = NULL ;
+ bit_clr( string_bmp, i );
+ }
+ continue ;
+ }
+ used++;
+ if ( wlog )
+ wlog( "[STRING] %4d [%4d]%s: {%s}\n", i, string_uct[i], ( i >= string_reserved ) ? "" : " STATIC", string_ptr[i] ) ;
+ else
+ printf( "[STRING] %4d [%4d]%s: {%s}\n", i, string_uct[i], ( i >= string_reserved ) ? "" : " STATIC", string_ptr[i] ) ;
+ }
+ else
+ {
+ continue ;
+ }
+ }
+ if ( wlog )
+ wlog( "[STRING] ---- Dumping Used=%d End ----\n", used ) ;
+ else
+ printf( "[STRING] ---- Dumping Used=%d End ----\n", used ) ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_get */
+/****************************************************************************/
+/* int code: identifier of the string you want */
+/****************************************************************************/
+/* Returns the contens of an string. Beware: this pointer with only be */
+/* valid while no other string function is called. */
+/****************************************************************************/
+
+const char * string_get( int code )
+{
+ assert( code < string_allocated && code >= 0 ) ;
+ return string_ptr[code] ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_load */
+/****************************************************************************/
+/* file * fp: the DCB file (must be opened) */
+/* */
+/* This function uses the global "dcb" struct. It should be already filled. */
+/****************************************************************************/
+/* Loads the string portion of a DCB file. This includes an area with all */
+/* the text (that will be stored in the string_mem pointer) and an array of */
+/* the offsets of every string. This function fills the internal arrayswith */
+/* all this data and allocates memory if needed. */
+/****************************************************************************/
+
+void string_load( void * fp, int ostroffs, int ostrdata, int nstrings, int totalsize )
+{
+ uint32_t * string_offset;
+ int n;
+
+ string_mem = malloc( totalsize );
+ assert( string_mem );
+
+ string_offset = ( uint32_t * ) malloc( sizeof( uint32_t ) * nstrings ) ;
+ assert( string_offset );
+
+ file_seek(( file * )fp, ostroffs, SEEK_SET ) ;
+ file_readUint32A(( file * )fp, string_offset, nstrings ) ;
+
+ if ( string_last_id + nstrings > string_allocated )
+ string_alloc((( string_last_id + nstrings - string_allocated ) / BLOCK_INCR + 1 ) * BLOCK_INCR ) ;
+
+ file_seek(( file * )fp, ostrdata, SEEK_SET ) ;
+ file_read(( file * )fp, string_mem, totalsize ) ;
+
+ for ( n = 0 ; n < nstrings ; n++ )
+ {
+ string_ptr[string_last_id + n] = string_mem + string_offset[n] ;
+ string_uct[string_last_id + n] = 0 ;
+ bit_set( string_bmp, string_last_id + n );
+ }
+
+ string_last_id += nstrings ;
+
+ string_last_id = ( string_last_id + 32 ) & ~0x1F;
+
+ string_reserved = string_last_id ;
+ string_bmp_start = string_last_id >> 5;
+
+ free( string_offset ) ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_use */
+/****************************************************************************/
+/* int code: identifier of the string you are using */
+/****************************************************************************/
+/* Increase the usage counter of an string. Use this when you store the */
+/* identifier of the string somewhere. */
+/****************************************************************************/
+
+void string_use( int code )
+{
+ string_uct[code]++ ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_discard */
+/****************************************************************************/
+/* int code: identifier of the string you don't need anymore */
+/****************************************************************************/
+/* Decrease the usage counter of an string. Use this when you retrieve the */
+/* identifier of the string and discard it, or some memory (like private */
+/* variables) containing the string identifier is destroyed. If the usage */
+/* count is decreased to zero, the string will be discarted, and the */
+/* identifier may be used in the future by other string. */
+/****************************************************************************/
+
+void string_discard( int code )
+{
+ if ( code < 0 || code > string_allocated || !string_ptr[code] ) return;
+
+ if ( !string_uct[code] ) return ;
+
+ string_uct[code]-- ;
+
+ if ( !string_uct[code] )
+ {
+ if ( code >= string_reserved )
+ {
+ free( string_ptr[code] ) ;
+ string_ptr[code] = NULL ;
+ bit_clr( string_bmp, code );
+ }
+ }
+}
+
+/****************************************************************************/
+/* FUNCTION : string_getid */
+/****************************************************************************/
+/* Searchs for an available ID and returns it. If none available, more space*/
+/* is allocated for the new string. This is used for new strings only. */
+/****************************************************************************/
+
+static int string_getid()
+{
+ int n, nb, lim, ini ;
+
+ /* Si tengo suficientes alocados, retorno el siguiente segun string_last_id */
+ if ( string_last_id < string_allocated )
+ {
+ if ( !bit_tst( string_bmp, string_last_id ) )
+ {
+ bit_set( string_bmp, string_last_id );
+ return string_last_id++ ;
+ }
+ }
+
+ /* Ya no tengo mas espacio, entonces busco alguno libre entre ~+32 desde el ultimo fijo y ~-32 del ultimo asignado */
+
+ ini = ( string_last_id < string_allocated ) ? ( string_last_id >> 5 ) : string_reserved ;
+ lim = ( string_allocated >> 5 ) ;
+
+ while ( 1 )
+ {
+ for ( n = ini; n < lim ; n++ )
+ {
+ if ( string_bmp[n] != ( uint32_t ) 0xFFFFFFFF ) /* Aca hay 1 libre, busco cual es */
+ {
+ for ( nb = 0; nb < 32; nb++ )
+ {
+ if ( !bit_tst( string_bmp + n, nb ) )
+ {
+ string_last_id = ( n << 5 ) + nb ;
+ bit_set( string_bmp, string_last_id );
+ return string_last_id++ ;
+ }
+ }
+ }
+ }
+ if ( ini == string_reserved ) break;
+ lim = ini;
+ ini = string_reserved;
+ }
+
+ string_last_id = string_allocated ;
+
+ /* Incremento espacio, no habia libres */
+ string_alloc( BLOCK_INCR ) ;
+
+ assert( !bit_tst( string_bmp, string_last_id ) );
+
+ /* Devuelvo string_last_id e incremento en 1, ya que ahora tengo BLOCK_INCR mas que antes */
+ bit_set( string_bmp, string_last_id );
+ return string_last_id++ ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_new */
+/****************************************************************************/
+/* Create a new string. It returns its ID. Note that it uses strdup() */
+/* TODO: do something if no memory available */
+/****************************************************************************/
+
+int string_new( const char * ptr )
+{
+ char * str = strdup( ptr ) ;
+ int id ;
+
+ assert( str ) ;
+
+ id = string_getid() ;
+
+ string_ptr[id] = str ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/*
+ * FUNCTION : string_newa
+ *
+ * Create a new string from a text buffer section
+ *
+ * PARAMS:
+ * ptr Pointer to the text buffer at start position
+ * count Number of characters
+ *
+ * RETURN VALUE:
+ * ID of the new string
+ */
+
+int string_newa( const char * ptr, unsigned count )
+{
+ char * str = malloc( count + 1 );
+ int id ;
+
+ assert( str ) ;
+ id = string_getid() ;
+
+ strncpy( str, ptr, count );
+ str[count] = '\0';
+ string_ptr[id] = str ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_concat */
+/****************************************************************************/
+/* Add some text to an string and return the resulting string. This does not*/
+/* modify the original string, but creates a new one. */
+/****************************************************************************/
+
+int string_concat( int code1, char * str2 )
+{
+ char * str1 ;
+ int len1, len2;
+
+ assert( code1 < string_allocated && code1 >= 0 ) ;
+
+ str1 = string_ptr[code1] ;
+ assert( str1 ) ;
+
+ len1 = strlen( str1 ) ;
+ len2 = strlen( str2 ) + 1 ;
+
+ str1 = ( char * ) realloc( str1, len1 + len2 ) ;
+ assert( str1 ) ;
+
+ memmove( str1 + len1, str2, len2 ) ;
+
+ string_ptr[code1] = str1 ;
+
+ return code1 ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_add */
+/****************************************************************************/
+/* Add an string to another one and return the resulting string. This does */
+/* not modify the original string, but creates a new one. */
+/****************************************************************************/
+
+int string_add( int code1, int code2 )
+{
+ const char * str1 = string_get( code1 ) ;
+ const char * str2 = string_get( code2 ) ;
+ char * str3 ;
+ int id ;
+ int len1, len2;
+
+ assert( str1 ) ;
+ assert( str2 ) ;
+
+ len1 = strlen( str1 ) ;
+ len2 = strlen( str2 ) + 1;
+
+ str3 = ( char * ) malloc( len1 + len2 ) ;
+ assert( str3 ) ;
+
+ memmove( str3, str1, len1 ) ;
+ memmove( str3 + len1, str2, len2 ) ;
+
+ id = string_getid() ;
+
+ string_ptr[id] = str3 ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_ptoa */
+/****************************************************************************/
+/* Convert a pointer to a new created string and return its ID. */
+/****************************************************************************/
+
+int string_ptoa( void * n )
+{
+ char * str ;
+ int id ;
+
+ str = ( char * ) malloc( 10 ) ;
+ assert( str ) ;
+
+ _string_ptoa( str, n ) ;
+
+ id = string_getid() ;
+ string_ptr[id] = str ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_ftoa */
+/****************************************************************************/
+/* Convert a float to a new created string and return its ID. */
+/****************************************************************************/
+
+int string_ftoa( float n )
+{
+ char * str = ( char * ) malloc( 32 ), * ptr = str;
+ int id ;
+
+ assert( str ) ;
+
+ ptr += sprintf( str, "%f", n ) - 1;
+
+ while ( ptr >= str )
+ {
+ if ( *ptr != '0' ) break ;
+ *ptr-- = 0 ;
+ }
+ if ( ptr >= str && *ptr == '.' ) *ptr = 0 ;
+ if ( *str == 0 )
+ {
+ *str = '0';
+ *( str + 1 ) = '\0';
+ }
+
+ id = string_getid() ;
+ string_ptr[id] = str ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_itoa */
+/****************************************************************************/
+/* Convert an integer to a new created string and return its ID. */
+/****************************************************************************/
+
+int string_itoa( int n )
+{
+ char * str ;
+ int id ;
+
+ str = ( char * ) malloc( 16 ) ;
+ assert( str ) ;
+
+ _string_ntoa( str, n ) ;
+
+ id = string_getid() ;
+ string_ptr[id] = str ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_uitoa */
+/****************************************************************************/
+/* Convert an unsigned integer to a new created string and return its ID. */
+/****************************************************************************/
+
+int string_uitoa( unsigned int n )
+{
+ char * str ;
+ int id ;
+
+ str = ( char * ) malloc( 16 ) ;
+ assert( str ) ;
+
+ _string_utoa( str, n ) ;
+
+ id = string_getid() ;
+ string_ptr[id] = str ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_comp */
+/****************************************************************************/
+/* Compare two strings using strcmp and return the result */
+/****************************************************************************/
+
+int string_comp( int code1, int code2 )
+{
+ const char * str1 = string_get( code1 ) ;
+ const char * str2 = string_get( code2 ) ;
+
+ return strcmp( str1, str2 ) ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_char */
+/****************************************************************************/
+/* Extract a character from a string. The parameter nchar can be: */
+/* - 0 or positive: return this character from the left (0 = leftmost) */
+/* - negative: return this character from the right (-1 = rightmost) */
+/* The result is not the ASCII value, but the identifier of a new string */
+/* that is create in the process and contains only the extracted character */
+/****************************************************************************/
+
+int string_char( int n, int nchar )
+{
+ const char * str = string_get( n ) ;
+
+ assert( str ) ;
+
+ if ( nchar < 0 )
+ {
+ nchar = strlen( str ) + nchar ;
+ if ( nchar < 0 ) return 0 ;
+ }
+
+ return str[nchar] ;
+}
+
+/****************************************************************************/
+/* FUNCTION : string_substr */
+/****************************************************************************/
+/* Extract a substring from a string. The parameters can be: */
+/* - 0 or positive: count this character from the left (0 = leftmost) */
+/* - negative: count this character from the right (-1 = rightmost) */
+/* */
+/* NO MORE: If first > last, the two values are swapped before returning the result */
+/****************************************************************************/
+
+int string_substr( int code, int first, int len )
+{
+ const char * str = string_get( code ) ;
+ char * ptr ;
+ int rlen, n ;
+
+ assert( str ) ;
+ rlen = strlen( str ) ;
+
+ if ( first < 0 )
+ {
+ first = rlen + first ;
+ if ( first < 0 ) return string_new( "" ) ;
+ }
+ else
+ if ( first > ( rlen - 1 ) ) return string_new( "" ) ;
+
+ if ( len < 0 )
+ {
+ len = rlen + ( len + 2 ) - first - 1 ;
+ if ( len < 1 ) return string_new( "" ) ;
+ }
+
+ if (( first + len ) > rlen ) len = ( rlen - first ) ;
+
+ ptr = ( char * )malloc( len + 1 ) ;
+ memcpy( ptr, str + first, len ) ;
+ ptr[len] = '\0' ;
+
+ n = string_getid() ;
+ string_ptr[n] = ptr ;
+ string_uct[n] = 0 ;
+
+ return n ;
+}
+
+/*
+ * FUNCTION : string_find
+ *
+ * Find a substring. Returns the position of the leftmost character (0
+ * for the leftmost position) or -1 if the string was not found.
+ *
+ * PARAMS:
+ * code1 Code of the string
+ * code2 Code of the substring
+ * first Character to start the search
+ * (negative to search backwards)
+ *
+ * RETURN VALUE:
+ * Result of the comparison
+ */
+
+int string_find( int code1, int code2, int first )
+{
+ char * str1 = ( char * ) string_get( code1 ) ;
+ char * str2 = ( char * ) string_get( code2 ) ;
+ char * p = str1, * p1, * p2 ;
+
+ assert( str1 && str2 ) ;
+
+ if ( first < 0 )
+ {
+ first += strlen( str1 ) ;
+ if ( first < 0 ) return -1;
+ str1 += first;
+ }
+ else
+ {
+ /* Avoid use strlen */
+ while ( first-- && *str1 ) str1++;
+ if ( !*str1 ) return -1;
+ }
+
+ while ( *str1 )
+ {
+ if ( *str1 == *str2 )
+ {
+ p1 = str1 + 1;
+ p2 = str2 + 1;
+
+ while ( *p1 && *p2 && *p1 == *p2 )
+ {
+ p1++;
+ p2++;
+ }
+ if ( !*p2 ) return str1 - p;
+ }
+ str1++;
+ }
+
+ return -1 ;
+}
+
+/*
+ * FUNCTION : string_ucase
+ *
+ * Convert an string to upper case. It does not alter the given string, but
+ * creates a new string in the correct case and returns its id.
+ *
+ * PARAMS:
+ * code Internal code of original string
+ *
+ * RETURN VALUE:
+ * Code of the resulting string
+ */
+
+int string_ucase( int code )
+{
+ const char * str = string_get( code ) ;
+ char * base, * ptr ;
+ int id ;
+
+ assert( str ) ;
+
+ base = ( char * )malloc( strlen( str ) + 1 ) ;
+ assert( base ) ;
+
+ for ( ptr = base; *str ; ptr++, str++ ) *ptr = TOUPPER( *str ) ;
+ ptr[0] = '\0' ;
+
+ id = string_getid() ;
+ string_ptr[id] = base ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/*
+ * FUNCTION : string_lcase
+ *
+ * Convert an string to lower case. It does not alter the given string, but
+ * creates a new string in the correct case and returns its id.
+ *
+ * PARAMS:
+ * code Internal code of original string
+ *
+ * RETURN VALUE:
+ * Code of the resulting string
+ */
+
+int string_lcase( int code )
+{
+ const char * str = string_get( code ) ;
+ char * base, * ptr ;
+ int id ;
+
+ assert( str ) ;
+
+ base = ( char * )malloc( strlen( str ) + 1 ) ;
+ assert( base ) ;
+
+ for ( ptr = base; *str ; ptr++, str++ ) *ptr = TOLOWER( *str ) ;
+ ptr[0] = '\0' ;
+
+ id = string_getid() ;
+ string_ptr[id] = base ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}
+
+/*
+ * FUNCTION : string_strip
+ *
+ * Create a copy of a string, without any leading or ending blanks
+ *
+ * PARAMS:
+ * code Internal code of original string
+ *
+ * RETURN VALUE:
+ * Code of the resulting string
+ */
+
+int string_strip( int code )
+{
+ const char * str = string_get( code ) ;
+ char * base, * ptr;
+ int id = string_new( str );
+
+ ptr = base = ( char * )string_get( id ) ;
+
+ assert( ptr );
+
+ while ( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' ) str++;
+ while ( *str ) *ptr++ = *str++;
+ while ( ptr > base && ( ptr[-1] == ' ' || ptr[-1] == '\n' || ptr[-1] == '\r' || ptr[-1] == '\t' ) ) ptr--;
+ *ptr = '\0';
+
+ return id ;
+}
+
+/*
+ * FUNCTION : string_format
+ *
+ * Format a number using the given characters
+ *
+ * PARAMS:
+ * number Number to format
+ * spec Format specification
+ *
+ * RETURN VALUE:
+ * Code of the resulting string
+ */
+
+int string_format( double number, int dec, char point, char thousands )
+{
+ char * str = malloc( 128 );
+ char * s = str, * t, * p = NULL;
+ int c, id, neg ;
+
+ assert( str );
+
+ if ( dec == -1 )
+ s += sprintf( str, "%f", number );
+ else
+ s += sprintf( str, "%.*f", dec, number );
+
+ neg = (*str == '-') ? 1 : 0;
+
+ if ( dec )
+ {
+ p = str;
+ while ( *p && *p != '.' ) p++;
+ if ( *p ) *p = point;
+ }
+ else
+ {
+ p = s;
+ }
+
+ /* p = where decimal point is */
+ /* s = where '\0' is */
+
+ if ( thousands )
+ t = s + (p - (str + neg) - 1 ) / 3;
+ else
+ t = s;
+
+ c = 0;
+ while ( s >= str )
+ {
+ if ( isdigit( *s ) && s < p )
+ {
+ if ( c == 3 )
+ {
+ *t-- = thousands ;
+ c = 0;
+ continue;
+ }
+ else
+ c++;
+ }
+ *t-- = *s-- ;
+ }
+
+ id = string_getid() ;
+
+ string_ptr[id] = str ;
+ string_uct[id] = 0 ;
+
+ return id;
+}
+
+/*
+ * FUNCTION : string_casecmp
+ *
+ * Compare two strings (case-insensitive version)
+ *
+ * PARAMS:
+ * code1 Code of the first string
+ * code2 Code of the second string
+ *
+ * RETURN VALUE:
+ * Result of the comparison
+ */
+
+int string_casecmp( int code1, int code2 )
+{
+ unsigned char * str1 = ( unsigned char * ) string_get( code1 ) ;
+ unsigned char * str2 = ( unsigned char * ) string_get( code2 ) ;
+
+ while ( *str1 || *str2 )
+ {
+ if ( TOUPPER( *str1 ) != TOUPPER( *str2 ) ) return TOUPPER( *str1 ) - TOUPPER( *str2 );
+
+ str1++;
+ str2++;
+ }
+
+ return 0 ;
+}
+
+/*
+ * FUNCTION : string_pad
+ *
+ * Add spaces to the left or right of a string
+ *
+ * PARAMS:
+ * code Code of the string
+ * total Total length of the final string
+ * align 0 = align to the right; 1 = align to the left
+ *
+ * RETURN VALUE:
+ * Result of the comparison
+ */
+
+int string_pad( int code, int total, int align )
+{
+ const char * ptr = string_get( code );
+
+ int len;
+ int spaces = 0;
+ int id;
+ char * str;
+
+ assert( ptr );
+ len = strlen( ptr );
+ if ( len < total ) spaces = total - len;
+
+ if ( !spaces ) return string_new( ptr ) ;
+
+ str = malloc( total + 1 );
+ assert( str );
+
+ if ( !align )
+ {
+ memset( str, ' ', spaces );
+ strcpy( str + spaces, ptr ) ;
+ }
+ else
+ {
+ strcpy( str, ptr ) ;
+ memset( str + len, ' ', spaces ) ;
+ str[total] = '\0';
+ }
+
+ id = string_getid() ;
+ string_ptr[id] = str ;
+ string_uct[id] = 0 ;
+
+ return id ;
+}