source: trunk/src/helpers/xstring.c@ 38

Last change on this file since 38 was 38, checked in by umoeller, 25 years ago

Updates to XML.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 38.6 KB
Line 
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 an XSTRING structure, which contains a char*
15 * pointer instead.
16 *
17 * Using these functions has the following advantages:
18 *
19 * -- Automatic memory management. For example, xstrcat will
20 * automatically allocate new memory if the new string
21 * does not fit into the present buffer.
22 *
23 * -- The length of the string is always known. Instead
24 * of running strlen (which consumes time), XSTRING.ulLength
25 * always contains the current length of the string.
26 *
27 * -- The functions also differentiate between allocated
28 * memory and the length of the string. That is, for
29 * iterative appends, you can pre-allocate memory to
30 * avoid excessive reallocations.
31 *
32 * Usage:
33 *
34 * 1) Allocate an XSTRING structure on the stack. Always
35 * call xstrInit on the structure, like this:
36 *
37 + XSTRING str;
38 + xstrInit(&str, 0); // no pre-allocation
39 *
40 * Alternatively, use xstrCreate to have an XSTRING
41 * allocated from the heap.
42 *
43 * Always call xstrClear(&str) to free allocated
44 * memory. Otherwise you'll get memory leaks.
45 * (For heap XSTRING's from xstrCreate, use xstrFree.)
46 *
47 * 2) To copy something into the string, use xstrcpy.
48 * To append something to the string, use xstrcat.
49 * See those functions for samples.
50 *
51 * 3) If you need the char* pointer (e.g. for a call
52 * to another function), use XSTRING.psz. However,
53 * you should ONLY modify the psz pointer yourself
54 * if the other XSTRING members are updated accordingly.
55 * You may, for example, change single characters
56 * in the psz buffer. By contrast, if you change the
57 * length of the string, you must update XSTRING.ulLength.
58 * Otherwise these functions will get into trouble.
59 *
60 * Also, you should never assume that the "psz"
61 * pointer has not changed after you have called
62 * one of the xstr* functions because these can
63 * always reallocate the buffer if more memory
64 * was needed.
65 *
66 * 4) If (and only if) you have a char* buffer which
67 * is free()'able (e.g. from strdup()), you can
68 * use xstrset to avoid duplicate copying.
69 *
70 * Function prefixes:
71 * -- xstr* extended string functions.
72 *
73 * The functions in this file used to be in stringh.c
74 * before V0.9.3 (2000-04-01). These have been largely
75 * rewritten with V0.9.6 (2000-11-01) and are now much
76 * more efficient.
77 *
78 * Note: Version numbering in this file relates to XWorkplace
79 * version numbering.
80 *
81 *@@added V0.9.3 (2000-04-01) [umoeller]
82 *@@header "helpers\xstring.h"
83 */
84
85/*
86 * Copyright (C) 1999-2000 Ulrich M”ller.
87 * This file is part of the "XWorkplace helpers" source package.
88 * This is free software; you can redistribute it and/or modify
89 * it under the terms of the GNU General Public License as published
90 * by the Free Software Foundation, in version 2 as it comes in the
91 * "COPYING" file of the XWorkplace main distribution.
92 * This program is distributed in the hope that it will be useful,
93 * but WITHOUT ANY WARRANTY; without even the implied warranty of
94 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
95 * GNU General Public License for more details.
96 */
97
98#define OS2EMX_PLAIN_CHAR
99 // this is needed for "os2emx.h"; if this is defined,
100 // emx will define PSZ as _signed_ char, otherwise
101 // as unsigned char
102
103#include <os2.h>
104
105#include <stdlib.h>
106#include <stdio.h>
107#include <string.h>
108
109#include "setup.h" // code generation and debugging options
110
111#include "helpers\stringh.h"
112#include "helpers\xstring.h" // extended string helpers
113
114/*
115 *@@category: Helpers\C helpers\String management\XStrings (with memory management)
116 * See xstring.c.
117 */
118
119/*
120 *@@ xstrInit:
121 * initializes an empty XSTRING.
122 *
123 * If (ulPreAllocate != 0), memory is pre-allocated
124 * for the string, but the string will be empty.
125 * This is useful if you plan to add more stuff to
126 * the string later so we don't have to reallocate
127 * all the time in xstrcat.
128 *
129 * Do not use this on an XSTRING which is already
130 * initialized. Use xstrset instead.
131 *
132 *@@added V0.9.6 (2000-11-01) [umoeller]
133 */
134
135void xstrInit(PXSTRING pxstr, // in/out: string
136 ULONG ulPreAllocate) // in: if > 0, memory to allocate
137{
138 memset(pxstr, 0, sizeof(XSTRING));
139 if (ulPreAllocate)
140 {
141 pxstr->psz = (PSZ)malloc(ulPreAllocate);
142 pxstr->cbAllocated = ulPreAllocate;
143 // ulLength is still zero
144 *(pxstr->psz) = 0;
145 }
146}
147
148/*
149 *@@ xstrInitSet:
150 * this can be used instead of xstrInit if you
151 * have a free()'able string you want to initialize
152 * the XSTRING with.
153 *
154 * This does not create a copy of pszNew. Instead,
155 * pszNew is used as the member string in pxstr
156 * directly.
157 *
158 * Do not use this on an XSTRING which is already
159 * initialized. Use xstrset instead.
160 *
161 * Example:
162 *
163 + XSTRING str;
164 + xstrInitSet(&str, strdup("blah"));
165 *
166 *@@added V0.9.6 (2000-11-01) [umoeller]
167 */
168
169void xstrInitSet(PXSTRING pxstr,
170 PSZ pszNew)
171{
172 pxstr->psz = pszNew;
173 if (!pszNew)
174 {
175 pxstr->cbAllocated = 0;
176 pxstr->ulLength = 0;
177 }
178 else
179 {
180 pxstr->ulLength = strlen(pszNew);
181 pxstr->cbAllocated = pxstr->ulLength + 1;
182 }
183}
184
185/*
186 *@@ xstrInitCopy:
187 * this can be used instead of xstrInit if you
188 * want to initialize an XSTRING with a copy
189 * of an existing string. This is a shortcut
190 * for xstrInit() and then xstrcpy().
191 *
192 * As opposed to xstrInitSet, this does create
193 * a copy of pcszSource.
194 *
195 * Do not use this on an XSTRING which is already
196 * initialized. Use xstrcpy instead.
197 *
198 * Example:
199 *
200 + XSTRING str;
201 + xstrInitCopy(&str, "blah");
202 *
203 *@@added V0.9.6 (2000-11-01) [umoeller]
204 *@@changed V0.9.7 (2000-12-31) [umoeller]: added ulExtraAllocate
205 */
206
207void xstrInitCopy(PXSTRING pxstr,
208 const char *pcszSource,
209 ULONG ulExtraAllocate) // in: if > 0, extra memory to allocate
210{
211 if (pxstr)
212 {
213 memset(pxstr, 0, sizeof(XSTRING));
214
215 if (pcszSource)
216 pxstr->ulLength = strlen(pcszSource);
217
218 if (pxstr->ulLength)
219 {
220 // we do have a source string:
221 pxstr->cbAllocated = pxstr->ulLength + 1 + ulExtraAllocate;
222 pxstr->psz = (PSZ)malloc(pxstr->cbAllocated);
223 strcpy(pxstr->psz, pcszSource);
224 }
225 }
226}
227
228/*
229 *@@ xstrClear:
230 * clears the specified stack XSTRING and
231 * frees allocated memory.
232 *
233 * This is the reverse to xstrInit.
234 *
235 *@@added V0.9.6 (2000-11-01) [umoeller]
236 */
237
238void xstrClear(PXSTRING pxstr) // in/out: string
239{
240 if (pxstr->psz)
241 free(pxstr->psz);
242 memset(pxstr, 0, sizeof(XSTRING));
243}
244
245/*
246 *@@ xstrReserve:
247 * this function makes sure that the specified
248 * XSTRING has at least ulBytes bytes allocated.
249 *
250 * This function is useful if you plan to do
251 * a lot of string replacements or appends and
252 * want to avoid that the buffer is reallocated
253 * with each operation. Before those operations,
254 * call this function to make room for the operations.
255 *
256 * If ulBytes is smaller than the current allocation,
257 * this function does nothing.
258 *
259 * The XSTRING must be initialized before the
260 * call.
261 *
262 * Returns the new total no. of allocated bytes.
263 *
264 *@@added V0.9.7 (2001-01-07) [umoeller]
265 */
266
267ULONG xstrReserve(PXSTRING pxstr,
268 ULONG ulBytes)
269{
270 ULONG cbNeeded = ulBytes;
271
272 if (cbNeeded > pxstr->cbAllocated)
273 {
274 // we need more memory than we have previously
275 // allocated:
276 if (pxstr->cbAllocated)
277 // appendee already had memory:
278 // reallocate
279 pxstr->psz = (PSZ)realloc(pxstr->psz,
280 cbNeeded);
281 else
282 {
283 // appendee has no memory:
284 pxstr->psz = (PSZ)malloc(cbNeeded);
285 *(pxstr->psz) = 0;
286 }
287
288 pxstr->cbAllocated = cbNeeded;
289 // ulLength is unchanged
290 }
291
292 return (pxstr->cbAllocated);
293}
294
295/*
296 *@@ xstrCreate:
297 * allocates a new XSTRING from the heap
298 * and calls xstrInit on it.
299 *
300 * Always use xstrFree to free associated
301 * resources.
302 *
303 *@@added V0.9.6 (2000-11-01) [umoeller]
304 */
305
306PXSTRING xstrCreate(ULONG ulPreAllocate)
307{
308 PXSTRING pxstr = (PXSTRING)malloc(sizeof(XSTRING));
309 if (pxstr)
310 xstrInit(pxstr, ulPreAllocate);
311
312 return (pxstr);
313}
314
315/*
316 *@@ xstrFree:
317 * frees the specified heap XSTRING, which must
318 * have been created using xstrCreate.
319 *
320 *@@added V0.9.6 (2000-11-01) [umoeller]
321 */
322
323VOID xstrFree(PXSTRING pxstr) // in/out: string
324{
325 if (pxstr)
326 {
327 xstrClear(pxstr);
328 free(pxstr);
329 }
330}
331
332/*
333 *@@ xstrset:
334 * sets the specified XSTRING to a new string
335 * without copying it.
336 *
337 * pxstr is cleared before the new string is set.
338 *
339 * This ONLY works if pszNew has been allocated from
340 * the heap using malloc() or strdup() and is thus
341 * free()'able.
342 *
343 * This assumes that exactly strlen(pszNew) + 1
344 * bytes have been allocated for pszNew, which
345 * is true if pszNew comes from strdup().
346 *
347 *@@added V0.9.6 (2000-11-01) [umoeller]
348 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
349 */
350
351ULONG xstrset(PXSTRING pxstr, // in/out: string
352 PSZ pszNew) // in: heap PSZ to use
353{
354 if (!pxstr)
355 return (0); // V0.9.9 (2001-02-14) [umoeller]
356
357 xstrClear(pxstr);
358 pxstr->psz = pszNew;
359 if (pszNew)
360 {
361 pxstr->ulLength = strlen(pszNew);
362 pxstr->cbAllocated = pxstr->ulLength + 1;
363 }
364 // else null string: cbAllocated and ulLength are 0 already
365
366 return (pxstr->ulLength);
367}
368
369/*
370 *@@ xstrcpy:
371 * copies pcszSource to pxstr, for which memory is allocated
372 * as necessary.
373 *
374 * If pxstr contains something, its contents are destroyed.
375 *
376 * With ulSourceLength, specify the length of pcszSource.
377 * If you specify 0, this function will run strlen(pcszSource)
378 * itself. If you already know the length of pcszSource (or
379 * only want to copy a substring), you can speed this function
380 * up a bit this way.
381 *
382 * Returns the length of the new string (excluding the null
383 * terminator), or null upon errors.
384 *
385 * Example:
386 *
387 + XSTRING str;
388 + xstrInit(&str, 0);
389 + xstrcpy(&str, "blah", 0);
390 *
391 * This sequence can be abbreviated using xstrInitCopy.
392 *
393 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcpy
394 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
395 *@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
396 *@@changed V0.9.9 (2001-01-28) [lafaix]: fixed memory leak and NULL source behavior
397 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
398 */
399
400ULONG xstrcpy(PXSTRING pxstr, // in/out: string
401 const char *pcszSource, // in: source, can be NULL
402 ULONG ulSourceLength) // in: length of pcszSource or 0
403{
404 // xstrClear(pxstr); NOOOO! this frees the string, we want to keep the memory
405
406 if (!pxstr)
407 return (0); // V0.9.9 (2001-02-14) [umoeller]
408
409 if (pcszSource)
410 {
411 // source specified:
412 if (ulSourceLength == 0)
413 // but not length:
414 ulSourceLength = strlen(pcszSource);
415 }
416 else
417 ulSourceLength = 0;
418
419 if (ulSourceLength)
420 {
421 // we do have a source string:
422 ULONG cbNeeded = ulSourceLength + 1;
423 if (cbNeeded > pxstr->cbAllocated)
424 {
425 // we need more memory than we have previously
426 // allocated:
427 if (pxstr->psz)
428 free(pxstr->psz); // V0.9.9 (2001-01-28) [lafaix]
429 pxstr->cbAllocated = cbNeeded;
430 pxstr->psz = (PSZ)malloc(cbNeeded);
431 }
432 // else: we have enough memory
433
434 // strcpy(pxstr->psz, pcszSource);
435 memcpy(pxstr->psz,
436 pcszSource,
437 ulSourceLength + 1); // V0.9.9 (2001-01-31) [umoeller]
438 }
439 else
440 {
441 // no source specified or source is empty:
442 if (pxstr->cbAllocated)
443 // we did have a string: set to empty,
444 // but leave allocated memory intact
445 *(pxstr->psz) = 0;
446 // else
447 // we had no string previously: in that case
448 // psz and ulLength and cbAllocated are all still NULL
449 }
450
451 // in all cases, set new length
452 pxstr->ulLength = ulSourceLength;
453
454 return (pxstr->ulLength);
455}
456
457/*
458 *@@ xstrcpys:
459 * shortcut to xstrcpy if the source is an XSTRING also.
460 *
461 *@@added V0.9.9 (2001-02-14) [umoeller]
462 */
463
464ULONG xstrcpys(PXSTRING pxstr,
465 const XSTRING *pcstrSource)
466{
467 if (!pcstrSource)
468 return (0);
469
470 return (xstrcpy(pxstr, pcstrSource->psz, pcstrSource->ulLength));
471}
472
473/*
474 *@@ xstrcat:
475 * appends pcszSource to pxstr, for which memory is
476 * reallocated if necessary.
477 *
478 * If pxstr is empty, this behaves just like xstrcpy.
479 *
480 * With ulSourceLength, specify the length of pcszSource.
481 * If you specify 0, this function will run strlen(pcszSource)
482 * itself. If you already know the length of pcszSource (or
483 * only want to copy a substring), you can speed this function
484 * up a bit this way.
485 *
486 * Returns the length of the new string (excluding the null
487 * terminator) if the string was changed, or 0 if nothing
488 * happened.
489 *
490 * Note: To append a single character, xstrcatc is faster
491 * than xstrcat.
492 *
493 * Example:
494 *
495 + XSTRING str;
496 + xstrInit(&str, 0);
497 + xstrcpy(&str, "blah", 0);
498 + xstrcat(&str, "blup", 0);
499 *
500 * After this, str.psz points to a new string containing
501 * "blahblup".
502 *
503 *@@changed V0.9.1 (99-12-20) [umoeller]: fixed memory leak
504 *@@changed V0.9.1 (2000-01-03) [umoeller]: crashed if pszString was null; fixed
505 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcat
506 *@@changed V0.9.3 (2000-05-11) [umoeller]: returned 0 if pszString was initially empty; fixed
507 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
508 *@@changed V0.9.7 (2000-12-10) [umoeller]: return value was wrong
509 *@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
510 */
511
512ULONG xstrcat(PXSTRING pxstr, // in/out: string
513 const char *pcszSource, // in: source, can be NULL
514 ULONG ulSourceLength) // in: length of pcszSource or 0
515{
516 ULONG ulrc = 0;
517
518 if (pxstr)
519 {
520 if (pcszSource)
521 {
522 if (ulSourceLength == 0)
523 ulSourceLength = strlen(pcszSource);
524
525 if (ulSourceLength)
526 {
527 // we do have a source string:
528
529 // 1) memory management
530 ULONG cbNeeded = pxstr->ulLength + ulSourceLength + 1;
531 if (cbNeeded > pxstr->cbAllocated)
532 {
533 // we need more memory than we have previously
534 // allocated:
535 if (pxstr->cbAllocated)
536 // appendee already had memory:
537 // reallocate
538 pxstr->psz = (PSZ)realloc(pxstr->psz,
539 cbNeeded);
540 else
541 // appendee has no memory:
542 pxstr->psz = (PSZ)malloc(cbNeeded);
543
544 pxstr->cbAllocated = cbNeeded;
545 // ulLength is unchanged yet
546 }
547 // else: we have enough memory, both if appendee
548 // is empty or not empty
549
550 // now we have:
551 // -- if appendee (pxstr) had enough memory, no problem
552 // -- if appendee (pxstr) needed more memory
553 // -- and was not empty: pxstr->psz now points to a
554 // reallocated copy of the old string
555 // -- and was empty: pxstr->psz now points to a
556 // new (unitialized) buffer
557
558 // 2) append source string:
559 memcpy(pxstr->psz + pxstr->ulLength,
560 pcszSource,
561 ulSourceLength + 1); // null terminator
562
563 // in all cases, set new length
564 pxstr->ulLength += ulSourceLength;
565 ulrc = pxstr->ulLength; // V0.9.7 (2000-12-10) [umoeller]
566
567 } // end if (ulSourceLength)
568 }
569
570 // else no source specified or source is empty:
571 // do nothing
572 }
573
574 return (ulrc);
575}
576
577/*
578 *@@ xstrcatc:
579 * this is similar to xstrcat, except that this is
580 * for a single character. This is a bit faster than
581 * xstrcat.
582 *
583 * If "c" is \0, nothing happens.
584 *
585 * If pxstr is empty, this behaves just like xstrcpy.
586 *
587 * Returns the length of the new string (excluding the null
588 * terminator) if the string was changed, or 0 if nothing
589 * happened.
590 *
591 * Example:
592 *
593 + XSTRING str;
594 + xstrInit(&str, 0);
595 + xstrcpy(&str, "blu");
596 + xstrcatc(&str, 'p');
597 *
598 * After this, str.psz points to a new string containing
599 * "blup".
600 *
601 *@@added V0.9.7 (2000-12-10) [umoeller]
602 */
603
604ULONG xstrcatc(PXSTRING pxstr, // in/out: string
605 CHAR c) // in: character to append, can be \0
606{
607 ULONG ulrc = 0;
608
609 if ((pxstr) && (c))
610 {
611 // ULONG ulSourceLength = 1;
612 // 1) memory management
613 ULONG cbNeeded = pxstr->ulLength // existing length, without null terminator
614 + 1 // new character
615 + 1; // null terminator
616 if (cbNeeded > pxstr->cbAllocated)
617 {
618 // we need more memory than we have previously
619 // allocated:
620 if (pxstr->cbAllocated)
621 // appendee already had memory:
622 // reallocate
623 pxstr->psz = (PSZ)realloc(pxstr->psz,
624 cbNeeded);
625 else
626 // appendee has no memory:
627 pxstr->psz = (PSZ)malloc(cbNeeded);
628
629 pxstr->cbAllocated = cbNeeded;
630 // ulLength is unchanged yet
631 }
632 // else: we have enough memory, both if appendee
633 // is empty or not empty
634
635 // now we have:
636 // -- if appendee (pxstr) had enough memory, no problem
637 // -- if appendee (pxstr) needed more memory
638 // -- and was not empty: pxstr->psz now points to a
639 // reallocated copy of the old string
640 // -- and was empty: pxstr->psz now points to a
641 // new (unitialized) buffer
642
643 // 2) append character:
644 pxstr->psz[pxstr->ulLength] = c;
645 pxstr->psz[pxstr->ulLength + 1] = '\0';
646
647 // in all cases, set new length
648 pxstr->ulLength++;
649 ulrc = pxstr->ulLength;
650
651 } // end if ((pxstr) && (c))
652
653 return (ulrc);
654}
655
656/*
657 *@@ xstrcats:
658 * shortcut to xstrcat if the source is an XSTRING also.
659 *
660 *@@added V0.9.9 (2001-02-14) [umoeller]
661 */
662
663ULONG xstrcats(PXSTRING pxstr,
664 const XSTRING *pcstrSource)
665{
666 if (!pcstrSource)
667 return (0);
668
669 return (xstrcat(pxstr, pcstrSource->psz, pcstrSource->ulLength));
670}
671
672/*
673 *@@ xstrrpl:
674 * replaces cReplLen characters in pxstr, starting
675 * at the position ulFirstReplPos, with the string
676 * in pxstrReplaceWith.
677 *
678 * Returns the new length of the string, excluding
679 * the null terminator, or 0 if the replacement failed
680 * (e.g. because the offsets were too large).
681 *
682 * This has been extracted from xstrFindReplace because
683 * if you already know the position of a substring,
684 * you can now call this directly. This properly
685 * reallocates the string if more memory is needed.
686 *
687 * Example:
688 *
689 + XSTRING xstr, xstrReplacement;
690 + xstrInitCopy(&xstr, "This is a test string.");
691 + // positions: 0123456789012345678901
692 + // 1 2
693 +
694 + xstrInitCopy(&xstrReplacement, "stupid");
695 +
696 + xstrrpl(&xstr,
697 + 10, // position of "test"
698 + 4, // length of "test"
699 + &xstrReplacement);
700 *
701 * This would yield "This is a stupid string."
702 *
703 *@@added V0.9.7 (2001-01-15) [umoeller]
704 *@@changed V0.9.9 (2001-01-29) [lafaix]: fixed unnecessary allocation when pxstr was big enough
705 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
706 */
707
708ULONG xstrrpl(PXSTRING pxstr, // in/out: string
709 ULONG ulFirstReplOfs, // in: ofs of first char to replace
710 ULONG cReplLen, // in: no. of chars to replace
711 const XSTRING *pstrReplaceWith) // in: string to replace chars with
712{
713 ULONG ulrc = 0;
714
715 // security checks...
716 if ( (pxstr) // V0.9.9 (2001-02-14) [umoeller]
717 && (ulFirstReplOfs + cReplLen <= pxstr->ulLength)
718 && (pstrReplaceWith)
719 // && (pstrReplaceWith->ulLength) no, this can be empty
720 )
721 {
722 ULONG cReplaceLen = pstrReplaceWith->ulLength;
723 // can be 0!
724
725 // length of new string
726 ULONG cbNeeded = pxstr->ulLength
727 + cReplaceLen
728 - cReplLen
729 + 1; // null terminator
730 // offset where pszSearch was found
731 // ulFirstReplOfs = pFound - pxstr->psz; now ulFirstReplOfs
732 PSZ pFound = pxstr->psz + ulFirstReplOfs;
733
734 // now check if we have enough memory...
735 if (pxstr->cbAllocated < cbNeeded)
736 {
737 // no, we need more memory:
738 // allocate new buffer
739 PSZ pszNew = (PSZ)malloc(cbNeeded);
740
741 if (ulFirstReplOfs)
742 // "found" was not at the beginning:
743 // copy from beginning up to found-offset
744 memcpy(pszNew,
745 pxstr->psz,
746 ulFirstReplOfs); // up to "found"
747
748 if (cReplaceLen)
749 {
750 // we have a replacement:
751 // insert it next
752 memcpy(pszNew + ulFirstReplOfs,
753 pstrReplaceWith->psz,
754 cReplaceLen + 1); // include null terminator
755 }
756
757 // copy rest:
758 // pxstr frontFOUNDtail
759 // 0 1
760 // 01234567890123
761 // ³ ³ ³ ³
762 // ³ ³ ÀÄ ulFirstReplOfs + cReplLen = 10
763 // ³ ³ ³
764 // ³ ÀÄ ulFirstReplOfs = 5
765 // ³ ³
766 // pxstr->ulLength = 14
767 memcpy(pszNew + ulFirstReplOfs + cReplaceLen,
768 pFound + cReplLen,
769 // remaining bytes:
770 pxstr->ulLength - ulFirstReplOfs - cReplLen // 9
771 + 1); // null terminator
772
773 // replace old buffer with new one
774 free(pxstr->psz);
775 pxstr->psz = pszNew;
776 pxstr->ulLength = cbNeeded - 1;
777 pxstr->cbAllocated = cbNeeded;
778 } // end if (pxstr->cbAllocated < cbNeeded)
779 else
780 {
781 // we have enough memory left,
782 // we can just overwrite in the middle...
783 // fixed V0.9.9 (2001-01-29) [lafaix]
784
785 // calc length of string after "found"
786 ULONG cTailLength = pxstr->ulLength - ulFirstReplOfs - cReplLen;
787
788 // first, we move the end to its new location
789 // (memmove handles overlap if needed)
790 memmove(pFound + cReplaceLen,
791 pFound + cReplLen,
792 cTailLength + 1); // including null terminator
793
794 // now overwrite "found" in the middle
795 if (cReplaceLen)
796 {
797 memcpy(pFound,
798 pstrReplaceWith->psz,
799 cReplaceLen); // no null terminator
800 }
801
802 // that's it; adjust the string length now
803 pxstr->ulLength = cbNeeded - 1;
804 }
805
806 ulrc = cbNeeded - 1;
807 } // end checks
808
809 return (ulrc);
810}
811
812/*
813 *@@ xstrFindWord:
814 * searches for pstrFind in pxstr, starting at ulOfs.
815 * However, this only finds pstrFind if it's a "word",
816 * i.e. surrounded by one of the characters in the
817 * pcszBeginChars and pcszEndChars array.
818 *
819 * This is similar to strhFindWord, but this uses
820 * strhmemfind for fast searching, and it doesn't
821 * have to calculate the string lengths because these
822 * already in XSTRING.
823 *
824 * Returns 0 if no "word" was found, or the offset
825 * of the "word" in pxstr if found.
826 *
827 *@@added V0.9.6 (2000-11-12) [umoeller]
828 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL string crashs
829 */
830
831PSZ xstrFindWord(const XSTRING *pxstr, // in: buffer to search ("haystack")
832 ULONG ulOfs, // in: where to begin search (0 = start)
833 const XSTRING *pstrFind, // in: word to find ("needle")
834 size_t *pShiftTable, // in: shift table (see strhmemfind)
835 PBOOL pfRepeatFind, // in: repeat find? (see strhmemfind)
836 const char *pcszBeginChars, // suggestion: "\x0d\x0a ()/\\-,."
837 const char *pcszEndChars) // suggestion: "\x0d\x0a ()/\\-,.:;"
838{
839 PSZ pReturn = 0;
840
841 if (pxstr && pstrFind) // V0.9.9 (2001-02-14) [umoeller]
842 {
843 ULONG ulFoundLen = pstrFind->ulLength;
844
845 if ((pxstr->ulLength) && (ulFoundLen))
846 {
847 const char *p = pxstr->psz + ulOfs;
848
849 do // while p
850 {
851 p = (PSZ)strhmemfind(p, // in: haystack
852 pxstr->ulLength - (p - pxstr->psz),
853 // remaining length of haystack
854 pstrFind->psz,
855 ulFoundLen,
856 pShiftTable,
857 pfRepeatFind);
858 if (p)
859 {
860 // string found:
861 // check if that's a word
862
863 if (strhIsWord(pxstr->psz,
864 p,
865 ulFoundLen,
866 pcszBeginChars,
867 pcszEndChars))
868 {
869 // valid end char:
870 pReturn = (PSZ)p;
871 break;
872 }
873
874 p += ulFoundLen;
875 }
876 } while (p);
877
878 }
879 }
880
881 return (pReturn);
882}
883
884/*
885 *@@ xstrFindReplace:
886 * replaces the first occurence of pstrSearch with
887 * pstrReplace in pxstr.
888 *
889 * Starting with V0.9.6, this operates entirely on
890 * XSTRING's for speed because we then know the string
891 * lengths already and can use memcpy instead of strcpy.
892 * This new version should be magnitudes faster,
893 * especially with large string bufffers.
894 *
895 * None of the pointers can be NULL, but if pstrReplace
896 * is empty, this effectively erases pstrSearch in pxstr.
897 *
898 * Returns the length of the new string (exclusing the
899 * null terminator) or 0 if pszSearch was not found
900 * (and pxstr was therefore not changed).
901 *
902 * This starts the search at *pulOfs. If
903 * (*pulOfs == 0), this starts from the beginning
904 * of pxstr.
905 *
906 * If the string was found, *pulOfs will be set to the
907 * first character after the new replacement string. This
908 * allows you to call this func again with the same strings
909 * to have several occurences replaced (see the example below).
910 *
911 * There are two wrappers around this function which
912 * work on C strings instead (however, thus losing the
913 * speed advantage):
914 *
915 * -- strhFindReplace operates on C strings only;
916 *
917 * -- xstrFindReplaceC uses C strings for the search and replace
918 * parameters.
919 *
920 * <B>Example usage:</B>
921 *
922 + XSTRING strBuf,
923 + strFind,
924 + strRepl;
925 + size_t ShiftTable[256];
926 + BOOL fRepeat = FALSE;
927 + ULONG ulOffset = 0;
928 +
929 + xstrInitCopy(&strBuf, "Test phrase 1. Test phrase 2.", 0);
930 + xstrInitSet(&strFind, "Test");
931 + xstrInitSet(&strRepl, "Dummy");
932 + while (xstrFindReplace(&str,
933 + &ulPos, // in/out: offset
934 + &strFind, // search
935 + &strRepl, // replace
936 + ShiftTable,
937 + &fRepeat))
938 + ;
939 *
940 * would replace all occurences of "Test" in str with
941 * "Dummy".
942 *
943 *@@changed V0.9.0 [umoeller]: totally rewritten.
944 *@@changed V0.9.0 (99-11-08) [umoeller]: crashed if *ppszBuf was NULL. Fixed.
945 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxrpl
946 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
947 *@@changed V0.9.6 (2000-11-12) [umoeller]: now using strhmemfind
948 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrrpl; extracted new xstrrpl
949 */
950
951ULONG xstrFindReplace(PXSTRING pxstr, // in/out: string
952 PULONG pulOfs, // in: where to begin search (0 = start);
953 // out: ofs of first char after replacement string
954 const XSTRING *pstrSearch, // in: search string; cannot be NULL
955 const XSTRING *pstrReplace, // in: replacement string; cannot be NULL
956 size_t *pShiftTable, // in: shift table (see strhmemfind)
957 PBOOL pfRepeatFind) // in: repeat find? (see strhmemfind)
958{
959 ULONG ulrc = 0; // default: not found
960
961 if ((pxstr) && (pstrSearch) && (pstrReplace))
962 {
963 ULONG cSearchLen = pstrSearch->ulLength;
964
965 // can we search this?
966 if ( (*pulOfs < pxstr->ulLength)
967 && (cSearchLen)
968 )
969 {
970 // yes:
971 ULONG ulOfs = *pulOfs;
972 const char *pFound
973 = (const char *)strhmemfind(pxstr->psz + ulOfs, // in: haystack
974 pxstr->ulLength - ulOfs,
975 pstrSearch->psz,
976 cSearchLen,
977 pShiftTable,
978 pfRepeatFind);
979 if (pFound)
980 {
981 ULONG ulFirstReplOfs = pFound - pxstr->psz;
982 // found in buffer from ofs:
983 // replace pFound with pstrReplace
984 ulrc = xstrrpl(pxstr,
985 ulFirstReplOfs, // where to start
986 cSearchLen, // chars to replace
987 pstrReplace);
988
989 // return new length
990 *pulOfs = ulFirstReplOfs + pstrReplace->ulLength;
991 } // end if (pFound)
992 } // end if ( (*pulOfs < pxstr->ulLength) ...
993 } // end if ((pxstr) && (pstrSearch) && (pstrReplace))
994
995 return (ulrc);
996}
997
998/*
999 *@@ xstrFindReplaceC:
1000 * wrapper around xstrFindReplace() which allows using
1001 * C strings for the find and replace parameters.
1002 *
1003 * This creates two temporary XSTRING's for pcszSearch
1004 * and pcszReplace and thus cannot use the shift table
1005 * for repetitive searches. As a result, this is slower
1006 * than xstrFindReplace.
1007 *
1008 * If you search with the same strings several times,
1009 * you'll be better off using xstrFindReplace() directly.
1010 *
1011 *@@added V0.9.6 (2000-11-01) [umoeller]
1012 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrcrpl
1013 */
1014
1015ULONG xstrFindReplaceC(PXSTRING pxstr, // in/out: string
1016 PULONG pulOfs, // in: where to begin search (0 = start);
1017 // out: ofs of first char after replacement string
1018 const char *pcszSearch, // in: search string; cannot be NULL
1019 const char *pcszReplace) // in: replacement string; cannot be NULL
1020{
1021 XSTRING xstrFind,
1022 xstrReplace;
1023 size_t ShiftTable[256];
1024 BOOL fRepeat = FALSE;
1025 // initialize find/replace strings... note that the
1026 // C strings are not free()'able, so we MUST NOT use xstrClear
1027 // before leaving
1028 xstrInitSet(&xstrFind, (PSZ)pcszSearch);
1029 xstrInitSet(&xstrReplace, (PSZ)pcszReplace);
1030
1031 return (xstrFindReplace(pxstr, pulOfs, &xstrFind, &xstrReplace, ShiftTable, &fRepeat));
1032}
1033
1034/*
1035 *@@ xstrConvertLineFormat:
1036 * converts between line formats.
1037 *
1038 * If (fToCFormat == CRLF2LF), all \r\n pairs are replaced
1039 * with \n chars (UNIX or C format).
1040 *
1041 * Reversely, if (fToCFormat == LF2CRLF), all \n chars
1042 * are converted to \r\n pairs (DOS and OS/2 formats).
1043 * No check is made whether this has already been done.
1044 *
1045 *@@added V0.9.7 (2001-01-15) [umoeller]
1046 */
1047
1048VOID xstrConvertLineFormat(PXSTRING pxstr,
1049 BOOL fToCFormat) // in: if CRLF2LF, to C format; if LF2CRLF, to OS/2 format.
1050{
1051 XSTRING strFind,
1052 strRepl;
1053 size_t ShiftTable[256];
1054 BOOL fRepeat = FALSE;
1055 ULONG ulOfs = 0;
1056
1057 if (fToCFormat)
1058 {
1059 // OS/2 to C:
1060 xstrInitSet(&strFind, "\r\n");
1061 xstrInitSet(&strRepl, "\n");
1062 }
1063 else
1064 {
1065 // C to OS/2:
1066 xstrInitSet(&strFind, "\n");
1067 xstrInitSet(&strRepl, "\r\n");
1068 }
1069
1070 while (xstrFindReplace(pxstr,
1071 &ulOfs,
1072 &strFind,
1073 &strRepl,
1074 ShiftTable,
1075 &fRepeat))
1076 ;
1077}
1078
1079// test case
1080
1081/* int main(void)
1082{
1083 XSTRING str,
1084 strFind,
1085 strReplace;
1086 size_t shift[256];
1087 BOOL fRepeat = FALSE;
1088 ULONG ulOfs = 0;
1089
1090 xstrInit(&str, 100);
1091 xstrInit(&strFind, 0);
1092 xstrInit(&strReplace, 0);
1093
1094 xstrcpy(&str, "Test string 1. Test string 2. Test string 3. !", 0);
1095 xstrcpy(&strFind, "Test", 0);
1096 xstrcpy(&strReplace, "Dummy", 0);
1097
1098 printf("Old string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1099
1100 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1101
1102 fRepeat = FALSE;
1103 ulOfs = 0;
1104 while (xstrFindReplace(&str,
1105 &ulOfs,
1106 &strFind,
1107 &strReplace,
1108 shift, &fRepeat));
1109 ;
1110
1111 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1112
1113 xstrcpy(&strFind, strReplace.psz, 0);
1114 xstrClear(&strReplace);
1115
1116 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1117
1118 fRepeat = FALSE;
1119 ulOfs = 0;
1120 while (xstrFindReplace(&str,
1121 &ulOfs,
1122 &strFind,
1123 &strReplace,
1124 shift, &fRepeat));
1125 ;
1126
1127 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1128
1129 xstrcpy(&strFind, " ", 0);
1130 xstrcpy(&strReplace, ".", 0);
1131
1132 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1133
1134 fRepeat = FALSE;
1135 ulOfs = 0;
1136 while (xstrFindReplace(&str,
1137 &ulOfs,
1138 &strFind,
1139 &strReplace,
1140 shift, &fRepeat));
1141 ;
1142
1143 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1144
1145 xstrcpy(&strFind, ".", 0);
1146 xstrcpy(&strReplace, "*.........................*", 0);
1147
1148 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1149
1150 fRepeat = FALSE;
1151 ulOfs = 0;
1152 while (xstrFindReplace(&str,
1153 &ulOfs,
1154 &strFind,
1155 &strReplace,
1156 shift, &fRepeat));
1157 ;
1158
1159 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1160
1161 printf("Reserving extra mem.\n");
1162
1163 xstrReserve(&str, 6000);
1164 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1165
1166 xstrcpy(&strFind, "..........", 0);
1167 xstrcpy(&strReplace, "@", 0);
1168
1169 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1170
1171 fRepeat = FALSE;
1172 ulOfs = 0;
1173 while (xstrFindReplace(&str,
1174 &ulOfs,
1175 &strFind,
1176 &strReplace,
1177 shift, &fRepeat));
1178 ;
1179
1180 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1181
1182 return (0);
1183}
1184*/
1185
1186
Note: See TracBrowser for help on using the repository browser.