home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
xwphescr.zip
/
XWPH0208.ZIP
/
src
/
helpers
/
xstring.c
< prev
Wrap
C/C++ Source or Header
|
2002-07-08
|
57KB
|
1,694 lines
/*
*@@sourcefile xstring.c:
* string functions with memory management.
*
* Usage: All OS/2 programs.
*
* The functions in this file are intended as a replacement
* to the C library string functions (such as strcpy, strcat)
* in cases where the length of the string is unknown and
* dynamic memory management is desirable.
*
* Instead of char* pointers, the functions in this file
* operate on XSTRING structures, which in turn contain
* a char* pointer pointing to heap memory, which is managed
* automatically.
*
* Besides being convenient, these functions are highly
* optimized to use as few strlen's and memcpy's as
* possible.
*
* Using these functions has the following advantages:
*
* -- Automatic memory management. For example, xstrcat will
* automatically allocate new memory if the new string
* does not fit into the present buffer.
*
* -- The length of the string is always known. Instead
* of running strlen (which consumes time), XSTRING.ulLength
* always contains the current length of the string.
*
* -- The functions also differentiate between allocated
* memory and the length of the string. That is, for
* iterative appends, you can pre-allocate memory to
* avoid excessive reallocations.
*
* These functions are also used internally by the
* WarpIN BSString class (and related classes).
*
* Usage:
*
* 1) Allocate an XSTRING structure on the stack. Always
* call xstrInit on the structure, like this:
*
+ XSTRING str;
+ xstrInit(&str, 0); // no pre-allocation
*
* Alternatively, use xstrCreate to have an XSTRING
* allocated from the heap.
*
* Always call xstrClear(&str) to free allocated
* memory. Otherwise you'll get memory leaks.
* (For heap XSTRING's from xstrCreate, use xstrFree.)
*
* 2) To copy something into the string, use xstrcpy.
* To append something to the string, use xstrcat.
* See those functions for samples.
*
* 3) If you need the char* pointer (e.g. for a call
* to another function), use XSTRING.psz. However,
* you should ONLY modify the psz pointer yourself
* if the other XSTRING members are updated accordingly.
* You may, for example, change single characters
* in the psz buffer. By contrast, if you change the
* length of the string, you must update XSTRING.ulLength.
* Otherwise these functions will get into trouble.
*
* Also, you should never assume that the "psz"
* pointer has not changed after you have called
* one of the xstr* functions because these can
* always reallocate the buffer if more memory
* was needed.
*
* 4) If (and only if) you have a char* buffer which
* is free()'able (e.g. from strdup()), you can
* use xstrset to avoid duplicate copying.
*
* Function prefixes:
* -- xstr* extended string functions.
*
* The functions in this file used to be in stringh.c
* before V0.9.3 (2000-04-01). These have been largely
* rewritten with V0.9.6 (2000-11-01) and are now much
* more efficient.
*
* Note: Version numbering in this file relates to XWorkplace
* version numbering.
*
*@@added V0.9.3 (2000-04-01) [umoeller]
*@@header "helpers\xstring.h"
*/
/*
* Copyright (C) 1999-2002 Ulrich Möller.
* This file is part of the "XWorkplace helpers" source package.
* This 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, in version 2 as it comes in the
* "COPYING" file of the XWorkplace main distribution.
* 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.
*/
#define OS2EMX_PLAIN_CHAR
// this is needed for "os2emx.h"; if this is defined,
// emx will define PSZ as _signed_ char, otherwise
// as unsigned char
#include <os2.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "setup.h" // code generation and debugging options
#include "helpers\stringh.h"
#define DONT_REPLACE_XSTR_MALLOC
#include "helpers\xstring.h" // extended string helpers
/*
*@@category: Helpers\C helpers\String management\XStrings (with memory management)
* See xstring.c.
*/
#ifdef __DEBUG_MALLOC_ENABLED__
/*
*@@ xstrInitDebug:
*
*@@added V0.9.14 (2001-08-01) [umoeller]
*/
void XWPENTRY xstrInitDebug(PXSTRING pxstr,
ULONG ulPreAllocate,
PCSZ file,
unsigned long line,
PCSZ function)
{
memset(pxstr, 0, sizeof(XSTRING));
if (ulPreAllocate)
{
pxstr->psz = (PSZ)memdMalloc(ulPreAllocate,
file,
line,
function);
pxstr->cbAllocated = ulPreAllocate;
// ulLength is still zero
*(pxstr->psz) = 0;
pxstr->ulDelta = ulPreAllocate * 10 / 100;
}
// else: pxstr->ulDelta is still 0
pxstr->file = file;
pxstr->line = line;
pxstr->function = function;
}
#endif // __DEBUG_MALLOC_ENABLED__
/*
*@@ xstrInit:
* initializes a new XSTRING. Always call this before
* using an XSTRING from the stack.
*
* If (ulPreAllocate != 0), memory is pre-allocated
* for the string, but the string will be empty
* (its first byte is set to '\0'). In addition,
* pxstr->ulDelta will be set to 10% of ulPreAllocate.
*
* This is useful if you plan to add more stuff to
* the string later so we don't have to reallocate
* all the time in xstrcat.
*
* Do not use this on an XSTRING which is already
* initialized (this would cause memory leaks).
* Use xstrcpy or xstrset instead.
*
*@@added V0.9.6 (2000-11-01) [umoeller]
*@@changed V0.9.9 (2001-03-09) [umoeller]: added ulDelta
*/
void xstrInit(PXSTRING pxstr, // in/out: string
ULONG ulPreAllocate) // in: if > 0, memory to allocate
{
memset(pxstr, 0, sizeof(XSTRING));
if (ulPreAllocate)
{
pxstr->psz = (PSZ)malloc(ulPreAllocate);
pxstr->cbAllocated = ulPreAllocate;
// ulLength is still zero
*(pxstr->psz) = 0;
pxstr->ulDelta = ulPreAllocate * 10 / 100;
}
// else: pxstr->ulDelta is still 0
}
/*
*@@ xstrInitSet2:
* this can be used instead of xstrInit if you
* have a free()'able string you want to initialize
* the XSTRING with.
*
* This does not create a copy of pszNew. Instead,
* pszNew is used as the member string in pxstr
* directly.
*
* Do not use this on an XSTRING which is already
* initialized (this would cause memory leaks).
* Use xstrcpy or xstrset instead.
*
* Example:
*
+ XSTRING str;
+ xstrInitSet(&str, strdup("blah"), 0);
*
*@@added V0.9.16 (2002-01-13) [umoeller]
*/
void xstrInitSet2(PXSTRING pxstr, // in/out: string
PSZ pszNew, // in: malloc'd string to load pxstr with
ULONG ulNewLength) // in: length of pszNew or 0 to run strlen()
{
if (!pszNew)
memset(pxstr, 0, sizeof(XSTRING));
else
{
if (!ulNewLength)
ulNewLength = strlen(pszNew);
pxstr->psz = pszNew;
pxstr->ulLength = ulNewLength;
pxstr->cbAllocated = ulNewLength + 1;
pxstr->ulDelta = ulNewLength * 10 / 100;
}
}
/*
*@@ xstrInitSet:
* shortcut to xstrInitSet2 to retain compatibility.
*
*@@added V0.9.6 (2000-11-01) [umoeller]
*@@changed V0.9.9 (2001-03-09) [umoeller]: added ulDelta
*/
void xstrInitSet(PXSTRING pxstr,
PSZ pszNew)
{
xstrInitSet2(pxstr, pszNew, 0);
}
#ifdef __DEBUG_MALLOC_ENABLED__
/*
*@@ xstrInitCopyDebug:
*
*@@added V0.9.16 (2002-01-05) [umoeller]
*/
void XWPENTRY xstrInitCopyDebug(PXSTRING pxstr,
PCSZ pcszSource,
ULONG ulExtraAllocate,
PCSZ file,
unsigned long line,
PCSZ function)
{
if (pxstr)
{
memset(pxstr, 0, sizeof(XSTRING));
if (pcszSource)
{
pxstr->ulLength = strlen(pcszSource);
if (pxstr->ulLength)
{
// we do have a source string:
pxstr->cbAllocated = pxstr->ulLength + 1 + ulExtraAllocate;
pxstr->psz = (PSZ)memdMalloc(pxstr->cbAllocated,
file,
line,
function);
// V0.9.16 (2002-01-05) [umoeller]
memcpy(pxstr->psz,
pcszSource,
pxstr->ulLength);
pxstr->psz[pxstr->ulLength] = '\0';
pxstr->ulDelta = pxstr->cbAllocated * 10 / 100;
}
}
}
}
#endif
/*
*@@ xstrInitCopy:
* this can be used instead of xstrInit if you
* want to initialize an XSTRING with a copy
* of an existing string. This is a shortcut
* for xstrInit() and then xstrcpy().
*
* As opposed to xstrInitSet, this does create
* a copy of pcszSource.
*
* Do not use this on an XSTRING which is already
* initialized (this would cause memory leaks).
* Use xstrcpy or xstrset instead.
*
* Example:
*
+ XSTRING str;
+ xstrInitCopy(&str, "blah");
*
*@@added V0.9.6 (2000-11-01) [umoeller]
*@@changed V0.9.7 (2000-12-31) [umoeller]: added ulExtraAllocate
*@@changed V0.9.9 (2001-03-09) [umoeller]: added ulDelta
*@@changed V0.9.16 (2002-01-05) [umoeller]: use memcpy instead of strcpy
*/
void xstrInitCopy(PXSTRING pxstr,
PCSZ pcszSource,
ULONG ulExtraAllocate) // in: if > 0, extra memory to allocate
{
if (pxstr)
{
memset(pxstr, 0, sizeof(XSTRING));
if (pcszSource)
{
if (pxstr->ulLength = strlen(pcszSource))
{
// we do have a source string:
pxstr->cbAllocated = pxstr->ulLength + 1 + ulExtraAllocate;
pxstr->psz = (PSZ)malloc(pxstr->cbAllocated);
// V0.9.16 (2002-01-05) [umoeller]
memcpy(pxstr->psz,
pcszSource,
pxstr->ulLength);
pxstr->psz[pxstr->ulLength] = '\0';
pxstr->ulDelta = pxstr->cbAllocated * 10 / 100;
}
}
}
}
/*
*@@ xstrClear:
* clears the specified stack XSTRING and
* frees allocated memory.
*
* This is the reverse to xstrInit.
*
*@@added V0.9.6 (2000-11-01) [umoeller]
*/
void xstrClear(PXSTRING pxstr) // in/out: string
{
if (pxstr->psz)
free(pxstr->psz);
memset(pxstr, 0, sizeof(XSTRING));
}
/*
*@@ xstrReserve:
* this function makes sure that the specified
* XSTRING has at least ulBytes bytes allocated.
*
* This function is useful if you plan to do
* a lot of string replacements or appends and
* want to avoid that the buffer is reallocated
* with each operation. Before those operations,
* call this function to make room for the operations.
*
* If ulBytes is smaller than the current allocation,
* this function does nothing.
*
* pxstr->ulDelta has no effect here.
*
* The XSTRING must be initialized before the
* call.
*
* Returns the new total no. of allocated bytes.
*
*@@added V0.9.7 (2001-01-07) [umoeller]
*@@changed V0.9.9 (2001-03-09) [umoeller]: now using ulDelta
*@@changed V0.9.12 (2001-05-21) [umoeller]: now reporting error on realloc fail
*/
ULONG xstrReserve(PXSTRING pxstr,
ULONG ulBytes)
{
ULONG cbNeeded = ulBytes;
if (cbNeeded > pxstr->cbAllocated)
{
// we need more memory than we have previously
// allocated:
ULONG cbAllocate;
if (pxstr->ulDelta)
{
// delta specified: allocate in chunks of that
// V0.9.9 (2001-03-07) [umoeller]
ULONG cbExtra = cbNeeded - pxstr->cbAllocated;
cbExtra = ( (cbExtra + pxstr->ulDelta)
/ pxstr->ulDelta
)
* pxstr->ulDelta;
// if we need 3 extra bytes and ulDelta is 10,
// this gives us 10 extra bytes
// if we need 3 extra bytes and ulDelta is 1000,
// this gives us 1000 extra bytes
cbAllocate = pxstr->cbAllocated + cbExtra;
}
else
// no delta specified:
cbAllocate = cbNeeded;
// V0.9.9 (2001-03-05) [umoeller]: use realloc;
// this gives the C runtime a chance to expand the
// existing block
#ifdef __DEBUG_MALLOC_ENABLED__
if (pxstr->psz = (PSZ)memdRealloc(pxstr->psz,
cbAllocate,
pxstr->file,
pxstr->line,
pxstr->function))
#else
if (pxstr->psz = (PSZ)realloc(pxstr->psz,
cbAllocate))
#endif
// if pxstr->psz is NULL, realloc behaves like malloc
pxstr->cbAllocated = cbAllocate;
// ulLength is unchanged
else
// error: V0.9.12 (2001-05-21) [umoeller]
pxstr->cbAllocated = 0;
}
// else: we have enough memory
return pxstr->cbAllocated;
}
/*
*@@ xstrShrink:
* reallocates the string buffer so that it
* is exactly the length of the string with
* its null byte, if the string has excessive
* memory allocated. Useful if you are sure
* that the string won't grow again.
*
*@@added V0.9.16 (2001-10-08) [umoeller]
*/
void XWPENTRY xstrShrink(PXSTRING pxstr)
{
if ( (pxstr)
&& (pxstr->psz)
&& (pxstr->cbAllocated > pxstr->ulLength + 1)
)
{
pxstr->psz = (PSZ)realloc(pxstr->psz,
pxstr->ulLength + 1);
pxstr->cbAllocated = pxstr->ulLength + 1;
}
}
/*
*@@ xstrCreate:
* allocates a new XSTRING from the heap
* and calls xstrInit on it.
*
* Always use xstrFree to free associated
* resources.
*
*@@added V0.9.6 (2000-11-01) [umoeller]
*/
PXSTRING xstrCreate(ULONG ulPreAllocate)
{
PXSTRING pxstr;
if (pxstr = (PXSTRING)malloc(sizeof(XSTRING)))
xstrInit(pxstr, ulPreAllocate);
return pxstr;
}
/*
*@@ xstrFree:
* frees the specified heap XSTRING, which must
* have been created using xstrCreate.
*
* This uses a pointer to a PXSTRING so that
* the pointer is automatically reset to NULL
* by this function AND to avoid confusion
* with xstrClear.
*
*@@added V0.9.6 (2000-11-01) [umoeller]
*@@changed V0.9.12 (2001-05-24) [umoeller]: changed prototype to use pointer to pointer
*/
VOID xstrFree(PXSTRING *ppxstr) // in/out: string
{
PXSTRING p;
if ( ppxstr
&& (p = *ppxstr)
)
{
xstrClear(p);
free(p);
*ppxstr = NULL;
}
}
/*
*@@ xstrset2:
* sets the specified XSTRING to a new string
* without copying it.
*
* pxstr is cleared before the new string is set.
*
* This ONLY works if pszNew has been allocated from
* the heap using malloc() or strdup() and is thus
* free()'able.
*
* This assumes that exactly strlen(pszNew) + 1
* bytes have been allocated for pszNew, which
* is true if pszNew comes from strdup().
*
* With this function, you can pass in the
* length of the string in ulNewLength.
* Otherwise use xstrset.
*
*@@added V0.9.16 (2002-01-13) [umoeller]
*/
ULONG xstrset2(PXSTRING pxstr, // in/out: string
PSZ pszNew, // in: heap PSZ to use
ULONG ulNewLength) // in: length of string or 0 to run strlen here
{
if (!pxstr)
return 0; // V0.9.9 (2001-02-14) [umoeller]
xstrClear(pxstr);
if (pxstr->psz = pszNew)
{
if (!ulNewLength)
ulNewLength = strlen(pszNew);
pxstr->ulLength = ulNewLength;
pxstr->cbAllocated = ulNewLength + 1;
pxstr->ulDelta = ulNewLength * 10 / 100;
}
// else null string: cbAllocated and ulLength are 0 already
return pxstr->ulLength;
}
/*
*@@ xstrset:
* shortcut for xstrset2 for retaining compatibility.
*
*@@added V0.9.6 (2000-11-01) [umoeller]
*@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
*/
ULONG xstrset(PXSTRING pxstr, // in/out: string
PSZ pszNew) // in: heap PSZ to use
{
return xstrset2(pxstr, pszNew, 0);
}
/*
*@@ xstrcpy:
* copies pcszSource to pxstr, for which memory is allocated
* as necessary.
*
* If pxstr contains something, its contents are overwritten.
*
* With ulSourceLength, specify the length of pcszSource
* or 0.
*
* -- If you specify 0, this function will run
* strlen(pcszSource) and copy the entire source
* string.
*
* -- If you already know the length of pcszSource, you
* can speed this function up by specifying the
* length.
*
* -- You are required to specify ulSourceLength if you
* only want to copy a substring, or if pcszSource is
* not zero-terminated.
*
* Returns the length of the new string (excluding the null
* terminator), or null upon errors.
*
* Example:
*
+ XSTRING str;
+ xstrInit(&str, 0);
+ xstrcpy(&str, "blah", 0);
*
* This sequence can be abbreviated using xstrInitCopy.
*
* Memory cost: If there's enough room in pxstr for
* pcszSource, none. Otherwise pxstr is reallocated
* to hold enough room for pcszSource.
*
*@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcpy
*@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
*@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
*@@changed V0.9.9 (2001-01-28) [lafaix]: fixed memory leak and NULL source behavior
*@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
*@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
*@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
*@@changed V0.9.12 (2001-05-21) [umoeller]: added xstrReserve error checking
*/
ULONG xstrcpy(PXSTRING pxstr, // in/out: string
PCSZ pcszSource, // in: source, can be NULL
ULONG ulSourceLength) // in: length of pcszSource or 0
{
if (!pxstr)
return 0; // V0.9.9 (2001-02-14) [umoeller]
if (pcszSource)
{
// source specified:
if (ulSourceLength == 0)
// but not length:
ulSourceLength = strlen(pcszSource);
}
else
ulSourceLength = 0;
if (ulSourceLength)
{
// we do have a source string:
if (xstrReserve(pxstr,
// required memory:
ulSourceLength + 1))
{
memcpy(pxstr->psz,
pcszSource,
ulSourceLength);
pxstr->psz[ulSourceLength] = '\0';
// V0.9.9 (2001-02-16) [umoeller]
// we must do this or otherwise we require pcszSource
// to be zero-terminated... not a good idea
}
else
pxstr->ulLength = 0; // error V0.9.12 (2001-05-21) [umoeller]
}
else
{
// no source specified or source is empty:
if (pxstr->cbAllocated)
// we did have a string: set to empty,
// but leave allocated memory intact
*(pxstr->psz) = 0;
// else
// we had no string previously: in that case
// psz and ulLength and cbAllocated are all still NULL
}
// in all cases, set new length
pxstr->ulLength = ulSourceLength;
return pxstr->ulLength;
}
/*
*@@ xstrcpys:
* shortcut to xstrcpy if the source is an XSTRING also.
*
*@@added V0.9.9 (2001-02-14) [umoeller]
*/
ULONG xstrcpys(PXSTRING pxstr,
const XSTRING *pcstrSource)
{
if (!pcstrSource)
return 0;
return xstrcpy(pxstr, pcstrSource->psz, pcstrSource->ulLength);
}
/*
*@@ xstrcat:
* appends pcszSource to pxstr, for which memory is
* reallocated if necessary.
*
* If pxstr is empty, this behaves just like xstrcpy.
*
* With ulSourceLength, specify the length of pcszSource
* or 0 (see xstrcpy for details).
*
* Returns the length of the new string (excluding the null
* terminator) if the string was changed, or 0 if nothing
* happened.
*
* Note: To append a single character, xstrcatc is faster
* than xstrcat.
*
* Example:
*
+ XSTRING str;
+ xstrInit(&str, 0);
+ xstrcpy(&str, "blah", 0);
+ xstrcat(&str, "blup", 0);
*
* After this, str.psz points to a new string containing
* "blahblup".
*
* Memory cost: If there's enough room in pxstr for
* pcszSource, none. Otherwise pxstr is reallocated
* to hold enough room for pcszSource.
*
*@@changed V0.9.1 (99-12-20) [umoeller]: fixed memory leak
*@@changed V0.9.1 (2000-01-03) [umoeller]: crashed if pszString was null; fixed
*@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcat
*@@changed V0.9.3 (2000-05-11) [umoeller]: returned 0 if pszString was initially empty; fixed
*@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
*@@changed V0.9.7 (2000-12-10) [umoeller]: return value was wrong
*@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
*@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
*@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
*/
ULONG xstrcat(PXSTRING pxstr, // in/out: string
PCSZ pcszSource, // in: source, can be NULL
ULONG ulSourceLength) // in: length of pcszSource or 0
{
ULONG ulrc = 0;
if (pxstr)
{
if (pcszSource)
{
if (ulSourceLength == 0)
ulSourceLength = strlen(pcszSource);
if (ulSourceLength)
{
// we do have a source string:
// 1) memory management
xstrReserve(pxstr,
// required memory:
pxstr->ulLength + ulSourceLength + 1);
// 2) append source string:
memcpy(pxstr->psz + pxstr->ulLength,
pcszSource,
ulSourceLength);
pxstr->psz[pxstr->ulLength + ulSourceLength] = '\0';
// V0.9.9 (2001-02-16) [umoeller]
// we must do this or otherwise we require pcszSource
// to be zero-terminated... not a good idea
// in all cases, set new length
pxstr->ulLength += ulSourceLength;
ulrc = pxstr->ulLength; // V0.9.7 (2000-12-10) [umoeller]
} // end if (ulSourceLength)
}
// else no source specified or source is empty:
// do nothing
}
return ulrc;
}
/*
*@@ xstrcatc:
* this is similar to xstrcat, except that this is
* for a single character. This is a bit faster than
* xstrcat.
*
* If "c" is \0, nothing happens.
*
* If pxstr is empty, this behaves just like xstrcpy.
*
* Returns the length of the new string (excluding the null
* terminator) if the string was changed, or 0 if nothing
* happened.
*
* Example:
*
+ XSTRING str;
+ xstrInit(&str, 0);
+ xstrcpy(&str, "blu", 0);
+ xstrcatc(&str, 'p');
*
* After this, str.psz points to a new string containing
* "blup".
*
* Memory cost: If there's enough room in pxstr for
* c, none. Otherwise pxstr is reallocated
* to hold enough room for c.
*
*@@added V0.9.7 (2000-12-10) [umoeller]
*@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
*/
ULONG xstrcatc(PXSTRING pxstr, // in/out: string
CHAR c) // in: character to append, can be \0
{
ULONG ulrc = 0;
if ((pxstr) && (c))
{
// 1) memory management
xstrReserve(pxstr,
// required memory:
pxstr->ulLength // existing length, without null terminator
+ 1 // new character
+ 1); // null terminator
// 2) append character:
pxstr->psz[pxstr->ulLength] = c;
pxstr->psz[pxstr->ulLength + 1] = '\0';
// in all cases, set new length
(pxstr->ulLength)++;
ulrc = pxstr->ulLength;
} // end if ((pxstr) && (c))
return ulrc;
}
/*
*@@ xstrcats:
* shortcut to xstrcat if the source is an XSTRING also.
*
*@@added V0.9.9 (2001-02-14) [umoeller]
*/
ULONG xstrcats(PXSTRING pxstr,
const XSTRING *pcstrSource)
{
if (!pcstrSource)
return 0;
return xstrcat(pxstr,
pcstrSource->psz,
pcstrSource->ulLength);
}
/*
*@@ xstrrpl:
* replaces "cReplLen" characters in pxstr, starting
* at the position "ulFirstReplPos", with the first
* "cReplaceWithLen" characters from pcszReplaceWith.
*
* If cReplaceWithLen is 0, characters are removed only.
*
* Returns the new length of the string, excluding
* the null terminator, or 0 if the replacement failed
* (e.g. because the offsets were too large).
*
* This has been extracted from xstrFindReplace because
* if you already know the position of a substring,
* you can now call this directly. This properly
* reallocates the string if more memory is needed.
*
* Example:
*
+ XSTRING xstr, xstrReplacement;
+ xstrInitCopy(&xstr, "This is a test string.");
+ // positions: 0123456789012345678901
+ // 1 2
+
+ xstrrpl(&xstr,
+ 10, // position of "test"
+ 4, // length of "test"
+ "stupid",
+ 6); // strlen("stupid")
*
* This would yield "This is a stupid string."
*
* Memory cost: If there's enough room in pxstr for
* the replacement, none. Otherwise pxstr is reallocated
* to hold enough room for the replacement.
*
*@@added V0.9.7 (2001-01-15) [umoeller]
*@@changed V0.9.9 (2001-01-29) [lafaix]: fixed unnecessary allocation when pxstr was big enough
*@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
*@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
*@@changed V0.9.11 (2001-04-22) [umoeller]: replaced replacement XSTRING with PCSZ
*@@changed V0.9.14 (2001-07-07) [umoeller]: this did nothing if cReplaceWithLen == 0, fixed
*/
ULONG xstrrpl(PXSTRING pxstr, // in/out: string
ULONG ulFirstReplOfs, // in: ofs of first char to replace
ULONG cReplLen, // in: no. of chars to replace
PCSZ pcszReplaceWith, // in: string to replace chars with
ULONG cReplaceWithLen) // in: length of replacement string
// (this MUST be specified; if 0, chars are removed only)
{
ULONG ulrc = 0;
// security checks...
if ( (pxstr) // V0.9.9 (2001-02-14) [umoeller]
&& (ulFirstReplOfs + cReplLen <= pxstr->ulLength)
&& ( (pcszReplaceWith)
|| (cReplaceWithLen == 0) // fixed V0.9.14 (2001-07-07) [umoeller]
)
)
{
// size of new buffer:
ULONG cbNeeded = pxstr->ulLength // existing
+ cReplaceWithLen // plus replacement string length
- cReplLen // minus replaced characters
+ 1; // plus null terminator
// offset where pszSearch was found
PSZ pFound = pxstr->psz + ulFirstReplOfs;
// now check if we have enough memory...
if (cbNeeded > pxstr->cbAllocated)
{
// we need more memory than we have previously
// allocated:
// reallocate using ulDelta V0.9.9 (2001-03-07) [umoeller]
ULONG cbAllocate;
PSZ pszNew;
if (pxstr->ulDelta)
{
// delta specified: allocate in chunks of that
// V0.9.9 (2001-03-07) [umoeller]
ULONG cbExtra = cbNeeded - pxstr->cbAllocated;
cbExtra = ( (cbExtra + pxstr->ulDelta)
/ pxstr->ulDelta
)
* pxstr->ulDelta;
// if we need 3 extra bytes and ulDelta is 10,
// this gives us 10 extra bytes
// if we need 3 extra bytes and ulDelta is 1000,
// this gives us 1000 extra bytes
cbAllocate = pxstr->cbAllocated + cbExtra;
}
else
// no delta specified:
cbAllocate = cbNeeded;
// allocate new buffer
pszNew = (PSZ)malloc(cbAllocate);
// end V0.9.9 (2001-03-07) [umoeller]
if (ulFirstReplOfs)
// "found" was not at the beginning:
// copy from beginning up to found-offset
memcpy(pszNew,
pxstr->psz,
ulFirstReplOfs); // up to "found"
if (cReplaceWithLen)
{
// we have a replacement:
// insert it next...
// we no longer can be sure that pcszReplaceWith
// is null terminated, so terminate explicitly
// V0.9.11 (2001-04-22) [umoeller]
memcpy(pszNew + ulFirstReplOfs,
pcszReplaceWith,
cReplaceWithLen);
*(pszNew + ulFirstReplOfs + cReplaceWithLen) = '\0';
}
// copy rest:
// pxstr frontFOUNDtail
// 0 1
// 01234567890123
// │ │ │ │
// │ │ └─ ulFirstReplOfs + cReplLen = 10
// │ │ │
// │ └─ ulFirstReplOfs = 5
// │ │
// pxstr->ulLength = 14
memcpy(pszNew + ulFirstReplOfs + cReplaceWithLen,
pFound + cReplLen,
// remaining bytes:
pxstr->ulLength - ulFirstReplOfs - cReplLen // 9
+ 1); // null terminator
// replace old buffer with new one
free(pxstr->psz);
pxstr->psz = pszNew;
pxstr->ulLength = cbNeeded - 1;
pxstr->cbAllocated = cbAllocate; // V0.9.9 (2001-03-07) [umoeller]
} // end if (pxstr->cbAllocated < cbNeeded)
else
{
// we have enough memory left,
// we can just overwrite in the middle...
// fixed V0.9.9 (2001-01-29) [lafaix]
// calc length of string after "found"
ULONG cTailLength = pxstr->ulLength - ulFirstReplOfs - cReplLen;
// first, we move the end to its new location
// (memmove handles overlap if needed)
memmove(pFound + cReplaceWithLen,
pFound + cReplLen,
cTailLength + 1); // including null terminator
// now overwrite "found" in the middle
if (cReplaceWithLen)
memcpy(pFound,
pcszReplaceWith,
cReplaceWithLen); // no null terminator
// that's it; adjust the string length now
pxstr->ulLength = cbNeeded - 1;
}
ulrc = cbNeeded - 1;
} // end checks
return ulrc;
}
/*
*@@ xstrFindWord:
* searches for pstrFind in pxstr, starting at ulOfs.
* However, this only finds pstrFind if it's a "word",
* i.e. surrounded by one of the characters in the
* pcszBeginChars and pcszEndChars array.
*
* This is similar to strhFindWord, but this uses
* strhmemfind for fast searching, and it doesn't
* have to calculate the string lengths because these
* already in XSTRING.
*
* Returns 0 if no "word" was found, or the offset
* of the "word" in pxstr if found.
*
*@@added V0.9.6 (2000-11-12) [umoeller]
*@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL string crashs
*/
PSZ xstrFindWord(const XSTRING *pxstr, // in: buffer to search ("haystack")
ULONG ulOfs, // in: where to begin search (0 = start)
const XSTRING *pstrFind, // in: word to find ("needle")
size_t *pShiftTable, // in: shift table (see strhmemfind)
PBOOL pfRepeatFind, // in: repeat find? (see strhmemfind)
PCSZ pcszBeginChars, // suggestion: "\x0d\x0a ()/\\-,."
PCSZ pcszEndChars) // suggestion: "\x0d\x0a ()/\\-,.:;"
{
PSZ pReturn = 0;
if (pxstr && pstrFind) // V0.9.9 (2001-02-14) [umoeller]
{
ULONG ulFoundLen = pstrFind->ulLength;
if ((pxstr->ulLength) && (ulFoundLen))
{
PCSZ p = pxstr->psz + ulOfs;
do // while p
{
if (p = (PSZ)strhmemfind(p, // in: haystack
pxstr->ulLength - (p - pxstr->psz),
// remaining length of haystack
pstrFind->psz,
ulFoundLen,
pShiftTable,
pfRepeatFind))
{
// string found:
// check if that's a word
if (strhIsWord(pxstr->psz,
p,
ulFoundLen,
pcszBeginChars,
pcszEndChars))
{
// valid end char:
pReturn = (PSZ)p;
break;
}
p += ulFoundLen;
}
} while (p);
}
}
return pReturn;
}
/*
*@@ xstrFindReplace:
* replaces the first occurence of pstrSearch with
* pstrReplace in pxstr.
*
* Starting with V0.9.6, this operates entirely on
* XSTRING's for speed because we then know the string
* lengths already and can use memcpy instead of strcpy.
* This new version should be magnitudes faster,
* especially with large string bufffers.
*
* None of the pointers can be NULL, but if pstrReplace
* is empty, this effectively erases pstrSearch in pxstr.
*
* Returns the length of the new string (exclusing the
* null terminator) or 0 if pszSearch was not found
* (and pxstr was therefore not changed).
*
* This starts the search at *pulOfs. If
* (*pulOfs == 0), this starts from the beginning
* of pxstr.
*
* If the string was found, *pulOfs will be set to the
* first character after the new replacement string. This
* allows you to call this func again with the same strings
* to have several occurences replaced (see the example below).
*
* There are two wrappers around this function which
* work on C strings instead (however, thus losing the
* speed advantage):
*
* -- strhFindReplace operates on C strings only;
*
* -- xstrFindReplaceC uses C strings for the search and replace
* parameters.
*
* <B>Example usage:</B>
*
+ XSTRING strBuf,
+ strFind,
+ strRepl;
+ size_t ShiftTable[256];
+ BOOL fRepeat = FALSE;
+ ULONG ulOffset = 0;
+
+ xstrInitCopy(&strBuf, "Test phrase 1. Test phrase 2.", 0);
+ xstrInitSet(&strFind, "Test");
+ xstrInitSet(&strRepl, "Dummy");
+ while (xstrFindReplace(&str,
+ &ulPos, // in/out: offset
+ &strFind, // search
+ &strRepl, // replace
+ ShiftTable,
+ &fRepeat))
+ ;
*
* would replace all occurences of "Test" in str with
* "Dummy".
*
* Memory cost: Calls xstrrpl if pstrSearch was found.
*
*@@changed V0.9.0 [umoeller]: totally rewritten.
*@@changed V0.9.0 (99-11-08) [umoeller]: crashed if *ppszBuf was NULL. Fixed.
*@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxrpl
*@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
*@@changed V0.9.6 (2000-11-12) [umoeller]: now using strhmemfind
*@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrrpl; extracted new xstrrpl
*/
ULONG xstrFindReplace(PXSTRING pxstr, // in/out: string
PULONG pulOfs, // in: where to begin search (0 = start);
// out: ofs of first char after replacement string
const XSTRING *pstrSearch, // in: search string; cannot be NULL
const XSTRING *pstrReplace, // in: replacement string; cannot be NULL
size_t *pShiftTable, // in: shift table (see strhmemfind)
PBOOL pfRepeatFind) // in: repeat find? (see strhmemfind)
{
ULONG ulrc = 0; // default: not found
if ((pxstr) && (pstrSearch) && (pstrReplace))
{
ULONG cSearchLen = pstrSearch->ulLength;
// can we search this?
if ( (*pulOfs < pxstr->ulLength)
&& (cSearchLen)
)
{
// yes:
ULONG ulOfs = *pulOfs;
PCSZ pFound;
if (pFound = (PCSZ)strhmemfind(pxstr->psz + ulOfs, // in: haystack
pxstr->ulLength - ulOfs,
pstrSearch->psz,
cSearchLen,
pShiftTable,
pfRepeatFind))
{
ULONG ulFirstReplOfs = pFound - pxstr->psz;
// found in buffer from ofs:
// replace pFound with pstrReplace
ulrc = xstrrpl(pxstr,
ulFirstReplOfs, // where to start
cSearchLen, // chars to replace
pstrReplace->psz,
pstrReplace->ulLength); // adjusted V0.9.11 (2001-04-22) [umoeller]
// return new length
*pulOfs = ulFirstReplOfs + pstrReplace->ulLength;
} // end if (pFound)
} // end if ( (*pulOfs < pxstr->ulLength) ...
} // end if ((pxstr) && (pstrSearch) && (pstrReplace))
return ulrc;
}
/*
*@@ xstrFindReplaceC:
* wrapper around xstrFindReplace() which allows using
* C strings for the find and replace parameters.
*
* This creates two temporary XSTRING's for pcszSearch
* and pcszReplace and thus cannot use the shift table
* for repetitive searches. As a result, this is slower
* than xstrFindReplace.
*
* If you search with the same strings several times,
* you'll be better off using xstrFindReplace() directly.
*
*@@added V0.9.6 (2000-11-01) [umoeller]
*@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrcrpl
*/
ULONG xstrFindReplaceC(PXSTRING pxstr, // in/out: string
PULONG pulOfs, // in: where to begin search (0 = start);
// out: ofs of first char after replacement string
PCSZ pcszSearch, // in: search string; cannot be NULL
PCSZ pcszReplace) // in: replacement string; cannot be NULL
{
XSTRING xstrFind,
xstrReplace;
size_t ShiftTable[256];
BOOL fRepeat = FALSE;
// initialize find/replace strings... note that the
// C strings are not free()'able, so we MUST NOT use xstrClear
// before leaving
xstrInitSet(&xstrFind, (PSZ)pcszSearch);
xstrInitSet(&xstrReplace, (PSZ)pcszReplace);
return xstrFindReplace(pxstr, pulOfs, &xstrFind, &xstrReplace, ShiftTable, &fRepeat);
}
// static encoding table for xstrEncode
static PSZ apszEncoding[] =
{
"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
"%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
"%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
"%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
"%28", "%29", "%2A", "%2B", "%2C", "%2D", "%2E", "%2F",
"%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",
"%38", "%39", "%3A", "%3B", "%3C", "%3D", "%3E", "%3F",
"%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",
"%48", "%49", "%4A", "%4B", "%4C", "%4D", "%4E", "%4F",
"%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",
"%58", "%59", "%5A", "%5B", "%5C", "%5D", "%5E", "%5F",
"%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",
"%68", "%69", "%6A", "%6B", "%6C", "%6D", "%6E", "%6F",
"%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",
"%78", "%79", "%7A", "%7B", "%7C", "%7D", "%7E", "%7F",
"%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
"%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F",
"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
"%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F",
"%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7",
"%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF",
"%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7",
"%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF",
"%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7",
"%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF",
"%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7",
"%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF",
"%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7",
"%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF",
"%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7",
"%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
};
/*
*@@ xstrEncode:
* encodes characters in a string.
*
* This searches pxstr for all occurences of the
* characters in pcszEncode (which must be a
* null-terminated list of characters to be
* encoded). Each occurence that is found is
* replaced with "%hh", with "hh" being the
* two-digit hex number of the encoded character.
*
* For example, to encode strings for the XCenter,
* set pcszEncode to "%,();=".
*
* Returns the no. of characters replaced.
*
* NOTE: You must make sure that pcszEncode ALWAYS
* contains the "%" character as well, which must
* always be encoded (i.e. escaped) because it is
* used for encoding the characters. Otherwise
* you won't be able to decode the string again.
*
* Example: To encode all occurences of
* "a", "b", and "c" in a string, do this:
*
+ XSTRING str;
+ xstrInitCopy(&str, "Sample characters.";
+ xstrEncode(&str, "abc%";
*
* would convert str to contain:
*
+ S%61mple %63hara%63ters.
*
* Memory cost: None, except for that of xstrcpy.
*
*@@added V0.9.9 (2001-02-28) [umoeller]
*@@changed V0.9.9 (2001-03-06) [lafaix]: rewritten.
*/
ULONG xstrEncode(PXSTRING pxstr, // in/out: string to convert
PCSZ pcszEncode) // in: characters to encode (e.g. "%,();=")
{
ULONG ulrc = 0,
ul,
ulEncodeLength;
if ( (pxstr)
&& (pxstr->ulLength)
&& (pcszEncode)
&& (ulEncodeLength = strlen(pcszEncode)))
{
PSZ pszDest = (PSZ)malloc(pxstr->ulLength * 3
+ 1),
pszDestCurr = pszDest;
if (pszDest)
{
for (ul = 0;
ul < pxstr->ulLength;
ul++)
{
ULONG ulEncode;
for (ulEncode = 0;
ulEncode < ulEncodeLength;
ulEncode++)
{
if (pxstr->psz[ul] == pcszEncode[ulEncode])
{
// use the static encoding table for speed
memcpy(pszDestCurr,
apszEncoding[(unsigned char)pcszEncode[ulEncode]],
3);
pszDestCurr += 3;
ulrc++;
goto iterate;
}
}
*pszDestCurr++ = pxstr->psz[ul];
iterate:
;
}
}
// something was encoded; update pxstr
if (ulrc)
{
*pszDestCurr = 0;
xstrcpy(pxstr, pszDest, pszDestCurr-pszDest);
}
free(pszDest);
}
return ulrc;
}
/*
*@@ xstrDecode:
* decodes a string previously encoded by xstrEncode.
*
* This simply assumes that all '%' characters in
* pxstr introduce encodings and the next two characters
* after '%' always are a hex character code. This
* only recognizes hex in upper case. All this will
* work properly with encodings from xstrEncode.
*
* Returns the no. of encodings replaced.
*
* Memory cost: None.
*
*@@added V0.9.9 (2001-02-28) [umoeller]
*@@changed V0.9.9 (2001-03-06) [lafaix]: removed memory allocation
*@@changed V0.9.16 (2002-02-02) [umoeller]: added cKey
*/
ULONG xstrDecode2(PXSTRING pxstr, // in/out: string to be decoded
CHAR cKey) // in: encoding key (normally '%')
{
ULONG ulrc = 0;
if ( (pxstr)
&& (pxstr->ulLength)
)
{
const char *pSource = pxstr->psz;
PSZ pszDest = (PSZ)pSource,
pDest = (PSZ)pSource;
CHAR c;
while ((c = *pSource++))
{
// pSource points to next char now
if (c == cKey)
{
static char ach[] = "0123456789ABCDEF";
// convert two chars after '%'
CHAR c2, // first char after '%' --> hi-nibble
c3; // second char after '%' --> lo-nibble
const char *p2, // for first char: points into ach or is NULL
*p3; // for second char: points into ach or is NULL
if ( (c2 = *pSource)
&& (p2 = strchr(ach, c2))
&& (c3 = *(pSource + 1))
&& (p3 = strchr(ach, c3))
)
{
// both chars after '%' were valid:
*pDest++ = // lo-nibble:
(p3 - ach) // 0 for '0', 10 for 'A', ...
// hi-nibble:
+ ((p2 - ach) << 4);
// go on after that
pSource += 2;
// raise return count
ulrc++;
// next in loop
continue;
}
}
// not encoding, or null after '%', or invalid encoding:
// just leave thisalone
*pDest++ = c;
} // while ((ch = *pSource++))
if (ulrc)
{
*pDest = 0;
pxstr->ulLength = (pDest - pszDest);
}
}
return ulrc;
}
/*
*@@ xstrDecode:
* added for compatibility with exports.
*
*@@added V0.9.16 (2002-02-02) [umoeller]
*/
ULONG xstrDecode(PXSTRING pxstr)
{
return xstrDecode2(pxstr, '%');
}
/*
*@@ xstrConvertLineFormat:
* converts between line formats.
*
* If (fToCFormat == CRLF2LF), all \r\n pairs are replaced
* with \n chars (UNIX or C format).
*
* Reversely, if (fToCFormat == LF2CRLF), all \n chars
* are converted to \r\n pairs (DOS and OS/2 formats).
* No check is made whether this has already been done.
*
*@@added V0.9.7 (2001-01-15) [umoeller]
*/
VOID xstrConvertLineFormat(PXSTRING pxstr,
BOOL fToCFormat) // in: if CRLF2LF, to C format; if LF2CRLF, to OS/2 format.
{
XSTRING strFind,
strRepl;
size_t ShiftTable[256];
BOOL fRepeat = FALSE;
ULONG ulOfs = 0;
if (fToCFormat)
{
// OS/2 to C:
xstrInitSet(&strFind, "\r\n");
xstrInitSet(&strRepl, "\n");
}
else
{
// C to OS/2:
xstrInitSet(&strFind, "\n");
xstrInitSet(&strRepl, "\r\n");
}
while (xstrFindReplace(pxstr,
&ulOfs,
&strFind,
&strRepl,
ShiftTable,
&fRepeat))
;
}
/*
*@@ xstrPrintf:
* like sprintf, but prints into an XSTRING
* bufer (which must be initialized).
*
* Note that the internal stack buffer is
* limited to 2000 bytes, so watch out.
*
*@@added V0.9.19 (2002-03-28) [umoeller]
*/
VOID xstrPrintf(XSTRING *pstr, // in/out: string buffer (must be init'ed)
PCSZ pcszFormat, // in: format string (like with printf)
...) // in: additional stuff (like with printf)
{
va_list args;
CHAR szBuf[2000];
va_start(args, pcszFormat);
vsprintf(szBuf, pcszFormat, args);
va_end(args);
xstrcpy(pstr, szBuf, 0);
}
/*
*@@ xstrCatf:
* like xstrPrintf, but appends to the
* given XSTRING.
*
*@@added V0.9.19 (2002-04-14) [umoeller]
*/
VOID xstrCatf(XSTRING *pstr, // in/out: string buffer (must be init'ed)
PCSZ pcszFormat, // in: format string (like with printf)
...) // in: additional stuff (like with printf)
{
va_list args;
CHAR szBuf[2000];
va_start(args, pcszFormat);
vsprintf(szBuf, pcszFormat, args);
va_end(args);
xstrcat(pstr, szBuf, 0);
}
// test case
/* int main(void)
{
XSTRING str,
strFind,
strReplace;
size_t shift[256];
BOOL fRepeat = FALSE;
ULONG ulOfs = 0;
xstrInit(&str, 0);
xstrInit(&strFind, 0);
xstrInit(&strReplace, 0);
str.ulDelta = 50;
xstrcpy(&str, "Test string 1. Test string 2. Test string 3. !", 0);
xstrcpy(&strFind, "Test", 0);
xstrcpy(&strReplace, "Dummy", 0);
printf("Old string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
fRepeat = FALSE;
ulOfs = 0;
while (xstrFindReplace(&str,
&ulOfs,
&strFind,
&strReplace,
shift, &fRepeat));
;
printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
printf("Appending \"blah\".\n");
xstrcat(&str, "blah", 0);
printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
xstrcpy(&strFind, strReplace.psz, 0);
xstrClear(&strReplace);
printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
fRepeat = FALSE;
ulOfs = 0;
while (xstrFindReplace(&str,
&ulOfs,
&strFind,
&strReplace,
shift, &fRepeat));
;
printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
xstrcpy(&strFind, " ", 0);
xstrcpy(&strReplace, ".", 0);
printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
fRepeat = FALSE;
ulOfs = 0;
while (xstrFindReplace(&str,
&ulOfs,
&strFind,
&strReplace,
shift, &fRepeat));
;
printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
xstrcpy(&strFind, ".", 0);
xstrcpy(&strReplace, "*.........................*", 0);
printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
fRepeat = FALSE;
ulOfs = 0;
while (xstrFindReplace(&str,
&ulOfs,
&strFind,
&strReplace,
shift, &fRepeat));
;
printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
xstrcpy(&strFind, "..........", 0);
xstrcpy(&strReplace, "@", 0);
printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
fRepeat = FALSE;
ulOfs = 0;
while (xstrFindReplace(&str,
&ulOfs,
&strFind,
&strReplace,
shift, &fRepeat));
;
printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
printf("Encoding @* chars.\n");
xstrEncode(&str, "@*");
printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
printf("Decoding @* chars.\n");
xstrDecode(&str);
printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
return 0;
} */