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   
C/C++ Source or Header  |  2002-07-08  |  57KB  |  1,694 lines

  1.  
  2. /*
  3.  *@@sourcefile xstring.c:
  4.  *      string functions with memory management.
  5.  *
  6.  *      Usage: All OS/2 programs.
  7.  *
  8.  *      The functions in this file are intended as a replacement
  9.  *      to the C library string functions (such as strcpy, strcat)
  10.  *      in cases where the length of the string is unknown and
  11.  *      dynamic memory management is desirable.
  12.  *
  13.  *      Instead of char* pointers, the functions in this file
  14.  *      operate on XSTRING structures, which in turn contain
  15.  *      a char* pointer pointing to heap memory, which is managed
  16.  *      automatically.
  17.  *
  18.  *      Besides being convenient, these functions are highly
  19.  *      optimized to use as few strlen's and memcpy's as
  20.  *      possible.
  21.  *
  22.  *      Using these functions has the following advantages:
  23.  *
  24.  *      -- Automatic memory management. For example, xstrcat will
  25.  *         automatically allocate new memory if the new string
  26.  *         does not fit into the present buffer.
  27.  *
  28.  *      -- The length of the string is always known. Instead
  29.  *         of running strlen (which consumes time), XSTRING.ulLength
  30.  *         always contains the current length of the string.
  31.  *
  32.  *      -- The functions also differentiate between allocated
  33.  *         memory and the length of the string. That is, for
  34.  *         iterative appends, you can pre-allocate memory to
  35.  *         avoid excessive reallocations.
  36.  *
  37.  *      These functions are also used internally by the
  38.  *      WarpIN BSString class (and related classes).
  39.  *
  40.  *      Usage:
  41.  *
  42.  *      1) Allocate an XSTRING structure on the stack. Always
  43.  *         call xstrInit on the structure, like this:
  44.  *
  45.  +              XSTRING str;
  46.  +              xstrInit(&str, 0);      // no pre-allocation
  47.  *
  48.  *         Alternatively, use xstrCreate to have an XSTRING
  49.  *         allocated from the heap.
  50.  *
  51.  *         Always call xstrClear(&str) to free allocated
  52.  *         memory. Otherwise you'll get memory leaks.
  53.  *         (For heap XSTRING's from xstrCreate, use xstrFree.)
  54.  *
  55.  *      2) To copy something into the string, use xstrcpy.
  56.  *         To append something to the string, use xstrcat.
  57.  *         See those functions for samples.
  58.  *
  59.  *      3) If you need the char* pointer (e.g. for a call
  60.  *         to another function), use XSTRING.psz. However,
  61.  *         you should ONLY modify the psz pointer yourself
  62.  *         if the other XSTRING members are updated accordingly.
  63.  *         You may, for example, change single characters
  64.  *         in the psz buffer. By contrast, if you change the
  65.  *         length of the string, you must update XSTRING.ulLength.
  66.  *         Otherwise these functions will get into trouble.
  67.  *
  68.  *         Also, you should never assume that the "psz"
  69.  *         pointer has not changed after you have called
  70.  *         one of the xstr* functions because these can
  71.  *         always reallocate the buffer if more memory
  72.  *         was needed.
  73.  *
  74.  *      4) If (and only if) you have a char* buffer which
  75.  *         is free()'able (e.g. from strdup()), you can
  76.  *         use xstrset to avoid duplicate copying.
  77.  *
  78.  *      Function prefixes:
  79.  *      --  xstr*       extended string functions.
  80.  *
  81.  *      The functions in this file used to be in stringh.c
  82.  *      before V0.9.3 (2000-04-01). These have been largely
  83.  *      rewritten with V0.9.6 (2000-11-01) and are now much
  84.  *      more efficient.
  85.  *
  86.  *      Note: Version numbering in this file relates to XWorkplace
  87.  *            version numbering.
  88.  *
  89.  *@@added V0.9.3 (2000-04-01) [umoeller]
  90.  *@@header "helpers\xstring.h"
  91.  */
  92.  
  93. /*
  94.  *      Copyright (C) 1999-2002 Ulrich Möller.
  95.  *      This file is part of the "XWorkplace helpers" source package.
  96.  *      This is free software; you can redistribute it and/or modify
  97.  *      it under the terms of the GNU General Public License as published
  98.  *      by the Free Software Foundation, in version 2 as it comes in the
  99.  *      "COPYING" file of the XWorkplace main distribution.
  100.  *      This program is distributed in the hope that it will be useful,
  101.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  102.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  103.  *      GNU General Public License for more details.
  104.  */
  105.  
  106. #define OS2EMX_PLAIN_CHAR
  107.     // this is needed for "os2emx.h"; if this is defined,
  108.     // emx will define PSZ as _signed_ char, otherwise
  109.     // as unsigned char
  110.  
  111. #include <os2.h>
  112.  
  113. #include <stdlib.h>
  114. #include <stdio.h>
  115. #include <stdarg.h>
  116. #include <string.h>
  117.  
  118. #include "setup.h"                      // code generation and debugging options
  119.  
  120. #include "helpers\stringh.h"
  121. #define DONT_REPLACE_XSTR_MALLOC
  122. #include "helpers\xstring.h"            // extended string helpers
  123.  
  124. /*
  125.  *@@category: Helpers\C helpers\String management\XStrings (with memory management)
  126.  *      See xstring.c.
  127.  */
  128.  
  129. #ifdef __DEBUG_MALLOC_ENABLED__
  130.  
  131. /*
  132.  *@@ xstrInitDebug:
  133.  *
  134.  *@@added V0.9.14 (2001-08-01) [umoeller]
  135.  */
  136.  
  137. void XWPENTRY xstrInitDebug(PXSTRING pxstr,
  138.                             ULONG ulPreAllocate,
  139.                             PCSZ file,
  140.                             unsigned long line,
  141.                             PCSZ function)
  142. {
  143.     memset(pxstr, 0, sizeof(XSTRING));
  144.  
  145.     if (ulPreAllocate)
  146.     {
  147.         pxstr->psz = (PSZ)memdMalloc(ulPreAllocate,
  148.                                      file,
  149.                                      line,
  150.                                      function);
  151.         pxstr->cbAllocated = ulPreAllocate;
  152.                 // ulLength is still zero
  153.         *(pxstr->psz) = 0;
  154.  
  155.         pxstr->ulDelta = ulPreAllocate * 10 / 100;
  156.     }
  157.  
  158.     // else: pxstr->ulDelta is still 0
  159.     pxstr->file = file;
  160.     pxstr->line = line;
  161.     pxstr->function = function;
  162. }
  163.  
  164. #endif // __DEBUG_MALLOC_ENABLED__
  165.  
  166. /*
  167.  *@@ xstrInit:
  168.  *      initializes a new XSTRING. Always call this before
  169.  *      using an XSTRING from the stack.
  170.  *
  171.  *      If (ulPreAllocate != 0), memory is pre-allocated
  172.  *      for the string, but the string will be empty
  173.  *      (its first byte is set to '\0'). In addition,
  174.  *      pxstr->ulDelta will be set to 10% of ulPreAllocate.
  175.  *
  176.  *      This is useful if you plan to add more stuff to
  177.  *      the string later so we don't have to reallocate
  178.  *      all the time in xstrcat.
  179.  *
  180.  *      Do not use this on an XSTRING which is already
  181.  *      initialized (this would cause memory leaks).
  182.  *      Use xstrcpy or xstrset instead.
  183.  *
  184.  *@@added V0.9.6 (2000-11-01) [umoeller]
  185.  *@@changed V0.9.9 (2001-03-09) [umoeller]: added ulDelta
  186.  */
  187.  
  188. void xstrInit(PXSTRING pxstr,               // in/out: string
  189.               ULONG ulPreAllocate)          // in: if > 0, memory to allocate
  190. {
  191.     memset(pxstr, 0, sizeof(XSTRING));
  192.  
  193.     if (ulPreAllocate)
  194.     {
  195.         pxstr->psz = (PSZ)malloc(ulPreAllocate);
  196.         pxstr->cbAllocated = ulPreAllocate;
  197.                 // ulLength is still zero
  198.         *(pxstr->psz) = 0;
  199.  
  200.         pxstr->ulDelta = ulPreAllocate * 10 / 100;
  201.     }
  202.     // else: pxstr->ulDelta is still 0
  203. }
  204.  
  205. /*
  206.  *@@ xstrInitSet2:
  207.  *      this can be used instead of xstrInit if you
  208.  *      have a free()'able string you want to initialize
  209.  *      the XSTRING with.
  210.  *
  211.  *      This does not create a copy of pszNew. Instead,
  212.  *      pszNew is used as the member string in pxstr
  213.  *      directly.
  214.  *
  215.  *      Do not use this on an XSTRING which is already
  216.  *      initialized (this would cause memory leaks).
  217.  *      Use xstrcpy or xstrset instead.
  218.  *
  219.  *      Example:
  220.  *
  221.  +          XSTRING str;
  222.  +          xstrInitSet(&str, strdup("blah"), 0);
  223.  *
  224.  *@@added V0.9.16 (2002-01-13) [umoeller]
  225.  */
  226.  
  227. void xstrInitSet2(PXSTRING pxstr,           // in/out: string
  228.                   PSZ pszNew,               // in: malloc'd string to load pxstr with
  229.                   ULONG ulNewLength)        // in: length of pszNew or 0 to run strlen()
  230. {
  231.     if (!pszNew)
  232.         memset(pxstr, 0, sizeof(XSTRING));
  233.     else
  234.     {
  235.         if (!ulNewLength)
  236.             ulNewLength = strlen(pszNew);
  237.         pxstr->psz = pszNew;
  238.         pxstr->ulLength = ulNewLength;
  239.         pxstr->cbAllocated = ulNewLength + 1;
  240.         pxstr->ulDelta = ulNewLength * 10 / 100;
  241.     }
  242. }
  243.  
  244. /*
  245.  *@@ xstrInitSet:
  246.  *      shortcut to xstrInitSet2 to retain compatibility.
  247.  *
  248.  *@@added V0.9.6 (2000-11-01) [umoeller]
  249.  *@@changed V0.9.9 (2001-03-09) [umoeller]: added ulDelta
  250.  */
  251.  
  252. void xstrInitSet(PXSTRING pxstr,
  253.                  PSZ pszNew)
  254. {
  255.     xstrInitSet2(pxstr, pszNew, 0);
  256. }
  257.  
  258. #ifdef __DEBUG_MALLOC_ENABLED__
  259.  
  260. /*
  261.  *@@ xstrInitCopyDebug:
  262.  *
  263.  *@@added V0.9.16 (2002-01-05) [umoeller]
  264.  */
  265.  
  266. void XWPENTRY xstrInitCopyDebug(PXSTRING pxstr,
  267.                                 PCSZ pcszSource,
  268.                                 ULONG ulExtraAllocate,
  269.                                 PCSZ file,
  270.                                 unsigned long line,
  271.                                 PCSZ function)
  272. {
  273.     if (pxstr)
  274.     {
  275.         memset(pxstr, 0, sizeof(XSTRING));
  276.  
  277.         if (pcszSource)
  278.         {
  279.             pxstr->ulLength = strlen(pcszSource);
  280.  
  281.             if (pxstr->ulLength)
  282.             {
  283.                 // we do have a source string:
  284.                 pxstr->cbAllocated = pxstr->ulLength + 1 + ulExtraAllocate;
  285.                 pxstr->psz = (PSZ)memdMalloc(pxstr->cbAllocated,
  286.                                              file,
  287.                                              line,
  288.                                              function);
  289.                 // V0.9.16 (2002-01-05) [umoeller]
  290.                 memcpy(pxstr->psz,
  291.                        pcszSource,
  292.                        pxstr->ulLength);
  293.                 pxstr->psz[pxstr->ulLength] = '\0';
  294.  
  295.                 pxstr->ulDelta = pxstr->cbAllocated * 10 / 100;
  296.             }
  297.         }
  298.     }
  299. }
  300.  
  301. #endif
  302.  
  303. /*
  304.  *@@ xstrInitCopy:
  305.  *      this can be used instead of xstrInit if you
  306.  *      want to initialize an XSTRING with a copy
  307.  *      of an existing string. This is a shortcut
  308.  *      for xstrInit() and then xstrcpy().
  309.  *
  310.  *      As opposed to xstrInitSet, this does create
  311.  *      a copy of pcszSource.
  312.  *
  313.  *      Do not use this on an XSTRING which is already
  314.  *      initialized (this would cause memory leaks).
  315.  *      Use xstrcpy or xstrset instead.
  316.  *
  317.  *      Example:
  318.  *
  319.  +          XSTRING str;
  320.  +          xstrInitCopy(&str, "blah");
  321.  *
  322.  *@@added V0.9.6 (2000-11-01) [umoeller]
  323.  *@@changed V0.9.7 (2000-12-31) [umoeller]: added ulExtraAllocate
  324.  *@@changed V0.9.9 (2001-03-09) [umoeller]: added ulDelta
  325.  *@@changed V0.9.16 (2002-01-05) [umoeller]: use memcpy instead of strcpy
  326.  */
  327.  
  328. void xstrInitCopy(PXSTRING pxstr,
  329.                   PCSZ pcszSource,
  330.                   ULONG ulExtraAllocate)          // in: if > 0, extra memory to allocate
  331. {
  332.     if (pxstr)
  333.     {
  334.         memset(pxstr, 0, sizeof(XSTRING));
  335.  
  336.         if (pcszSource)
  337.         {
  338.             if (pxstr->ulLength = strlen(pcszSource))
  339.             {
  340.                 // we do have a source string:
  341.                 pxstr->cbAllocated = pxstr->ulLength + 1 + ulExtraAllocate;
  342.                 pxstr->psz = (PSZ)malloc(pxstr->cbAllocated);
  343.                 // V0.9.16 (2002-01-05) [umoeller]
  344.                 memcpy(pxstr->psz,
  345.                        pcszSource,
  346.                        pxstr->ulLength);
  347.                 pxstr->psz[pxstr->ulLength] = '\0';
  348.  
  349.                 pxstr->ulDelta = pxstr->cbAllocated * 10 / 100;
  350.             }
  351.         }
  352.     }
  353. }
  354.  
  355. /*
  356.  *@@ xstrClear:
  357.  *      clears the specified stack XSTRING and
  358.  *      frees allocated memory.
  359.  *
  360.  *      This is the reverse to xstrInit.
  361.  *
  362.  *@@added V0.9.6 (2000-11-01) [umoeller]
  363.  */
  364.  
  365. void xstrClear(PXSTRING pxstr)              // in/out: string
  366. {
  367.     if (pxstr->psz)
  368.         free(pxstr->psz);
  369.  
  370.     memset(pxstr, 0, sizeof(XSTRING));
  371. }
  372.  
  373. /*
  374.  *@@ xstrReserve:
  375.  *      this function makes sure that the specified
  376.  *      XSTRING has at least ulBytes bytes allocated.
  377.  *
  378.  *      This function is useful if you plan to do
  379.  *      a lot of string replacements or appends and
  380.  *      want to avoid that the buffer is reallocated
  381.  *      with each operation. Before those operations,
  382.  *      call this function to make room for the operations.
  383.  *
  384.  *      If ulBytes is smaller than the current allocation,
  385.  *      this function does nothing.
  386.  *
  387.  *      pxstr->ulDelta has no effect here.
  388.  *
  389.  *      The XSTRING must be initialized before the
  390.  *      call.
  391.  *
  392.  *      Returns the new total no. of allocated bytes.
  393.  *
  394.  *@@added V0.9.7 (2001-01-07) [umoeller]
  395.  *@@changed V0.9.9 (2001-03-09) [umoeller]: now using ulDelta
  396.  *@@changed V0.9.12 (2001-05-21) [umoeller]: now reporting error on realloc fail
  397.  */
  398.  
  399. ULONG xstrReserve(PXSTRING pxstr,
  400.                   ULONG ulBytes)
  401. {
  402.     ULONG   cbNeeded = ulBytes;
  403.  
  404.     if (cbNeeded > pxstr->cbAllocated)
  405.     {
  406.         // we need more memory than we have previously
  407.         // allocated:
  408.         ULONG cbAllocate;
  409.         if (pxstr->ulDelta)
  410.         {
  411.             // delta specified: allocate in chunks of that
  412.             // V0.9.9 (2001-03-07) [umoeller]
  413.             ULONG cbExtra = cbNeeded - pxstr->cbAllocated;
  414.             cbExtra = (   (cbExtra + pxstr->ulDelta)
  415.                         / pxstr->ulDelta
  416.                       )
  417.                       * pxstr->ulDelta;
  418.                     // if we need 3 extra bytes and ulDelta is 10,
  419.                     // this gives us 10 extra bytes
  420.                     // if we need 3 extra bytes and ulDelta is 1000,
  421.                     // this gives us 1000 extra bytes
  422.             cbAllocate = pxstr->cbAllocated + cbExtra;
  423.         }
  424.         else
  425.             // no delta specified:
  426.             cbAllocate = cbNeeded;
  427.         // V0.9.9 (2001-03-05) [umoeller]: use realloc;
  428.         // this gives the C runtime a chance to expand the
  429.         // existing block
  430. #ifdef __DEBUG_MALLOC_ENABLED__
  431.         if (pxstr->psz = (PSZ)memdRealloc(pxstr->psz,
  432.                                           cbAllocate,
  433.                                           pxstr->file,
  434.                                           pxstr->line,
  435.                                           pxstr->function))
  436. #else
  437.         if (pxstr->psz = (PSZ)realloc(pxstr->psz,
  438.                                       cbAllocate))
  439. #endif
  440.                     // if pxstr->psz is NULL, realloc behaves like malloc
  441.             pxstr->cbAllocated = cbAllocate;
  442.                 // ulLength is unchanged
  443.         else
  444.             // error: V0.9.12 (2001-05-21) [umoeller]
  445.             pxstr->cbAllocated = 0;
  446.     }
  447.     // else: we have enough memory
  448.  
  449.     return pxstr->cbAllocated;
  450. }
  451.  
  452. /*
  453.  *@@ xstrShrink:
  454.  *      reallocates the string buffer so that it
  455.  *      is exactly the length of the string with
  456.  *      its null byte, if the string has excessive
  457.  *      memory allocated. Useful if you are sure
  458.  *      that the string won't grow again.
  459.  *
  460.  *@@added V0.9.16 (2001-10-08) [umoeller]
  461.  */
  462.  
  463. void XWPENTRY xstrShrink(PXSTRING pxstr)
  464. {
  465.     if (    (pxstr)
  466.          && (pxstr->psz)
  467.          && (pxstr->cbAllocated > pxstr->ulLength + 1)
  468.        )
  469.     {
  470.         pxstr->psz = (PSZ)realloc(pxstr->psz,
  471.                                   pxstr->ulLength + 1);
  472.         pxstr->cbAllocated = pxstr->ulLength + 1;
  473.     }
  474. }
  475.  
  476. /*
  477.  *@@ xstrCreate:
  478.  *      allocates a new XSTRING from the heap
  479.  *      and calls xstrInit on it.
  480.  *
  481.  *      Always use xstrFree to free associated
  482.  *      resources.
  483.  *
  484.  *@@added V0.9.6 (2000-11-01) [umoeller]
  485.  */
  486.  
  487. PXSTRING xstrCreate(ULONG ulPreAllocate)
  488. {
  489.     PXSTRING pxstr;
  490.     if (pxstr = (PXSTRING)malloc(sizeof(XSTRING)))
  491.         xstrInit(pxstr, ulPreAllocate);
  492.  
  493.     return pxstr;
  494. }
  495.  
  496. /*
  497.  *@@ xstrFree:
  498.  *      frees the specified heap XSTRING, which must
  499.  *      have been created using xstrCreate.
  500.  *
  501.  *      This uses a pointer to a PXSTRING so that
  502.  *      the pointer is automatically reset to NULL
  503.  *      by this function AND to avoid confusion
  504.  *      with xstrClear.
  505.  *
  506.  *@@added V0.9.6 (2000-11-01) [umoeller]
  507.  *@@changed V0.9.12 (2001-05-24) [umoeller]: changed prototype to use pointer to pointer
  508.  */
  509.  
  510. VOID xstrFree(PXSTRING *ppxstr)               // in/out: string
  511. {
  512.     PXSTRING p;
  513.     if (    ppxstr
  514.          && (p = *ppxstr)
  515.        )
  516.     {
  517.         xstrClear(p);
  518.         free(p);
  519.         *ppxstr = NULL;
  520.     }
  521. }
  522.  
  523. /*
  524.  *@@ xstrset2:
  525.  *      sets the specified XSTRING to a new string
  526.  *      without copying it.
  527.  *
  528.  *      pxstr is cleared before the new string is set.
  529.  *
  530.  *      This ONLY works if pszNew has been allocated from
  531.  *      the heap using malloc() or strdup() and is thus
  532.  *      free()'able.
  533.  *
  534.  *      This assumes that exactly strlen(pszNew) + 1
  535.  *      bytes have been allocated for pszNew, which
  536.  *      is true if pszNew comes from strdup().
  537.  *
  538.  *      With this function, you can pass in the
  539.  *      length of the string in ulNewLength.
  540.  *      Otherwise use xstrset.
  541.  *
  542.  *@@added V0.9.16 (2002-01-13) [umoeller]
  543.  */
  544.  
  545. ULONG xstrset2(PXSTRING pxstr,              // in/out: string
  546.                PSZ pszNew,                  // in: heap PSZ to use
  547.                ULONG ulNewLength)           // in: length of string or 0 to run strlen here
  548. {
  549.     if (!pxstr)
  550.         return 0;         // V0.9.9 (2001-02-14) [umoeller]
  551.  
  552.     xstrClear(pxstr);
  553.     if (pxstr->psz = pszNew)
  554.     {
  555.         if (!ulNewLength)
  556.             ulNewLength = strlen(pszNew);
  557.         pxstr->ulLength = ulNewLength;
  558.         pxstr->cbAllocated = ulNewLength + 1;
  559.  
  560.         pxstr->ulDelta = ulNewLength * 10 / 100;
  561.     }
  562.     // else null string: cbAllocated and ulLength are 0 already
  563.  
  564.     return pxstr->ulLength;
  565. }
  566.  
  567. /*
  568.  *@@ xstrset:
  569.  *      shortcut for xstrset2 for retaining compatibility.
  570.  *
  571.  *@@added V0.9.6 (2000-11-01) [umoeller]
  572.  *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
  573.  */
  574.  
  575. ULONG xstrset(PXSTRING pxstr,               // in/out: string
  576.               PSZ pszNew)                   // in: heap PSZ to use
  577. {
  578.     return xstrset2(pxstr, pszNew, 0);
  579. }
  580.  
  581. /*
  582.  *@@ xstrcpy:
  583.  *      copies pcszSource to pxstr, for which memory is allocated
  584.  *      as necessary.
  585.  *
  586.  *      If pxstr contains something, its contents are overwritten.
  587.  *
  588.  *      With ulSourceLength, specify the length of pcszSource
  589.  *      or 0.
  590.  *
  591.  *      --  If you specify 0, this function will run
  592.  *          strlen(pcszSource) and copy the entire source
  593.  *          string.
  594.  *
  595.  *      --  If you already know the length of pcszSource, you
  596.  *          can speed this function up by specifying the
  597.  *          length.
  598.  *
  599.  *      --  You are required to specify ulSourceLength if you
  600.  *          only want to copy a substring, or if pcszSource is
  601.  *          not zero-terminated.
  602.  *
  603.  *      Returns the length of the new string (excluding the null
  604.  *      terminator), or null upon errors.
  605.  *
  606.  *      Example:
  607.  *
  608.  +          XSTRING str;
  609.  +          xstrInit(&str, 0);
  610.  +          xstrcpy(&str, "blah", 0);
  611.  *
  612.  *      This sequence can be abbreviated using xstrInitCopy.
  613.  *
  614.  *      Memory cost: If there's enough room in pxstr for
  615.  *      pcszSource, none. Otherwise pxstr is reallocated
  616.  *      to hold enough room for pcszSource.
  617.  *
  618.  *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcpy
  619.  *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
  620.  *@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
  621.  *@@changed V0.9.9 (2001-01-28) [lafaix]: fixed memory leak and NULL source behavior
  622.  *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
  623.  *@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
  624.  *@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
  625.  *@@changed V0.9.12 (2001-05-21) [umoeller]: added xstrReserve error checking
  626.  */
  627.  
  628. ULONG xstrcpy(PXSTRING pxstr,               // in/out: string
  629.               PCSZ pcszSource,              // in: source, can be NULL
  630.               ULONG ulSourceLength)         // in: length of pcszSource or 0
  631. {
  632.     if (!pxstr)
  633.         return 0;         // V0.9.9 (2001-02-14) [umoeller]
  634.  
  635.     if (pcszSource)
  636.     {
  637.         // source specified:
  638.         if (ulSourceLength == 0)
  639.             // but not length:
  640.             ulSourceLength = strlen(pcszSource);
  641.     }
  642.     else
  643.         ulSourceLength = 0;
  644.  
  645.     if (ulSourceLength)
  646.     {
  647.         // we do have a source string:
  648.         if (xstrReserve(pxstr,
  649.                         // required memory:
  650.                         ulSourceLength + 1))
  651.         {
  652.             memcpy(pxstr->psz,
  653.                    pcszSource,
  654.                    ulSourceLength);
  655.             pxstr->psz[ulSourceLength] = '\0';
  656.                     // V0.9.9 (2001-02-16) [umoeller]
  657.                     // we must do this or otherwise we require pcszSource
  658.                     // to be zero-terminated... not a good idea
  659.         }
  660.         else
  661.             pxstr->ulLength = 0;        // error V0.9.12 (2001-05-21) [umoeller]
  662.     }
  663.     else
  664.     {
  665.         // no source specified or source is empty:
  666.         if (pxstr->cbAllocated)
  667.             // we did have a string: set to empty,
  668.             // but leave allocated memory intact
  669.             *(pxstr->psz) = 0;
  670.         // else
  671.             // we had no string previously: in that case
  672.             // psz and ulLength and cbAllocated are all still NULL
  673.     }
  674.  
  675.     // in all cases, set new length
  676.     pxstr->ulLength = ulSourceLength;
  677.  
  678.     return pxstr->ulLength;
  679. }
  680.  
  681. /*
  682.  *@@ xstrcpys:
  683.  *      shortcut to xstrcpy if the source is an XSTRING also.
  684.  *
  685.  *@@added V0.9.9 (2001-02-14) [umoeller]
  686.  */
  687.  
  688. ULONG xstrcpys(PXSTRING pxstr,
  689.                const XSTRING *pcstrSource)
  690. {
  691.     if (!pcstrSource)
  692.         return 0;
  693.  
  694.     return xstrcpy(pxstr, pcstrSource->psz, pcstrSource->ulLength);
  695. }
  696.  
  697. /*
  698.  *@@ xstrcat:
  699.  *      appends pcszSource to pxstr, for which memory is
  700.  *      reallocated if necessary.
  701.  *
  702.  *      If pxstr is empty, this behaves just like xstrcpy.
  703.  *
  704.  *      With ulSourceLength, specify the length of pcszSource
  705.  *      or 0 (see xstrcpy for details).
  706.  *
  707.  *      Returns the length of the new string (excluding the null
  708.  *      terminator) if the string was changed, or 0 if nothing
  709.  *      happened.
  710.  *
  711.  *      Note: To append a single character, xstrcatc is faster
  712.  *      than xstrcat.
  713.  *
  714.  *      Example:
  715.  *
  716.  +          XSTRING str;
  717.  +          xstrInit(&str, 0);
  718.  +          xstrcpy(&str, "blah", 0);
  719.  +          xstrcat(&str, "blup", 0);
  720.  *
  721.  *      After this, str.psz points to a new string containing
  722.  *      "blahblup".
  723.  *
  724.  *      Memory cost: If there's enough room in pxstr for
  725.  *      pcszSource, none. Otherwise pxstr is reallocated
  726.  *      to hold enough room for pcszSource.
  727.  *
  728.  *@@changed V0.9.1 (99-12-20) [umoeller]: fixed memory leak
  729.  *@@changed V0.9.1 (2000-01-03) [umoeller]: crashed if pszString was null; fixed
  730.  *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcat
  731.  *@@changed V0.9.3 (2000-05-11) [umoeller]: returned 0 if pszString was initially empty; fixed
  732.  *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
  733.  *@@changed V0.9.7 (2000-12-10) [umoeller]: return value was wrong
  734.  *@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
  735.  *@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
  736.  *@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
  737.  */
  738.  
  739. ULONG xstrcat(PXSTRING pxstr,               // in/out: string
  740.               PCSZ pcszSource,       // in: source, can be NULL
  741.               ULONG ulSourceLength)         // in: length of pcszSource or 0
  742. {
  743.     ULONG   ulrc = 0;
  744.  
  745.     if (pxstr)
  746.     {
  747.         if (pcszSource)
  748.         {
  749.             if (ulSourceLength == 0)
  750.                 ulSourceLength = strlen(pcszSource);
  751.  
  752.             if (ulSourceLength)
  753.             {
  754.                 // we do have a source string:
  755.  
  756.                 // 1) memory management
  757.                 xstrReserve(pxstr,
  758.                             // required memory:
  759.                             pxstr->ulLength + ulSourceLength + 1);
  760.  
  761.                 // 2) append source string:
  762.                 memcpy(pxstr->psz + pxstr->ulLength,
  763.                        pcszSource,
  764.                        ulSourceLength);
  765.  
  766.                 pxstr->psz[pxstr->ulLength + ulSourceLength] = '\0';
  767.                         // V0.9.9 (2001-02-16) [umoeller]
  768.                         // we must do this or otherwise we require pcszSource
  769.                         // to be zero-terminated... not a good idea
  770.  
  771.                 // in all cases, set new length
  772.                 pxstr->ulLength += ulSourceLength;
  773.                 ulrc = pxstr->ulLength;     // V0.9.7 (2000-12-10) [umoeller]
  774.  
  775.             } // end if (ulSourceLength)
  776.         }
  777.  
  778.         // else no source specified or source is empty:
  779.         // do nothing
  780.     }
  781.  
  782.     return ulrc;
  783. }
  784.  
  785. /*
  786.  *@@ xstrcatc:
  787.  *      this is similar to xstrcat, except that this is
  788.  *      for a single character. This is a bit faster than
  789.  *      xstrcat.
  790.  *
  791.  *      If "c" is \0, nothing happens.
  792.  *
  793.  *      If pxstr is empty, this behaves just like xstrcpy.
  794.  *
  795.  *      Returns the length of the new string (excluding the null
  796.  *      terminator) if the string was changed, or 0 if nothing
  797.  *      happened.
  798.  *
  799.  *      Example:
  800.  *
  801.  +          XSTRING str;
  802.  +          xstrInit(&str, 0);
  803.  +          xstrcpy(&str, "blu", 0);
  804.  +          xstrcatc(&str, 'p');
  805.  *
  806.  *      After this, str.psz points to a new string containing
  807.  *      "blup".
  808.  *
  809.  *      Memory cost: If there's enough room in pxstr for
  810.  *      c, none. Otherwise pxstr is reallocated
  811.  *      to hold enough room for c.
  812.  *
  813.  *@@added V0.9.7 (2000-12-10) [umoeller]
  814.  *@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
  815.  */
  816.  
  817. ULONG xstrcatc(PXSTRING pxstr,     // in/out: string
  818.                CHAR c)             // in: character to append, can be \0
  819. {
  820.     ULONG   ulrc = 0;
  821.  
  822.     if ((pxstr) && (c))
  823.     {
  824.         // 1) memory management
  825.         xstrReserve(pxstr,
  826.                     // required memory:
  827.                     pxstr->ulLength  // existing length, without null terminator
  828.                             + 1      // new character
  829.                             + 1);    // null terminator
  830.         // 2) append character:
  831.         pxstr->psz[pxstr->ulLength] = c;
  832.         pxstr->psz[pxstr->ulLength + 1] = '\0';
  833.  
  834.         // in all cases, set new length
  835.         (pxstr->ulLength)++;
  836.         ulrc = pxstr->ulLength;
  837.  
  838.     } // end if ((pxstr) && (c))
  839.  
  840.     return ulrc;
  841. }
  842.  
  843. /*
  844.  *@@ xstrcats:
  845.  *      shortcut to xstrcat if the source is an XSTRING also.
  846.  *
  847.  *@@added V0.9.9 (2001-02-14) [umoeller]
  848.  */
  849.  
  850. ULONG xstrcats(PXSTRING pxstr,
  851.                const XSTRING *pcstrSource)
  852. {
  853.     if (!pcstrSource)
  854.         return 0;
  855.  
  856.     return xstrcat(pxstr,
  857.                    pcstrSource->psz,
  858.                    pcstrSource->ulLength);
  859. }
  860.  
  861. /*
  862.  *@@ xstrrpl:
  863.  *      replaces "cReplLen" characters in pxstr, starting
  864.  *      at the position "ulFirstReplPos", with the first
  865.  *      "cReplaceWithLen" characters from pcszReplaceWith.
  866.  *
  867.  *      If cReplaceWithLen is 0, characters are removed only.
  868.  *
  869.  *      Returns the new length of the string, excluding
  870.  *      the null terminator, or 0 if the replacement failed
  871.  *      (e.g. because the offsets were too large).
  872.  *
  873.  *      This has been extracted from xstrFindReplace because
  874.  *      if you already know the position of a substring,
  875.  *      you can now call this directly. This properly
  876.  *      reallocates the string if more memory is needed.
  877.  *
  878.  *      Example:
  879.  *
  880.  +          XSTRING xstr, xstrReplacement;
  881.  +          xstrInitCopy(&xstr, "This is a test string.");
  882.  +          //  positions:       0123456789012345678901
  883.  +          //                             1         2
  884.  +
  885.  +          xstrrpl(&xstr,
  886.  +                  10,     // position of "test"
  887.  +                  4,      // length of "test"
  888.  +                  "stupid",
  889.  +                  6);     // strlen("stupid")
  890.  *
  891.  *      This would yield "This is a stupid string."
  892.  *
  893.  *      Memory cost: If there's enough room in pxstr for
  894.  *      the replacement, none. Otherwise pxstr is reallocated
  895.  *      to hold enough room for the replacement.
  896.  *
  897.  *@@added V0.9.7 (2001-01-15) [umoeller]
  898.  *@@changed V0.9.9 (2001-01-29) [lafaix]: fixed unnecessary allocation when pxstr was big enough
  899.  *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
  900.  *@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
  901.  *@@changed V0.9.11 (2001-04-22) [umoeller]: replaced replacement XSTRING with PCSZ
  902.  *@@changed V0.9.14 (2001-07-07) [umoeller]: this did nothing if cReplaceWithLen == 0, fixed
  903.  */
  904.  
  905. ULONG xstrrpl(PXSTRING pxstr,                   // in/out: string
  906.               ULONG ulFirstReplOfs,             // in: ofs of first char to replace
  907.               ULONG cReplLen,                   // in: no. of chars to replace
  908.               PCSZ pcszReplaceWith,             // in: string to replace chars with
  909.               ULONG cReplaceWithLen)            // in: length of replacement string
  910.                                                 // (this MUST be specified; if 0, chars are removed only)
  911. {
  912.     ULONG   ulrc = 0;
  913.  
  914.     // security checks...
  915.     if (    (pxstr)         // V0.9.9 (2001-02-14) [umoeller]
  916.          && (ulFirstReplOfs + cReplLen <= pxstr->ulLength)
  917.          && (    (pcszReplaceWith)
  918.               || (cReplaceWithLen == 0)     // fixed V0.9.14 (2001-07-07) [umoeller]
  919.             )
  920.        )
  921.     {
  922.         // size of new buffer:
  923.         ULONG   cbNeeded = pxstr->ulLength      // existing
  924.                          + cReplaceWithLen      // plus replacement string length
  925.                          - cReplLen             // minus replaced characters
  926.                          + 1;                   // plus null terminator
  927.         // offset where pszSearch was found
  928.         PSZ     pFound = pxstr->psz + ulFirstReplOfs;
  929.  
  930.         // now check if we have enough memory...
  931.         if (cbNeeded > pxstr->cbAllocated)
  932.         {
  933.             // we need more memory than we have previously
  934.             // allocated:
  935.             // reallocate using ulDelta V0.9.9 (2001-03-07) [umoeller]
  936.             ULONG cbAllocate;
  937.             PSZ pszNew;
  938.             if (pxstr->ulDelta)
  939.             {
  940.                 // delta specified: allocate in chunks of that
  941.                 // V0.9.9 (2001-03-07) [umoeller]
  942.                 ULONG cbExtra = cbNeeded - pxstr->cbAllocated;
  943.                 cbExtra = (   (cbExtra + pxstr->ulDelta)
  944.                             / pxstr->ulDelta
  945.                           )
  946.                           * pxstr->ulDelta;
  947.                         // if we need 3 extra bytes and ulDelta is 10,
  948.                         // this gives us 10 extra bytes
  949.                         // if we need 3 extra bytes and ulDelta is 1000,
  950.                         // this gives us 1000 extra bytes
  951.                 cbAllocate = pxstr->cbAllocated + cbExtra;
  952.             }
  953.             else
  954.                 // no delta specified:
  955.                 cbAllocate = cbNeeded;
  956.             // allocate new buffer
  957.             pszNew = (PSZ)malloc(cbAllocate);
  958.             // end V0.9.9 (2001-03-07) [umoeller]
  959.  
  960.             if (ulFirstReplOfs)
  961.                 // "found" was not at the beginning:
  962.                 // copy from beginning up to found-offset
  963.                 memcpy(pszNew,
  964.                        pxstr->psz,
  965.                        ulFirstReplOfs);     // up to "found"
  966.  
  967.             if (cReplaceWithLen)
  968.             {
  969.                 // we have a replacement:
  970.                 // insert it next...
  971.                 // we no longer can be sure that pcszReplaceWith
  972.                 // is null terminated, so terminate explicitly
  973.                 // V0.9.11 (2001-04-22) [umoeller]
  974.                 memcpy(pszNew + ulFirstReplOfs,
  975.                        pcszReplaceWith,
  976.                        cReplaceWithLen);
  977.                 *(pszNew + ulFirstReplOfs + cReplaceWithLen) = '\0';
  978.             }
  979.  
  980.             // copy rest:
  981.             // pxstr      frontFOUNDtail
  982.             //            0         1
  983.             //            01234567890123
  984.             //            │    │    │  │
  985.             //            │    │    └─ ulFirstReplOfs + cReplLen = 10
  986.             //            │    │       │
  987.             //            │    └─ ulFirstReplOfs = 5
  988.             //            │            │
  989.             //            pxstr->ulLength = 14
  990.             memcpy(pszNew + ulFirstReplOfs + cReplaceWithLen,
  991.                    pFound + cReplLen,
  992.                    // remaining bytes:
  993.                    pxstr->ulLength - ulFirstReplOfs - cReplLen // 9
  994.                         + 1); // null terminator
  995.  
  996.             // replace old buffer with new one
  997.             free(pxstr->psz);
  998.             pxstr->psz = pszNew;
  999.             pxstr->ulLength = cbNeeded - 1;
  1000.             pxstr->cbAllocated = cbAllocate; // V0.9.9 (2001-03-07) [umoeller]
  1001.         } // end if (pxstr->cbAllocated < cbNeeded)
  1002.         else
  1003.         {
  1004.             // we have enough memory left,
  1005.             // we can just overwrite in the middle...
  1006.             // fixed V0.9.9 (2001-01-29) [lafaix]
  1007.  
  1008.             // calc length of string after "found"
  1009.             ULONG   cTailLength = pxstr->ulLength - ulFirstReplOfs - cReplLen;
  1010.  
  1011.             // first, we move the end to its new location
  1012.             // (memmove handles overlap if needed)
  1013.             memmove(pFound + cReplaceWithLen,
  1014.                     pFound + cReplLen,
  1015.                     cTailLength + 1); // including null terminator
  1016.  
  1017.             // now overwrite "found" in the middle
  1018.             if (cReplaceWithLen)
  1019.                 memcpy(pFound,
  1020.                        pcszReplaceWith,
  1021.                        cReplaceWithLen);        // no null terminator
  1022.  
  1023.             // that's it; adjust the string length now
  1024.             pxstr->ulLength = cbNeeded - 1;
  1025.         }
  1026.  
  1027.         ulrc = cbNeeded - 1;
  1028.     } // end checks
  1029.  
  1030.     return ulrc;
  1031. }
  1032.  
  1033. /*
  1034.  *@@ xstrFindWord:
  1035.  *      searches for pstrFind in pxstr, starting at ulOfs.
  1036.  *      However, this only finds pstrFind if it's a "word",
  1037.  *      i.e. surrounded by one of the characters in the
  1038.  *      pcszBeginChars and pcszEndChars array.
  1039.  *
  1040.  *      This is similar to strhFindWord, but this uses
  1041.  *      strhmemfind for fast searching, and it doesn't
  1042.  *      have to calculate the string lengths because these
  1043.  *      already in XSTRING.
  1044.  *
  1045.  *      Returns 0 if no "word" was found, or the offset
  1046.  *      of the "word" in pxstr if found.
  1047.  *
  1048.  *@@added V0.9.6 (2000-11-12) [umoeller]
  1049.  *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL string crashs
  1050.  */
  1051.  
  1052. PSZ xstrFindWord(const XSTRING *pxstr,        // in: buffer to search ("haystack")
  1053.                  ULONG ulOfs,                 // in: where to begin search (0 = start)
  1054.                  const XSTRING *pstrFind,     // in: word to find ("needle")
  1055.                  size_t *pShiftTable,         // in: shift table (see strhmemfind)
  1056.                  PBOOL pfRepeatFind,          // in: repeat find? (see strhmemfind)
  1057.                  PCSZ pcszBeginChars,  // suggestion: "\x0d\x0a ()/\\-,."
  1058.                  PCSZ pcszEndChars)    // suggestion: "\x0d\x0a ()/\\-,.:;"
  1059. {
  1060.     PSZ     pReturn = 0;
  1061.  
  1062.     if (pxstr && pstrFind)      // V0.9.9 (2001-02-14) [umoeller]
  1063.     {
  1064.         ULONG   ulFoundLen = pstrFind->ulLength;
  1065.  
  1066.         if ((pxstr->ulLength) && (ulFoundLen))
  1067.         {
  1068.             PCSZ p = pxstr->psz + ulOfs;
  1069.  
  1070.             do  // while p
  1071.             {
  1072.                 if (p = (PSZ)strhmemfind(p,         // in: haystack
  1073.                                          pxstr->ulLength - (p - pxstr->psz),
  1074.                                                     // remaining length of haystack
  1075.                                          pstrFind->psz,
  1076.                                          ulFoundLen,
  1077.                                          pShiftTable,
  1078.                                          pfRepeatFind))
  1079.                 {
  1080.                     // string found:
  1081.                     // check if that's a word
  1082.  
  1083.                     if (strhIsWord(pxstr->psz,
  1084.                                    p,
  1085.                                    ulFoundLen,
  1086.                                    pcszBeginChars,
  1087.                                    pcszEndChars))
  1088.                     {
  1089.                         // valid end char:
  1090.                         pReturn = (PSZ)p;
  1091.                         break;
  1092.                     }
  1093.  
  1094.                     p += ulFoundLen;
  1095.                 }
  1096.             } while (p);
  1097.  
  1098.         }
  1099.     }
  1100.  
  1101.     return pReturn;
  1102. }
  1103.  
  1104. /*
  1105.  *@@ xstrFindReplace:
  1106.  *      replaces the first occurence of pstrSearch with
  1107.  *      pstrReplace in pxstr.
  1108.  *
  1109.  *      Starting with V0.9.6, this operates entirely on
  1110.  *      XSTRING's for speed because we then know the string
  1111.  *      lengths already and can use memcpy instead of strcpy.
  1112.  *      This new version should be magnitudes faster,
  1113.  *      especially with large string bufffers.
  1114.  *
  1115.  *      None of the pointers can be NULL, but if pstrReplace
  1116.  *      is empty, this effectively erases pstrSearch in pxstr.
  1117.  *
  1118.  *      Returns the length of the new string (exclusing the
  1119.  *      null terminator) or 0 if pszSearch was not found
  1120.  *      (and pxstr was therefore not changed).
  1121.  *
  1122.  *      This starts the search at *pulOfs. If
  1123.  *      (*pulOfs == 0), this starts from the beginning
  1124.  *      of pxstr.
  1125.  *
  1126.  *      If the string was found, *pulOfs will be set to the
  1127.  *      first character after the new replacement string. This
  1128.  *      allows you to call this func again with the same strings
  1129.  *      to have several occurences replaced (see the example below).
  1130.  *
  1131.  *      There are two wrappers around this function which
  1132.  *      work on C strings instead (however, thus losing the
  1133.  *      speed advantage):
  1134.  *
  1135.  *      -- strhFindReplace operates on C strings only;
  1136.  *
  1137.  *      -- xstrFindReplaceC uses C strings for the search and replace
  1138.  *         parameters.
  1139.  *
  1140.  *      <B>Example usage:</B>
  1141.  *
  1142.  +          XSTRING strBuf,
  1143.  +                  strFind,
  1144.  +                  strRepl;
  1145.  +          size_t  ShiftTable[256];
  1146.  +          BOOL    fRepeat = FALSE;
  1147.  +          ULONG   ulOffset = 0;
  1148.  +
  1149.  +          xstrInitCopy(&strBuf, "Test phrase 1. Test phrase 2.", 0);
  1150.  +          xstrInitSet(&strFind, "Test");
  1151.  +          xstrInitSet(&strRepl, "Dummy");
  1152.  +          while (xstrFindReplace(&str,
  1153.  +                                 &ulPos,      // in/out: offset
  1154.  +                                 &strFind,    // search
  1155.  +                                 &strRepl,    // replace
  1156.  +                                 ShiftTable,
  1157.  +                                 &fRepeat))
  1158.  +              ;
  1159.  *
  1160.  *      would replace all occurences of "Test" in str with
  1161.  *      "Dummy".
  1162.  *
  1163.  *      Memory cost: Calls xstrrpl if pstrSearch was found.
  1164.  *
  1165.  *@@changed V0.9.0 [umoeller]: totally rewritten.
  1166.  *@@changed V0.9.0 (99-11-08) [umoeller]: crashed if *ppszBuf was NULL. Fixed.
  1167.  *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxrpl
  1168.  *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
  1169.  *@@changed V0.9.6 (2000-11-12) [umoeller]: now using strhmemfind
  1170.  *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrrpl; extracted new xstrrpl
  1171.  */
  1172.  
  1173. ULONG xstrFindReplace(PXSTRING pxstr,               // in/out: string
  1174.                       PULONG pulOfs,                // in: where to begin search (0 = start);
  1175.                                                     // out: ofs of first char after replacement string
  1176.                       const XSTRING *pstrSearch,    // in: search string; cannot be NULL
  1177.                       const XSTRING *pstrReplace,   // in: replacement string; cannot be NULL
  1178.                       size_t *pShiftTable,          // in: shift table (see strhmemfind)
  1179.                       PBOOL pfRepeatFind)           // in: repeat find? (see strhmemfind)
  1180. {
  1181.     ULONG    ulrc = 0;      // default: not found
  1182.  
  1183.     if ((pxstr) && (pstrSearch) && (pstrReplace))
  1184.     {
  1185.         ULONG   cSearchLen = pstrSearch->ulLength;
  1186.  
  1187.         // can we search this?
  1188.         if (    (*pulOfs < pxstr->ulLength)
  1189.              && (cSearchLen)
  1190.            )
  1191.         {
  1192.             // yes:
  1193.             ULONG   ulOfs = *pulOfs;
  1194.             PCSZ pFound;
  1195.             if (pFound = (PCSZ)strhmemfind(pxstr->psz + ulOfs, // in: haystack
  1196.                                            pxstr->ulLength - ulOfs,
  1197.                                            pstrSearch->psz,
  1198.                                            cSearchLen,
  1199.                                            pShiftTable,
  1200.                                            pfRepeatFind))
  1201.             {
  1202.                 ULONG ulFirstReplOfs = pFound - pxstr->psz;
  1203.                 // found in buffer from ofs:
  1204.                 // replace pFound with pstrReplace
  1205.                 ulrc = xstrrpl(pxstr,
  1206.                                ulFirstReplOfs,              // where to start
  1207.                                cSearchLen,                  // chars to replace
  1208.                                pstrReplace->psz,
  1209.                                pstrReplace->ulLength);      // adjusted V0.9.11 (2001-04-22) [umoeller]
  1210.  
  1211.                 // return new length
  1212.                 *pulOfs = ulFirstReplOfs + pstrReplace->ulLength;
  1213.             } // end if (pFound)
  1214.         } // end if (    (*pulOfs < pxstr->ulLength) ...
  1215.     } // end if ((pxstr) && (pstrSearch) && (pstrReplace))
  1216.  
  1217.     return ulrc;
  1218. }
  1219.  
  1220. /*
  1221.  *@@ xstrFindReplaceC:
  1222.  *      wrapper around xstrFindReplace() which allows using
  1223.  *      C strings for the find and replace parameters.
  1224.  *
  1225.  *      This creates two temporary XSTRING's for pcszSearch
  1226.  *      and pcszReplace and thus cannot use the shift table
  1227.  *      for repetitive searches. As a result, this is slower
  1228.  *      than xstrFindReplace.
  1229.  *
  1230.  *      If you search with the same strings several times,
  1231.  *      you'll be better off using xstrFindReplace() directly.
  1232.  *
  1233.  *@@added V0.9.6 (2000-11-01) [umoeller]
  1234.  *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrcrpl
  1235.  */
  1236.  
  1237. ULONG xstrFindReplaceC(PXSTRING pxstr,              // in/out: string
  1238.                        PULONG pulOfs,               // in: where to begin search (0 = start);
  1239.                                                     // out: ofs of first char after replacement string
  1240.                        PCSZ pcszSearch,      // in: search string; cannot be NULL
  1241.                        PCSZ pcszReplace)     // in: replacement string; cannot be NULL
  1242. {
  1243.     XSTRING xstrFind,
  1244.             xstrReplace;
  1245.     size_t  ShiftTable[256];
  1246.     BOOL    fRepeat = FALSE;
  1247.     // initialize find/replace strings... note that the
  1248.     // C strings are not free()'able, so we MUST NOT use xstrClear
  1249.     // before leaving
  1250.     xstrInitSet(&xstrFind, (PSZ)pcszSearch);
  1251.     xstrInitSet(&xstrReplace, (PSZ)pcszReplace);
  1252.  
  1253.     return xstrFindReplace(pxstr, pulOfs, &xstrFind, &xstrReplace, ShiftTable, &fRepeat);
  1254. }
  1255.  
  1256. // static encoding table for xstrEncode
  1257. static PSZ apszEncoding[] =
  1258. {
  1259.     "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
  1260.     "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
  1261.     "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
  1262.     "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
  1263.     "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
  1264.     "%28", "%29", "%2A", "%2B", "%2C", "%2D", "%2E", "%2F",
  1265.     "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",
  1266.     "%38", "%39", "%3A", "%3B", "%3C", "%3D", "%3E", "%3F",
  1267.     "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",
  1268.     "%48", "%49", "%4A", "%4B", "%4C", "%4D", "%4E", "%4F",
  1269.     "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",
  1270.     "%58", "%59", "%5A", "%5B", "%5C", "%5D", "%5E", "%5F",
  1271.     "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",
  1272.     "%68", "%69", "%6A", "%6B", "%6C", "%6D", "%6E", "%6F",
  1273.     "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",
  1274.     "%78", "%79", "%7A", "%7B", "%7C", "%7D", "%7E", "%7F",
  1275.     "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
  1276.     "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F",
  1277.     "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
  1278.     "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F",
  1279.     "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7",
  1280.     "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF",
  1281.     "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7",
  1282.     "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF",
  1283.     "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7",
  1284.     "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF",
  1285.     "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7",
  1286.     "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF",
  1287.     "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7",
  1288.     "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF",
  1289.     "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7",
  1290.     "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
  1291. };
  1292.  
  1293. /*
  1294.  *@@ xstrEncode:
  1295.  *      encodes characters in a string.
  1296.  *
  1297.  *      This searches pxstr for all occurences of the
  1298.  *      characters in pcszEncode (which must be a
  1299.  *      null-terminated list of characters to be
  1300.  *      encoded). Each occurence that is found is
  1301.  *      replaced with "%hh", with "hh" being the
  1302.  *      two-digit hex number of the encoded character.
  1303.  *
  1304.  *      For example, to encode strings for the XCenter,
  1305.  *      set pcszEncode to "%,();=".
  1306.  *
  1307.  *      Returns the no. of characters replaced.
  1308.  *
  1309.  *      NOTE: You must make sure that pcszEncode ALWAYS
  1310.  *      contains the "%" character as well, which must
  1311.  *      always be encoded (i.e. escaped) because it is
  1312.  *      used for encoding the characters. Otherwise
  1313.  *      you won't be able to decode the string again.
  1314.  *
  1315.  *      Example: To encode all occurences of
  1316.  *      "a", "b", and "c" in a string, do this:
  1317.  *
  1318.  +          XSTRING str;
  1319.  +          xstrInitCopy(&str, "Sample characters.";
  1320.  +          xstrEncode(&str, "abc%";
  1321.  *
  1322.  *      would convert str to contain:
  1323.  *
  1324.  +          S%61mple %63hara%63ters.
  1325.  *
  1326.  *      Memory cost: None, except for that of xstrcpy.
  1327.  *
  1328.  *@@added V0.9.9 (2001-02-28) [umoeller]
  1329.  *@@changed V0.9.9 (2001-03-06) [lafaix]: rewritten.
  1330.  */
  1331.  
  1332. ULONG xstrEncode(PXSTRING pxstr,     // in/out: string to convert
  1333.                  PCSZ pcszEncode)    // in: characters to encode (e.g. "%,();=")
  1334. {
  1335.     ULONG ulrc = 0,
  1336.           ul,
  1337.           ulEncodeLength;
  1338.  
  1339.     if (    (pxstr)
  1340.          && (pxstr->ulLength)
  1341.          && (pcszEncode)
  1342.          && (ulEncodeLength = strlen(pcszEncode)))
  1343.     {
  1344.         PSZ pszDest = (PSZ)malloc(pxstr->ulLength * 3
  1345.                                   + 1),
  1346.             pszDestCurr = pszDest;
  1347.  
  1348.         if (pszDest)
  1349.         {
  1350.             for (ul = 0;
  1351.                  ul < pxstr->ulLength;
  1352.                  ul++)
  1353.             {
  1354.                 ULONG ulEncode;
  1355.  
  1356.                 for (ulEncode = 0;
  1357.                      ulEncode < ulEncodeLength;
  1358.                      ulEncode++)
  1359.                 {
  1360.                     if (pxstr->psz[ul] == pcszEncode[ulEncode])
  1361.                     {
  1362.                         // use the static encoding table for speed
  1363.                         memcpy(pszDestCurr,
  1364.                                apszEncoding[(unsigned char)pcszEncode[ulEncode]],
  1365.                                3);
  1366.                         pszDestCurr += 3;
  1367.                         ulrc++;
  1368.                         goto iterate;
  1369.                     }
  1370.                 }
  1371.  
  1372.                 *pszDestCurr++ = pxstr->psz[ul];
  1373.  
  1374.                 iterate:
  1375.                     ;
  1376.             }
  1377.         }
  1378.  
  1379.         // something was encoded; update pxstr
  1380.         if (ulrc)
  1381.         {
  1382.             *pszDestCurr = 0;
  1383.  
  1384.             xstrcpy(pxstr, pszDest, pszDestCurr-pszDest);
  1385.         }
  1386.  
  1387.         free(pszDest);
  1388.     }
  1389.  
  1390.     return ulrc;
  1391. }
  1392.  
  1393. /*
  1394.  *@@ xstrDecode:
  1395.  *      decodes a string previously encoded by xstrEncode.
  1396.  *
  1397.  *      This simply assumes that all '%' characters in
  1398.  *      pxstr introduce encodings and the next two characters
  1399.  *      after '%' always are a hex character code. This
  1400.  *      only recognizes hex in upper case. All this will
  1401.  *      work properly with encodings from xstrEncode.
  1402.  *
  1403.  *      Returns the no. of encodings replaced.
  1404.  *
  1405.  *      Memory cost: None.
  1406.  *
  1407.  *@@added V0.9.9 (2001-02-28) [umoeller]
  1408.  *@@changed V0.9.9 (2001-03-06) [lafaix]: removed memory allocation
  1409.  *@@changed V0.9.16 (2002-02-02) [umoeller]: added cKey
  1410.  */
  1411.  
  1412. ULONG xstrDecode2(PXSTRING pxstr,       // in/out: string to be decoded
  1413.                   CHAR cKey)            // in: encoding key (normally '%')
  1414. {
  1415.     ULONG   ulrc = 0;
  1416.  
  1417.     if (    (pxstr)
  1418.          && (pxstr->ulLength)
  1419.        )
  1420.     {
  1421.         const char  *pSource = pxstr->psz;
  1422.         PSZ         pszDest  = (PSZ)pSource,
  1423.                     pDest    = (PSZ)pSource;
  1424.         CHAR        c;
  1425.  
  1426.         while ((c = *pSource++))
  1427.         {
  1428.             // pSource points to next char now
  1429.  
  1430.             if (c == cKey)
  1431.             {
  1432.                 static char ach[] = "0123456789ABCDEF";
  1433.  
  1434.                 // convert two chars after '%'
  1435.                 CHAR        c2,         // first char after '%'     --> hi-nibble
  1436.                             c3;         // second char after '%'    --> lo-nibble
  1437.                 const char  *p2,        // for first char: points into ach or is NULL
  1438.                             *p3;        // for second char: points into ach or is NULL
  1439.                 if (    (c2 = *pSource)
  1440.                      && (p2 = strchr(ach, c2))
  1441.                      && (c3 = *(pSource + 1))
  1442.                      && (p3 = strchr(ach, c3))
  1443.                    )
  1444.                 {
  1445.                     // both chars after '%' were valid:
  1446.                     *pDest++ =    // lo-nibble:
  1447.                                   (p3 - ach) // 0 for '0', 10 for 'A', ...
  1448.                                   // hi-nibble:
  1449.                                 + ((p2 - ach) << 4);
  1450.                     // go on after that
  1451.                     pSource += 2;
  1452.                     // raise return count
  1453.                     ulrc++;
  1454.                     // next in loop
  1455.                     continue;
  1456.                 }
  1457.             }
  1458.  
  1459.             // not encoding, or null after '%', or invalid encoding:
  1460.             // just leave thisalone
  1461.             *pDest++ = c;
  1462.         } // while ((ch = *pSource++))
  1463.  
  1464.         if (ulrc)
  1465.         {
  1466.             *pDest = 0;
  1467.             pxstr->ulLength = (pDest - pszDest);
  1468.         }
  1469.     }
  1470.  
  1471.     return ulrc;
  1472. }
  1473.  
  1474. /*
  1475.  *@@ xstrDecode:
  1476.  *      added for compatibility with exports.
  1477.  *
  1478.  *@@added V0.9.16 (2002-02-02) [umoeller]
  1479.  */
  1480.  
  1481. ULONG xstrDecode(PXSTRING pxstr)
  1482. {
  1483.     return xstrDecode2(pxstr, '%');
  1484. }
  1485.  
  1486. /*
  1487.  *@@ xstrConvertLineFormat:
  1488.  *      converts between line formats.
  1489.  *
  1490.  *      If (fToCFormat == CRLF2LF), all \r\n pairs are replaced
  1491.  *      with \n chars (UNIX or C format).
  1492.  *
  1493.  *      Reversely, if (fToCFormat == LF2CRLF), all \n chars
  1494.  *      are converted to \r\n pairs (DOS and OS/2 formats).
  1495.  *      No check is made whether this has already been done.
  1496.  *
  1497.  *@@added V0.9.7 (2001-01-15) [umoeller]
  1498.  */
  1499.  
  1500. VOID xstrConvertLineFormat(PXSTRING pxstr,
  1501.                            BOOL fToCFormat) // in: if CRLF2LF, to C format; if LF2CRLF, to OS/2 format.
  1502. {
  1503.     XSTRING     strFind,
  1504.                 strRepl;
  1505.     size_t      ShiftTable[256];
  1506.     BOOL        fRepeat = FALSE;
  1507.     ULONG       ulOfs = 0;
  1508.  
  1509.     if (fToCFormat)
  1510.     {
  1511.         // OS/2 to C:
  1512.         xstrInitSet(&strFind, "\r\n");
  1513.         xstrInitSet(&strRepl, "\n");
  1514.     }
  1515.     else
  1516.     {
  1517.         // C to OS/2:
  1518.         xstrInitSet(&strFind, "\n");
  1519.         xstrInitSet(&strRepl, "\r\n");
  1520.     }
  1521.  
  1522.     while (xstrFindReplace(pxstr,
  1523.                            &ulOfs,
  1524.                            &strFind,
  1525.                            &strRepl,
  1526.                            ShiftTable,
  1527.                            &fRepeat))
  1528.             ;
  1529. }
  1530.  
  1531. /*
  1532.  *@@ xstrPrintf:
  1533.  *      like sprintf, but prints into an XSTRING
  1534.  *      bufer (which must be initialized).
  1535.  *
  1536.  *      Note that the internal stack buffer is
  1537.  *      limited to 2000 bytes, so watch out.
  1538.  *
  1539.  *@@added V0.9.19 (2002-03-28) [umoeller]
  1540.  */
  1541.  
  1542. VOID xstrPrintf(XSTRING *pstr,       // in/out: string buffer (must be init'ed)
  1543.                 PCSZ pcszFormat,     // in: format string (like with printf)
  1544.                 ...)                 // in: additional stuff (like with printf)
  1545. {
  1546.     va_list     args;
  1547.     CHAR        szBuf[2000];
  1548.  
  1549.     va_start(args, pcszFormat);
  1550.     vsprintf(szBuf, pcszFormat, args);
  1551.     va_end(args);
  1552.  
  1553.     xstrcpy(pstr, szBuf, 0);
  1554. }
  1555.  
  1556. /*
  1557.  *@@ xstrCatf:
  1558.  *      like xstrPrintf, but appends to the
  1559.  *      given XSTRING.
  1560.  *
  1561.  *@@added V0.9.19 (2002-04-14) [umoeller]
  1562.  */
  1563.  
  1564. VOID xstrCatf(XSTRING *pstr,       // in/out: string buffer (must be init'ed)
  1565.               PCSZ pcszFormat,     // in: format string (like with printf)
  1566.               ...)                 // in: additional stuff (like with printf)
  1567. {
  1568.     va_list     args;
  1569.     CHAR        szBuf[2000];
  1570.  
  1571.     va_start(args, pcszFormat);
  1572.     vsprintf(szBuf, pcszFormat, args);
  1573.     va_end(args);
  1574.  
  1575.     xstrcat(pstr, szBuf, 0);
  1576. }
  1577.  
  1578. // test case
  1579.  
  1580. /* int main(void)
  1581. {
  1582.     XSTRING str,
  1583.             strFind,
  1584.             strReplace;
  1585.     size_t  shift[256];
  1586.     BOOL    fRepeat = FALSE;
  1587.     ULONG   ulOfs = 0;
  1588.  
  1589.     xstrInit(&str, 0);
  1590.     xstrInit(&strFind, 0);
  1591.     xstrInit(&strReplace, 0);
  1592.  
  1593.     str.ulDelta = 50;
  1594.  
  1595.     xstrcpy(&str, "Test string 1. Test string 2. Test string 3. !", 0);
  1596.     xstrcpy(&strFind, "Test", 0);
  1597.     xstrcpy(&strReplace, "Dummy", 0);
  1598.  
  1599.     printf("Old string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1600.  
  1601.     printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
  1602.  
  1603.     fRepeat = FALSE;
  1604.     ulOfs = 0;
  1605.     while (xstrFindReplace(&str,
  1606.                            &ulOfs,
  1607.                            &strFind,
  1608.                            &strReplace,
  1609.                            shift, &fRepeat));
  1610.         ;
  1611.  
  1612.     printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1613.  
  1614.     printf("Appending \"blah\".\n");
  1615.     xstrcat(&str, "blah", 0);
  1616.     printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1617.  
  1618.     xstrcpy(&strFind, strReplace.psz, 0);
  1619.     xstrClear(&strReplace);
  1620.  
  1621.     printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
  1622.  
  1623.     fRepeat = FALSE;
  1624.     ulOfs = 0;
  1625.     while (xstrFindReplace(&str,
  1626.                    &ulOfs,
  1627.                    &strFind,
  1628.                    &strReplace,
  1629.                    shift, &fRepeat));
  1630.         ;
  1631.  
  1632.     printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1633.  
  1634.     xstrcpy(&strFind, " ", 0);
  1635.     xstrcpy(&strReplace, ".", 0);
  1636.  
  1637.     printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
  1638.  
  1639.     fRepeat = FALSE;
  1640.     ulOfs = 0;
  1641.     while (xstrFindReplace(&str,
  1642.                    &ulOfs,
  1643.                    &strFind,
  1644.                    &strReplace,
  1645.                    shift, &fRepeat));
  1646.         ;
  1647.  
  1648.     printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1649.  
  1650.     xstrcpy(&strFind, ".", 0);
  1651.     xstrcpy(&strReplace, "*.........................*", 0);
  1652.  
  1653.     printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
  1654.  
  1655.     fRepeat = FALSE;
  1656.     ulOfs = 0;
  1657.     while (xstrFindReplace(&str,
  1658.                    &ulOfs,
  1659.                    &strFind,
  1660.                    &strReplace,
  1661.                    shift, &fRepeat));
  1662.         ;
  1663.  
  1664.     printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1665.  
  1666.     xstrcpy(&strFind, "..........", 0);
  1667.     xstrcpy(&strReplace, "@", 0);
  1668.  
  1669.     printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
  1670.  
  1671.     fRepeat = FALSE;
  1672.     ulOfs = 0;
  1673.     while (xstrFindReplace(&str,
  1674.                    &ulOfs,
  1675.                    &strFind,
  1676.                    &strReplace,
  1677.                    shift, &fRepeat));
  1678.         ;
  1679.  
  1680.     printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1681.  
  1682.     printf("Encoding @* chars.\n");
  1683.     xstrEncode(&str, "@*");
  1684.     printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1685.  
  1686.     printf("Decoding @* chars.\n");
  1687.     xstrDecode(&str);
  1688.     printf("New string is: \"%s\" (%d/%d/%d)\n", str.psz, str.ulLength, str.cbAllocated, str.ulDelta);
  1689.  
  1690.     return 0;
  1691. } */
  1692.  
  1693.  
  1694.