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

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

Updated string helpers.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 23.7 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 NEVER modify the psz pointer yourself
54 * because then these functions will get into trouble.
55 *
56 * Also, you should never assume that the "psz"
57 * pointer has not changed after you have called
58 * one of the xstr* functions because these can
59 * always reallocate the buffer if needed.
60 *
61 * 4) If (and only if) you have a char* buffer which
62 * is free()'able (e.g. from strdup()), you can
63 * use xstrset to avoid duplicate copying.
64 *
65 * Function prefixes:
66 * -- xstr* extended string functions.
67 *
68 * The functions in this file used to be in stringh.c
69 * before V0.9.3 (2000-04-01). These have been largely
70 * rewritten with V0.9.6 (2000-11-01).
71 *
72 * Note: Version numbering in this file relates to XWorkplace
73 * version numbering.
74 *
75 *@@added V0.9.3 (2000-04-01) [umoeller]
76 *@@header "helpers\xstring.h"
77 */
78
79/*
80 * Copyright (C) 1999-2000 Ulrich M”ller.
81 * This file is part of the XWorkplace source package.
82 * XWorkplace is free software; you can redistribute it and/or modify
83 * it under the terms of the GNU General Public License as published
84 * by the Free Software Foundation, in version 2 as it comes in the
85 * "COPYING" file of the XWorkplace main distribution.
86 * This program is distributed in the hope that it will be useful,
87 * but WITHOUT ANY WARRANTY; without even the implied warranty of
88 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
89 * GNU General Public License for more details.
90 */
91
92#define OS2EMX_PLAIN_CHAR
93 // this is needed for "os2emx.h"; if this is defined,
94 // emx will define PSZ as _signed_ char, otherwise
95 // as unsigned char
96
97#include <os2.h>
98
99#include <stdlib.h>
100#include <stdio.h>
101#include <string.h>
102
103#include "setup.h" // code generation and debugging options
104
105#include "helpers\stringh.h"
106#include "helpers\xstring.h" // extended string helpers
107
108/*
109 *@@category: Helpers\C helpers\String management
110 */
111
112/*
113 *@@ xstrInit:
114 * initializes an empty XSTRING.
115 *
116 * If (ulPreAllocate != 0), memory is pre-allocated
117 * for the string, but the string will be empty.
118 * This is useful if you plan to add more stuff to
119 * the string later so we don't have to reallocate
120 * all the time in xstrcat.
121 *
122 * Do not use this on an XSTRING which is already
123 * initialized. Use xstrset instead.
124 *
125 *@@added V0.9.6 (2000-11-01) [umoeller]
126 */
127
128void xstrInit(PXSTRING pxstr, // in/out: string
129 ULONG ulPreAllocate) // in: if > 0, memory to allocate
130{
131 memset(pxstr, 0, sizeof(XSTRING));
132 if (ulPreAllocate)
133 {
134 pxstr->psz = (PSZ)malloc(ulPreAllocate);
135 pxstr->cbAllocated = ulPreAllocate;
136 // ulLength is still zero
137 *(pxstr->psz) = 0;
138 }
139}
140
141/*
142 *@@ xstrInitSet:
143 * this can be used instead of xstrInit if you
144 * have a free()'able string you want to initialize
145 * the XSTRING with.
146 *
147 * Do not use this on an XSTRING which is already
148 * initialized. Use xstrset instead.
149 *
150 *@@added V0.9.6 (2000-11-01) [umoeller]
151 */
152
153void xstrInitSet(PXSTRING pxstr,
154 PSZ pszNew)
155{
156 pxstr->psz = pszNew;
157 if (!pszNew)
158 {
159 pxstr->cbAllocated = 0;
160 pxstr->ulLength = 0;
161 }
162 else
163 {
164 pxstr->ulLength = strlen(pszNew);
165 pxstr->cbAllocated = pxstr->ulLength + 1;
166 }
167}
168
169/*
170 *@@ xstrInitCopy:
171 * this can be used instead of xstrInit if you
172 * want to initialize an XSTRING with a copy
173 * of an existing string.
174 *
175 * Do not use this on an XSTRING which is already
176 * initialized. Use xstrcpy instead.
177 *
178 *@@added V0.9.6 (2000-11-01) [umoeller]
179 */
180
181void xstrInitCopy(PXSTRING pxstr,
182 const char *pcszSource)
183{
184 if (pxstr)
185 {
186 memset(pxstr, 0, sizeof(XSTRING));
187
188 if (pcszSource)
189 pxstr->ulLength = strlen(pcszSource);
190
191 if (pxstr->ulLength)
192 {
193 // we do have a source string:
194 pxstr->cbAllocated = pxstr->ulLength + 1;
195 pxstr->psz = (PSZ)malloc(pxstr->cbAllocated);
196 strcpy(pxstr->psz, pcszSource);
197 }
198 }
199}
200
201/*
202 *@@ xstrClear:
203 * clears the specified stack XSTRING and
204 * frees allocated memory.
205 *
206 * This is the reverse to xstrInit.
207 *
208 *@@added V0.9.6 (2000-11-01) [umoeller]
209 */
210
211void xstrClear(PXSTRING pxstr) // in/out: string
212{
213 if (pxstr->psz)
214 free(pxstr->psz);
215 memset(pxstr, 0, sizeof(XSTRING));
216}
217
218/*
219 *@@ xstrCreate:
220 * allocates a new XSTRING from the heap
221 * and calls xstrInit on it.
222 *
223 * Always use xstrFree to free associated
224 * resources.
225 *
226 *@@added V0.9.6 (2000-11-01) [umoeller]
227 */
228
229PXSTRING xstrCreate(ULONG ulPreAllocate)
230{
231 PXSTRING pxstr = (PXSTRING)malloc(sizeof(XSTRING));
232 if (pxstr)
233 xstrInit(pxstr, ulPreAllocate);
234
235 return (pxstr);
236}
237
238/*
239 *@@ xstrFree:
240 * frees the specified heap XSTRING, which must
241 * have been created using xstrCreate.
242 *
243 *@@added V0.9.6 (2000-11-01) [umoeller]
244 */
245
246VOID xstrFree(PXSTRING pxstr) // in/out: string
247{
248 if (pxstr)
249 {
250 xstrClear(pxstr);
251 free(pxstr);
252 }
253}
254
255/*
256 *@@ xstrset:
257 * sets the specified XSTRING to a new string
258 * without copying it.
259 *
260 * pxstr is cleared before the new string is set.
261 *
262 * This ONLY works if pszNew has been allocated from
263 * the heap using malloc() or strdup() and is thus
264 * free()'able.
265 *
266 * This assumes that exactly strlen(pszNew) + 1
267 * bytes have been allocated for pszNew, which
268 * is true if pszNew comes from strdup().
269 *
270 *@@added V0.9.6 (2000-11-01) [umoeller]
271 */
272
273ULONG xstrset(PXSTRING pxstr, // in/out: string
274 PSZ pszNew) // in: heap PSZ to use
275{
276 xstrClear(pxstr);
277 pxstr->psz = pszNew;
278 if (pszNew)
279 {
280 pxstr->ulLength = strlen(pszNew);
281 pxstr->cbAllocated = pxstr->ulLength + 1;
282 }
283 // else null string: cbAllocated and ulLength are 0 already
284
285 return (pxstr->ulLength);
286}
287
288/*
289 *@@ xstrcpy:
290 * copies pcszSource to pxstr, for which memory is allocated
291 * as necessary.
292 *
293 * If pxstr contains something, its contents are destroyed.
294 *
295 * Returns the length of the new string (excluding the null
296 * terminator), or null upon errors.
297 *
298 * Example:
299 *
300 + XSTRING str;
301 + xstrInit(&str, 0);
302 + xstrcpy(&str, "blah");
303 *
304 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcpy
305 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
306 */
307
308ULONG xstrcpy(PXSTRING pxstr, // in/out: string
309 const char *pcszSource) // in: source, can be NULL
310{
311 xstrClear(pxstr);
312
313 if (pxstr)
314 {
315 ULONG ulSourceLength = 0;
316 if (pcszSource)
317 ulSourceLength = strlen(pcszSource);
318
319 if (ulSourceLength)
320 {
321 // we do have a source string:
322 ULONG cbNeeded = ulSourceLength + 1;
323 if (cbNeeded > pxstr->cbAllocated)
324 {
325 // we need more memory than we have previously
326 // allocated:
327 pxstr->cbAllocated = cbNeeded;
328 pxstr->psz = (PSZ)malloc(cbNeeded);
329 }
330 // else: we have enough memory
331
332 strcpy(pxstr->psz, pcszSource);
333 }
334 else
335 {
336 // no source specified or source is empty:
337 if (pxstr->cbAllocated)
338 // we did have a string: set to empty,
339 // but leave allocated memory intact
340 *(pxstr->psz) = 0;
341 // else: pxstr->psz is still NULL
342 }
343
344 // in all cases, set new length
345 pxstr->ulLength = ulSourceLength;
346 }
347
348 return (pxstr->ulLength);
349}
350
351/*
352 *@@ xstrcat:
353 * appends pcszSource to pxstr, for which memory is allocated
354 * as necessary.
355 *
356 * If pxstr is empty, this behaves just like xstrcpy.
357 *
358 * Returns the length of the new string (excluding the null
359 * terminator), or null upon errors.
360 *
361 * Example:
362 *
363 + XSTRING str;
364 + xstrInit(&str, 0);
365 + xstrcpy(&str, "blah");
366 + xstrcat(&str, "blup");
367 *
368 * would do the following:
369 * a) free the old value of str ("blah");
370 * b) reallocate str;
371 * c) so that psz afterwards points to a new string containing
372 * "blahblup".
373 *
374 *@@changed V0.9.1 (99-12-20) [umoeller]: fixed memory leak
375 *@@changed V0.9.1 (2000-01-03) [umoeller]: crashed if pszString was null; fixed
376 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcat
377 *@@changed V0.9.3 (2000-05-11) [umoeller]: returned 0 if pszString was initially empty; fixed
378 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
379 */
380
381ULONG xstrcat(PXSTRING pxstr, // in/out: string
382 const char *pcszSource) // in: source, can be NULL
383{
384 ULONG ulrc = 0;
385
386 if (pxstr)
387 {
388 ULONG ulSourceLength = 0;
389 if (pcszSource)
390 ulSourceLength = strlen(pcszSource);
391
392 if (ulSourceLength)
393 {
394 // we do have a source string:
395
396 // 1) memory management
397 ULONG cbNeeded = pxstr->ulLength + ulSourceLength + 1;
398 if (cbNeeded > pxstr->cbAllocated)
399 {
400 // we need more memory than we have previously
401 // allocated:
402 if (pxstr->cbAllocated)
403 // appendee already had memory:
404 // reallocate
405 pxstr->psz = (PSZ)realloc(pxstr->psz,
406 cbNeeded);
407 else
408 // appendee has no memory:
409 pxstr->psz = (PSZ)malloc(cbNeeded);
410
411 pxstr->cbAllocated = cbNeeded;
412 // ulLength is unchanged yet
413 }
414 // else: we have enough memory, both if appendee
415 // is empty or not empty
416
417 // now we have:
418 // -- if appendee (pxstr) had enough memory, no problem
419 // -- if appendee (pxstr) needed more memory
420 // -- and was not empty: pxstr->psz now points to a
421 // reallocated copy of the old string
422 // -- and was empty: pxstr->psz now points to a
423 // new (unitialized) buffer
424
425 // 2) append source string:
426 strcpy(pxstr->psz + pxstr->ulLength,
427 pcszSource);
428
429 // in all cases, set new length
430 pxstr->ulLength += ulSourceLength;
431 ulrc = ulSourceLength;
432 }
433 // else no source specified or source is empty:
434 // do nothing
435 }
436
437 return (ulrc);
438}
439
440/*
441 *@@ xstrrpl:
442 * replaces pstrSearch with pstrReplace in pxstr.
443 *
444 * Starting with V0.9.6, this operates entirely on
445 * XSTRING's for speed because we then know the string
446 * lengths already and can use memcpy instead of strcpy.
447 * This new version should be magnitudes faster.
448 *
449 * None of the pointers can be NULL, but if pstrReplace
450 * is empty, this effectively erases pstrSearch in pxstr.
451 *
452 * Returns the length of the new string (exclusing the
453 * null terminator) or 0 if pszSearch was not found
454 * (and pxstr was therefore not changed).
455 *
456 * If the string was found and (pulAfterOfs != NULL),
457 * *pulAfterOfs will be set to the first character
458 * after the new replacement string. This allows you
459 * to call this func again with the same strings to
460 * have several occurences replaced.
461 *
462 * Only the first occurence is replaced. To replace
463 * all occurences in a buffer, repeat calling this
464 * function until it returns 0.
465 *
466 * There are two wrappers around this function which
467 * work on C strings instead (however, thus losing the
468 * speed advantage):
469 *
470 * -- strhrpl operates on C strings only;
471 *
472 * -- xstrcrpl uses C strings for the search and replace
473 * parameters.
474 *
475 * <B>Example usage:</B>
476 *
477 + XSTRING str;
478 + ULONG ulPos = 0;
479 + xstrInit(&str, 0);
480 + xstrcpy(&str, "Test phrase 1. Test phrase 2.");
481 + while (xstrrpl(&str,
482 + ulPos,
483 + "Test", // search
484 + "Dummy", // replace
485 + &ulPos))
486 + ;
487 *
488 * would replace all occurences of "Test" in str with
489 * "Dummy".
490 *
491 *@@changed V0.9.0 [umoeller]: totally rewritten.
492 *@@changed V0.9.0 (99-11-08) [umoeller]: crashed if *ppszBuf was NULL. Fixed.
493 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxrpl
494 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
495 */
496
497ULONG xstrrpl(PXSTRING pxstr, // in/out: string
498 ULONG ulOfs, // in: where to begin search (0 = start)
499 const XSTRING *pstrSearch, // in: search string; cannot be NULL
500 const XSTRING *pstrReplace, // in: replacement string; cannot be NULL
501 PULONG pulAfterOfs) // out: offset where found (ptr can be NULL)
502{
503 ULONG ulrc = 0;
504
505 if ((pxstr) && (pstrSearch) && (pstrReplace))
506 {
507 ULONG cSearchLen = pstrSearch->ulLength;
508
509 // can we search this?
510 if ( (ulOfs < pxstr->ulLength)
511 && (cSearchLen)
512 )
513 {
514 // yes:
515 PSZ pFound = strstr(pxstr->psz + ulOfs,
516 pstrSearch->psz);
517
518 if (pFound)
519 {
520 // found in buffer from ofs:
521 ULONG cReplaceLen = pstrReplace->ulLength;
522 // can be 0!
523
524 // length of new string
525 ULONG cbNeeded = pxstr->ulLength
526 + cReplaceLen
527 - cSearchLen
528 + 1, // null terminator
529 // offset where pszSearch was found
530 ulFoundOfs = pFound - pxstr->psz;
531
532 // now check if we have enough memory...
533 if (pxstr->cbAllocated < cbNeeded)
534 {
535 // no, we need more memory:
536 // allocate new buffer
537 PSZ pszNew = (PSZ)malloc(cbNeeded);
538
539 if (ulFoundOfs)
540 // "found" was not at the beginning:
541 // copy from beginning up to found-offset
542 memcpy(pszNew,
543 pxstr->psz,
544 ulFoundOfs); // up to "found"
545
546 if (cReplaceLen)
547 {
548 // we have a replacement:
549 // insert it next
550 memcpy(pszNew + ulFoundOfs,
551 pstrReplace->psz,
552 cReplaceLen + 1); // include null terminator
553 }
554
555 // copy rest:
556 // pxstr frontFOUNDtail
557 // 0 1
558 // 01234567890123
559 // ³ ³ ³ ³
560 // ³ ³ ÀÄ ulFoundOfs + cSearchLen = 10
561 // ³ ³ ³
562 // ³ ÀÄ ulFoundOfs = 5
563 // ³ ³
564 // pxstr->ulLength = 14
565 memcpy(pszNew + ulFoundOfs + cReplaceLen,
566 pFound + cSearchLen,
567 // remaining bytes:
568 pxstr->ulLength - ulFoundOfs - cSearchLen // 9
569 + 1); // null terminator
570
571 free(pxstr->psz);
572 pxstr->psz = pszNew;
573 pxstr->ulLength = cbNeeded - 1;
574 pxstr->cbAllocated = cbNeeded;
575 } // end if (pxstr->cbAllocated < cbNeeded)
576 else
577 {
578 // we have enough memory left,
579 // we can just overwrite in the middle...
580
581 PSZ pszAfterFoundBackup = 0;
582 // calc length of string after "found"
583 ULONG cTailLength = pxstr->ulLength - ulFoundOfs - cSearchLen;
584
585 // if "replace" is longer than "found",
586 // make a backup of the stuff after "found",
587 // or this would get overwritten
588 if (cReplaceLen > cSearchLen)
589 {
590 pszAfterFoundBackup = (PSZ)malloc(cTailLength + 1);
591 memcpy(pszAfterFoundBackup,
592 pFound + cSearchLen,
593 cTailLength + 1);
594 }
595
596 // now overwrite "found" in the middle
597 if (cReplaceLen)
598 {
599 memcpy(pxstr->psz + ulFoundOfs,
600 pstrReplace->psz,
601 cReplaceLen); // no null terminator
602 }
603
604 // now append tail (stuff after "found") again...
605 if (pszAfterFoundBackup)
606 {
607 // we made a backup above:
608 memcpy(pxstr->psz + ulFoundOfs + cReplaceLen,
609 pszAfterFoundBackup,
610 cTailLength + 1);
611 free(pszAfterFoundBackup);
612 // done!
613 }
614 else
615 // no backup:
616 if (cReplaceLen < cSearchLen)
617 // "replace" is shorter than "found:
618 memcpy(pxstr->psz + ulFoundOfs + cReplaceLen,
619 pFound + cSearchLen,
620 cTailLength + 1);
621 // else (cReplaceLen == cSearchLen):
622 // we can leave the tail as it is
623
624 pxstr->ulLength = cbNeeded - 1;
625 }
626
627 // return new length
628 ulrc = cbNeeded - 1;
629 if (pulAfterOfs)
630 *pulAfterOfs = ulFoundOfs + cReplaceLen;
631 }
632 }
633 }
634
635 return (ulrc);
636}
637
638/*
639 *@@ xstrcrpl:
640 * wrapper around xstrrpl which allows using C strings
641 * for the find and replace parameters.
642 *
643 *@@added V0.9.6 (2000-11-01) [umoeller]
644 */
645
646ULONG xstrcrpl(PXSTRING pxstr, // in/out: string
647 ULONG ulOfs, // in: where to begin search (0 = start)
648 const char *pcszSearch, // in: search string; cannot be NULL
649 const char *pcszReplace, // in: replacement string; cannot be NULL
650 PULONG pulAfterOfs) // out: offset where found (ptr can be NULL)
651{
652 ULONG ulrc = 0;
653 XSTRING xstrFind,
654 xstrReplace;
655 xstrInit(&xstrFind, 0);
656 xstrset(&xstrFind, (PSZ)pcszSearch);
657 xstrInit(&xstrReplace, 0);
658 xstrset(&xstrReplace, (PSZ)pcszReplace);
659
660 return (xstrrpl(pxstr, ulOfs, &xstrFind, &xstrReplace, pulAfterOfs));
661}
662
663// test case
664
665/* int main(void)
666{
667 XSTRING str,
668 strFind,
669 strReplace;
670 ULONG ulOfs = 0;
671
672 xstrInit(&str, 100);
673 xstrInit(&strFind, 0);
674 xstrInit(&strReplace, 0);
675
676 xstrcpy(&str, "Test string 1. Test string 2. Test string 3. !");
677 xstrcpy(&strFind, "Test");
678 xstrcpy(&strReplace, "Dummy");
679
680 printf("Old string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
681
682 while (xstrrpl(&str,
683 ulOfs,
684 &strFind,
685 &strReplace,
686 &ulOfs))
687 ;
688
689 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
690
691 xstrcpy(&strFind, strReplace.psz);
692 xstrClear(&strReplace);
693 ulOfs = 0;
694 while (xstrrpl(&str,
695 ulOfs,
696 &strFind,
697 &strReplace,
698 &ulOfs))
699 ;
700
701 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
702
703 xstrcpy(&strFind, " ");
704 xstrcpy(&strReplace, ".");
705 ulOfs = 0;
706 while (xstrrpl(&str,
707 ulOfs,
708 &strFind,
709 &strReplace,
710 &ulOfs))
711 ;
712
713 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
714
715 xstrcpy(&strFind, ".");
716 xstrcpy(&strReplace, "***************************");
717 ulOfs = 0;
718 while (xstrrpl(&str,
719 ulOfs,
720 &strFind,
721 &strReplace,
722 &ulOfs))
723 ;
724
725 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
726
727 xstrcpy(&strFind, "*");
728 xstrClear(&strReplace);
729 ulOfs = 0;
730 while (xstrrpl(&str,
731 ulOfs,
732 &strFind,
733 &strReplace,
734 &ulOfs))
735 ;
736
737 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
738} */
739
Note: See TracBrowser for help on using the repository browser.