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

Last change on this file since 39 was 39, checked in by umoeller, 24 years ago

Misc. fixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 39.5 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.
379 *
380 * If you already know the length of pcszSource, you can
381 * speed this function up a bit this way.
382 *
383 * You are required to specify ulSourceLength if you only want
384 * to copy a substring, or pcszSource is not zero-terminated.
385 *
386 * Returns the length of the new string (excluding the null
387 * terminator), or null upon errors.
388 *
389 * Example:
390 *
391 + XSTRING str;
392 + xstrInit(&str, 0);
393 + xstrcpy(&str, "blah", 0);
394 *
395 * This sequence can be abbreviated using xstrInitCopy.
396 *
397 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcpy
398 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
399 *@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
400 *@@changed V0.9.9 (2001-01-28) [lafaix]: fixed memory leak and NULL source behavior
401 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
402 *@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
403 */
404
405ULONG xstrcpy(PXSTRING pxstr, // in/out: string
406 const char *pcszSource, // in: source, can be NULL
407 ULONG ulSourceLength) // in: length of pcszSource or 0
408{
409 // xstrClear(pxstr); NOOOO! this frees the string, we want to keep the memory
410
411 if (!pxstr)
412 return (0); // V0.9.9 (2001-02-14) [umoeller]
413
414 if (pcszSource)
415 {
416 // source specified:
417 if (ulSourceLength == 0)
418 // but not length:
419 ulSourceLength = strlen(pcszSource);
420 }
421 else
422 ulSourceLength = 0;
423
424 if (ulSourceLength)
425 {
426 // we do have a source string:
427 ULONG cbNeeded = ulSourceLength + 1;
428 if (cbNeeded > pxstr->cbAllocated)
429 {
430 // we need more memory than we have previously
431 // allocated:
432 if (pxstr->psz)
433 free(pxstr->psz); // V0.9.9 (2001-01-28) [lafaix]
434 pxstr->cbAllocated = cbNeeded;
435 pxstr->psz = (PSZ)malloc(cbNeeded);
436 }
437 // else: we have enough memory
438
439 memcpy(pxstr->psz,
440 pcszSource,
441 ulSourceLength);
442 *(pxstr->psz + ulSourceLength) = '\0';
443 // V0.9.9 (2001-02-16) [umoeller]
444 // we must do this or otherwise we require pcszSource
445 // to be zero-terminated... not a good idea
446 }
447 else
448 {
449 // no source specified or source is empty:
450 if (pxstr->cbAllocated)
451 // we did have a string: set to empty,
452 // but leave allocated memory intact
453 *(pxstr->psz) = 0;
454 // else
455 // we had no string previously: in that case
456 // psz and ulLength and cbAllocated are all still NULL
457 }
458
459 // in all cases, set new length
460 pxstr->ulLength = ulSourceLength;
461
462 return (pxstr->ulLength);
463}
464
465/*
466 *@@ xstrcpys:
467 * shortcut to xstrcpy if the source is an XSTRING also.
468 *
469 *@@added V0.9.9 (2001-02-14) [umoeller]
470 */
471
472ULONG xstrcpys(PXSTRING pxstr,
473 const XSTRING *pcstrSource)
474{
475 if (!pcstrSource)
476 return (0);
477
478 return (xstrcpy(pxstr, pcstrSource->psz, pcstrSource->ulLength));
479}
480
481/*
482 *@@ xstrcat:
483 * appends pcszSource to pxstr, for which memory is
484 * reallocated if necessary.
485 *
486 * If pxstr is empty, this behaves just like xstrcpy.
487 *
488 * With ulSourceLength, specify the length of pcszSource.
489 * If you specify 0, this function will run strlen(pcszSource)
490 * itself.
491 *
492 * If you already know the length of pcszSource, you can
493 * speed this function up a bit this way.
494 *
495 * You are required to specify ulSourceLength if you only want
496 * to copy a substring, or pcszSource is not zero-terminated.
497 *
498 * Returns the length of the new string (excluding the null
499 * terminator) if the string was changed, or 0 if nothing
500 * happened.
501 *
502 * Note: To append a single character, xstrcatc is faster
503 * than xstrcat.
504 *
505 * Example:
506 *
507 + XSTRING str;
508 + xstrInit(&str, 0);
509 + xstrcpy(&str, "blah", 0);
510 + xstrcat(&str, "blup", 0);
511 *
512 * After this, str.psz points to a new string containing
513 * "blahblup".
514 *
515 *@@changed V0.9.1 (99-12-20) [umoeller]: fixed memory leak
516 *@@changed V0.9.1 (2000-01-03) [umoeller]: crashed if pszString was null; fixed
517 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcat
518 *@@changed V0.9.3 (2000-05-11) [umoeller]: returned 0 if pszString was initially empty; fixed
519 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
520 *@@changed V0.9.7 (2000-12-10) [umoeller]: return value was wrong
521 *@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
522 *@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
523 */
524
525ULONG xstrcat(PXSTRING pxstr, // in/out: string
526 const char *pcszSource, // in: source, can be NULL
527 ULONG ulSourceLength) // in: length of pcszSource or 0
528{
529 ULONG ulrc = 0;
530
531 if (pxstr)
532 {
533 if (pcszSource)
534 {
535 if (ulSourceLength == 0)
536 ulSourceLength = strlen(pcszSource);
537
538 if (ulSourceLength)
539 {
540 // we do have a source string:
541
542 // 1) memory management
543 ULONG cbNeeded = pxstr->ulLength + ulSourceLength + 1;
544 if (cbNeeded > pxstr->cbAllocated)
545 {
546 // we need more memory than we have previously
547 // allocated:
548 if (pxstr->cbAllocated)
549 // appendee already had memory:
550 // reallocate
551 pxstr->psz = (PSZ)realloc(pxstr->psz,
552 cbNeeded);
553 else
554 // appendee has no memory:
555 pxstr->psz = (PSZ)malloc(cbNeeded);
556
557 pxstr->cbAllocated = cbNeeded;
558 // ulLength is unchanged yet
559 }
560 // else: we have enough memory, both if appendee
561 // is empty or not empty
562
563 // now we have:
564 // -- if appendee (pxstr) had enough memory, no problem
565 // -- if appendee (pxstr) needed more memory
566 // -- and was not empty: pxstr->psz now points to a
567 // reallocated copy of the old string
568 // -- and was empty: pxstr->psz now points to a
569 // new (unitialized) buffer
570
571 // 2) append source string:
572 memcpy(pxstr->psz + pxstr->ulLength,
573 pcszSource,
574 ulSourceLength); // null terminator
575
576 *(pxstr->psz + pxstr->ulLength + ulSourceLength) = '\0';
577 // V0.9.9 (2001-02-16) [umoeller]
578 // we must do this or otherwise we require pcszSource
579 // to be zero-terminated... not a good idea
580
581 // in all cases, set new length
582 pxstr->ulLength += ulSourceLength;
583 ulrc = pxstr->ulLength; // V0.9.7 (2000-12-10) [umoeller]
584
585 } // end if (ulSourceLength)
586 }
587
588 // else no source specified or source is empty:
589 // do nothing
590 }
591
592 return (ulrc);
593}
594
595/*
596 *@@ xstrcatc:
597 * this is similar to xstrcat, except that this is
598 * for a single character. This is a bit faster than
599 * xstrcat.
600 *
601 * If "c" is \0, nothing happens.
602 *
603 * If pxstr is empty, this behaves just like xstrcpy.
604 *
605 * Returns the length of the new string (excluding the null
606 * terminator) if the string was changed, or 0 if nothing
607 * happened.
608 *
609 * Example:
610 *
611 + XSTRING str;
612 + xstrInit(&str, 0);
613 + xstrcpy(&str, "blu");
614 + xstrcatc(&str, 'p');
615 *
616 * After this, str.psz points to a new string containing
617 * "blup".
618 *
619 *@@added V0.9.7 (2000-12-10) [umoeller]
620 */
621
622ULONG xstrcatc(PXSTRING pxstr, // in/out: string
623 CHAR c) // in: character to append, can be \0
624{
625 ULONG ulrc = 0;
626
627 if ((pxstr) && (c))
628 {
629 // ULONG ulSourceLength = 1;
630 // 1) memory management
631 ULONG cbNeeded = pxstr->ulLength // existing length, without null terminator
632 + 1 // new character
633 + 1; // null terminator
634 if (cbNeeded > pxstr->cbAllocated)
635 {
636 // we need more memory than we have previously
637 // allocated:
638 if (pxstr->cbAllocated)
639 // appendee already had memory:
640 // reallocate
641 pxstr->psz = (PSZ)realloc(pxstr->psz,
642 cbNeeded);
643 else
644 // appendee has no memory:
645 pxstr->psz = (PSZ)malloc(cbNeeded);
646
647 pxstr->cbAllocated = cbNeeded;
648 // ulLength is unchanged yet
649 }
650 // else: we have enough memory, both if appendee
651 // is empty or not empty
652
653 // now we have:
654 // -- if appendee (pxstr) had enough memory, no problem
655 // -- if appendee (pxstr) needed more memory
656 // -- and was not empty: pxstr->psz now points to a
657 // reallocated copy of the old string
658 // -- and was empty: pxstr->psz now points to a
659 // new (unitialized) buffer
660
661 // 2) append character:
662 pxstr->psz[pxstr->ulLength] = c;
663 pxstr->psz[pxstr->ulLength + 1] = '\0';
664
665 // in all cases, set new length
666 pxstr->ulLength++;
667 ulrc = pxstr->ulLength;
668
669 } // end if ((pxstr) && (c))
670
671 return (ulrc);
672}
673
674/*
675 *@@ xstrcats:
676 * shortcut to xstrcat if the source is an XSTRING also.
677 *
678 *@@added V0.9.9 (2001-02-14) [umoeller]
679 */
680
681ULONG xstrcats(PXSTRING pxstr,
682 const XSTRING *pcstrSource)
683{
684 if (!pcstrSource)
685 return (0);
686
687 return (xstrcat(pxstr, pcstrSource->psz, pcstrSource->ulLength));
688}
689
690/*
691 *@@ xstrrpl:
692 * replaces cReplLen characters in pxstr, starting
693 * at the position ulFirstReplPos, with the string
694 * in pxstrReplaceWith.
695 *
696 * Returns the new length of the string, excluding
697 * the null terminator, or 0 if the replacement failed
698 * (e.g. because the offsets were too large).
699 *
700 * This has been extracted from xstrFindReplace because
701 * if you already know the position of a substring,
702 * you can now call this directly. This properly
703 * reallocates the string if more memory is needed.
704 *
705 * Example:
706 *
707 + XSTRING xstr, xstrReplacement;
708 + xstrInitCopy(&xstr, "This is a test string.");
709 + // positions: 0123456789012345678901
710 + // 1 2
711 +
712 + xstrInitCopy(&xstrReplacement, "stupid");
713 +
714 + xstrrpl(&xstr,
715 + 10, // position of "test"
716 + 4, // length of "test"
717 + &xstrReplacement);
718 *
719 * This would yield "This is a stupid string."
720 *
721 *@@added V0.9.7 (2001-01-15) [umoeller]
722 *@@changed V0.9.9 (2001-01-29) [lafaix]: fixed unnecessary allocation when pxstr was big enough
723 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
724 */
725
726ULONG xstrrpl(PXSTRING pxstr, // in/out: string
727 ULONG ulFirstReplOfs, // in: ofs of first char to replace
728 ULONG cReplLen, // in: no. of chars to replace
729 const XSTRING *pstrReplaceWith) // in: string to replace chars with
730{
731 ULONG ulrc = 0;
732
733 // security checks...
734 if ( (pxstr) // V0.9.9 (2001-02-14) [umoeller]
735 && (ulFirstReplOfs + cReplLen <= pxstr->ulLength)
736 && (pstrReplaceWith)
737 // && (pstrReplaceWith->ulLength) no, this can be empty
738 )
739 {
740 ULONG cReplaceLen = pstrReplaceWith->ulLength;
741 // can be 0!
742
743 // length of new string
744 ULONG cbNeeded = pxstr->ulLength
745 + cReplaceLen
746 - cReplLen
747 + 1; // null terminator
748 // offset where pszSearch was found
749 // ulFirstReplOfs = pFound - pxstr->psz; now ulFirstReplOfs
750 PSZ pFound = pxstr->psz + ulFirstReplOfs;
751
752 // now check if we have enough memory...
753 if (pxstr->cbAllocated < cbNeeded)
754 {
755 // no, we need more memory:
756 // allocate new buffer
757 PSZ pszNew = (PSZ)malloc(cbNeeded);
758
759 if (ulFirstReplOfs)
760 // "found" was not at the beginning:
761 // copy from beginning up to found-offset
762 memcpy(pszNew,
763 pxstr->psz,
764 ulFirstReplOfs); // up to "found"
765
766 if (cReplaceLen)
767 {
768 // we have a replacement:
769 // insert it next
770 memcpy(pszNew + ulFirstReplOfs,
771 pstrReplaceWith->psz,
772 cReplaceLen + 1); // include null terminator
773 }
774
775 // copy rest:
776 // pxstr frontFOUNDtail
777 // 0 1
778 // 01234567890123
779 // ³ ³ ³ ³
780 // ³ ³ ÀÄ ulFirstReplOfs + cReplLen = 10
781 // ³ ³ ³
782 // ³ ÀÄ ulFirstReplOfs = 5
783 // ³ ³
784 // pxstr->ulLength = 14
785 memcpy(pszNew + ulFirstReplOfs + cReplaceLen,
786 pFound + cReplLen,
787 // remaining bytes:
788 pxstr->ulLength - ulFirstReplOfs - cReplLen // 9
789 + 1); // null terminator
790
791 // replace old buffer with new one
792 free(pxstr->psz);
793 pxstr->psz = pszNew;
794 pxstr->ulLength = cbNeeded - 1;
795 pxstr->cbAllocated = cbNeeded;
796 } // end if (pxstr->cbAllocated < cbNeeded)
797 else
798 {
799 // we have enough memory left,
800 // we can just overwrite in the middle...
801 // fixed V0.9.9 (2001-01-29) [lafaix]
802
803 // calc length of string after "found"
804 ULONG cTailLength = pxstr->ulLength - ulFirstReplOfs - cReplLen;
805
806 // first, we move the end to its new location
807 // (memmove handles overlap if needed)
808 memmove(pFound + cReplaceLen,
809 pFound + cReplLen,
810 cTailLength + 1); // including null terminator
811
812 // now overwrite "found" in the middle
813 if (cReplaceLen)
814 {
815 memcpy(pFound,
816 pstrReplaceWith->psz,
817 cReplaceLen); // no null terminator
818 }
819
820 // that's it; adjust the string length now
821 pxstr->ulLength = cbNeeded - 1;
822 }
823
824 ulrc = cbNeeded - 1;
825 } // end checks
826
827 return (ulrc);
828}
829
830/*
831 *@@ xstrFindWord:
832 * searches for pstrFind in pxstr, starting at ulOfs.
833 * However, this only finds pstrFind if it's a "word",
834 * i.e. surrounded by one of the characters in the
835 * pcszBeginChars and pcszEndChars array.
836 *
837 * This is similar to strhFindWord, but this uses
838 * strhmemfind for fast searching, and it doesn't
839 * have to calculate the string lengths because these
840 * already in XSTRING.
841 *
842 * Returns 0 if no "word" was found, or the offset
843 * of the "word" in pxstr if found.
844 *
845 *@@added V0.9.6 (2000-11-12) [umoeller]
846 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL string crashs
847 */
848
849PSZ xstrFindWord(const XSTRING *pxstr, // in: buffer to search ("haystack")
850 ULONG ulOfs, // in: where to begin search (0 = start)
851 const XSTRING *pstrFind, // in: word to find ("needle")
852 size_t *pShiftTable, // in: shift table (see strhmemfind)
853 PBOOL pfRepeatFind, // in: repeat find? (see strhmemfind)
854 const char *pcszBeginChars, // suggestion: "\x0d\x0a ()/\\-,."
855 const char *pcszEndChars) // suggestion: "\x0d\x0a ()/\\-,.:;"
856{
857 PSZ pReturn = 0;
858
859 if (pxstr && pstrFind) // V0.9.9 (2001-02-14) [umoeller]
860 {
861 ULONG ulFoundLen = pstrFind->ulLength;
862
863 if ((pxstr->ulLength) && (ulFoundLen))
864 {
865 const char *p = pxstr->psz + ulOfs;
866
867 do // while p
868 {
869 p = (PSZ)strhmemfind(p, // in: haystack
870 pxstr->ulLength - (p - pxstr->psz),
871 // remaining length of haystack
872 pstrFind->psz,
873 ulFoundLen,
874 pShiftTable,
875 pfRepeatFind);
876 if (p)
877 {
878 // string found:
879 // check if that's a word
880
881 if (strhIsWord(pxstr->psz,
882 p,
883 ulFoundLen,
884 pcszBeginChars,
885 pcszEndChars))
886 {
887 // valid end char:
888 pReturn = (PSZ)p;
889 break;
890 }
891
892 p += ulFoundLen;
893 }
894 } while (p);
895
896 }
897 }
898
899 return (pReturn);
900}
901
902/*
903 *@@ xstrFindReplace:
904 * replaces the first occurence of pstrSearch with
905 * pstrReplace in pxstr.
906 *
907 * Starting with V0.9.6, this operates entirely on
908 * XSTRING's for speed because we then know the string
909 * lengths already and can use memcpy instead of strcpy.
910 * This new version should be magnitudes faster,
911 * especially with large string bufffers.
912 *
913 * None of the pointers can be NULL, but if pstrReplace
914 * is empty, this effectively erases pstrSearch in pxstr.
915 *
916 * Returns the length of the new string (exclusing the
917 * null terminator) or 0 if pszSearch was not found
918 * (and pxstr was therefore not changed).
919 *
920 * This starts the search at *pulOfs. If
921 * (*pulOfs == 0), this starts from the beginning
922 * of pxstr.
923 *
924 * If the string was found, *pulOfs will be set to the
925 * first character after the new replacement string. This
926 * allows you to call this func again with the same strings
927 * to have several occurences replaced (see the example below).
928 *
929 * There are two wrappers around this function which
930 * work on C strings instead (however, thus losing the
931 * speed advantage):
932 *
933 * -- strhFindReplace operates on C strings only;
934 *
935 * -- xstrFindReplaceC uses C strings for the search and replace
936 * parameters.
937 *
938 * <B>Example usage:</B>
939 *
940 + XSTRING strBuf,
941 + strFind,
942 + strRepl;
943 + size_t ShiftTable[256];
944 + BOOL fRepeat = FALSE;
945 + ULONG ulOffset = 0;
946 +
947 + xstrInitCopy(&strBuf, "Test phrase 1. Test phrase 2.", 0);
948 + xstrInitSet(&strFind, "Test");
949 + xstrInitSet(&strRepl, "Dummy");
950 + while (xstrFindReplace(&str,
951 + &ulPos, // in/out: offset
952 + &strFind, // search
953 + &strRepl, // replace
954 + ShiftTable,
955 + &fRepeat))
956 + ;
957 *
958 * would replace all occurences of "Test" in str with
959 * "Dummy".
960 *
961 *@@changed V0.9.0 [umoeller]: totally rewritten.
962 *@@changed V0.9.0 (99-11-08) [umoeller]: crashed if *ppszBuf was NULL. Fixed.
963 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxrpl
964 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
965 *@@changed V0.9.6 (2000-11-12) [umoeller]: now using strhmemfind
966 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrrpl; extracted new xstrrpl
967 */
968
969ULONG xstrFindReplace(PXSTRING pxstr, // in/out: string
970 PULONG pulOfs, // in: where to begin search (0 = start);
971 // out: ofs of first char after replacement string
972 const XSTRING *pstrSearch, // in: search string; cannot be NULL
973 const XSTRING *pstrReplace, // in: replacement string; cannot be NULL
974 size_t *pShiftTable, // in: shift table (see strhmemfind)
975 PBOOL pfRepeatFind) // in: repeat find? (see strhmemfind)
976{
977 ULONG ulrc = 0; // default: not found
978
979 if ((pxstr) && (pstrSearch) && (pstrReplace))
980 {
981 ULONG cSearchLen = pstrSearch->ulLength;
982
983 // can we search this?
984 if ( (*pulOfs < pxstr->ulLength)
985 && (cSearchLen)
986 )
987 {
988 // yes:
989 ULONG ulOfs = *pulOfs;
990 const char *pFound
991 = (const char *)strhmemfind(pxstr->psz + ulOfs, // in: haystack
992 pxstr->ulLength - ulOfs,
993 pstrSearch->psz,
994 cSearchLen,
995 pShiftTable,
996 pfRepeatFind);
997 if (pFound)
998 {
999 ULONG ulFirstReplOfs = pFound - pxstr->psz;
1000 // found in buffer from ofs:
1001 // replace pFound with pstrReplace
1002 ulrc = xstrrpl(pxstr,
1003 ulFirstReplOfs, // where to start
1004 cSearchLen, // chars to replace
1005 pstrReplace);
1006
1007 // return new length
1008 *pulOfs = ulFirstReplOfs + pstrReplace->ulLength;
1009 } // end if (pFound)
1010 } // end if ( (*pulOfs < pxstr->ulLength) ...
1011 } // end if ((pxstr) && (pstrSearch) && (pstrReplace))
1012
1013 return (ulrc);
1014}
1015
1016/*
1017 *@@ xstrFindReplaceC:
1018 * wrapper around xstrFindReplace() which allows using
1019 * C strings for the find and replace parameters.
1020 *
1021 * This creates two temporary XSTRING's for pcszSearch
1022 * and pcszReplace and thus cannot use the shift table
1023 * for repetitive searches. As a result, this is slower
1024 * than xstrFindReplace.
1025 *
1026 * If you search with the same strings several times,
1027 * you'll be better off using xstrFindReplace() directly.
1028 *
1029 *@@added V0.9.6 (2000-11-01) [umoeller]
1030 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrcrpl
1031 */
1032
1033ULONG xstrFindReplaceC(PXSTRING pxstr, // in/out: string
1034 PULONG pulOfs, // in: where to begin search (0 = start);
1035 // out: ofs of first char after replacement string
1036 const char *pcszSearch, // in: search string; cannot be NULL
1037 const char *pcszReplace) // in: replacement string; cannot be NULL
1038{
1039 XSTRING xstrFind,
1040 xstrReplace;
1041 size_t ShiftTable[256];
1042 BOOL fRepeat = FALSE;
1043 // initialize find/replace strings... note that the
1044 // C strings are not free()'able, so we MUST NOT use xstrClear
1045 // before leaving
1046 xstrInitSet(&xstrFind, (PSZ)pcszSearch);
1047 xstrInitSet(&xstrReplace, (PSZ)pcszReplace);
1048
1049 return (xstrFindReplace(pxstr, pulOfs, &xstrFind, &xstrReplace, ShiftTable, &fRepeat));
1050}
1051
1052/*
1053 *@@ xstrConvertLineFormat:
1054 * converts between line formats.
1055 *
1056 * If (fToCFormat == CRLF2LF), all \r\n pairs are replaced
1057 * with \n chars (UNIX or C format).
1058 *
1059 * Reversely, if (fToCFormat == LF2CRLF), all \n chars
1060 * are converted to \r\n pairs (DOS and OS/2 formats).
1061 * No check is made whether this has already been done.
1062 *
1063 *@@added V0.9.7 (2001-01-15) [umoeller]
1064 */
1065
1066VOID xstrConvertLineFormat(PXSTRING pxstr,
1067 BOOL fToCFormat) // in: if CRLF2LF, to C format; if LF2CRLF, to OS/2 format.
1068{
1069 XSTRING strFind,
1070 strRepl;
1071 size_t ShiftTable[256];
1072 BOOL fRepeat = FALSE;
1073 ULONG ulOfs = 0;
1074
1075 if (fToCFormat)
1076 {
1077 // OS/2 to C:
1078 xstrInitSet(&strFind, "\r\n");
1079 xstrInitSet(&strRepl, "\n");
1080 }
1081 else
1082 {
1083 // C to OS/2:
1084 xstrInitSet(&strFind, "\n");
1085 xstrInitSet(&strRepl, "\r\n");
1086 }
1087
1088 while (xstrFindReplace(pxstr,
1089 &ulOfs,
1090 &strFind,
1091 &strRepl,
1092 ShiftTable,
1093 &fRepeat))
1094 ;
1095}
1096
1097// test case
1098
1099/* int main(void)
1100{
1101 XSTRING str,
1102 strFind,
1103 strReplace;
1104 size_t shift[256];
1105 BOOL fRepeat = FALSE;
1106 ULONG ulOfs = 0;
1107
1108 xstrInit(&str, 100);
1109 xstrInit(&strFind, 0);
1110 xstrInit(&strReplace, 0);
1111
1112 xstrcpy(&str, "Test string 1. Test string 2. Test string 3. !", 0);
1113 xstrcpy(&strFind, "Test", 0);
1114 xstrcpy(&strReplace, "Dummy", 0);
1115
1116 printf("Old string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1117
1118 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1119
1120 fRepeat = FALSE;
1121 ulOfs = 0;
1122 while (xstrFindReplace(&str,
1123 &ulOfs,
1124 &strFind,
1125 &strReplace,
1126 shift, &fRepeat));
1127 ;
1128
1129 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1130
1131 xstrcpy(&strFind, strReplace.psz, 0);
1132 xstrClear(&strReplace);
1133
1134 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1135
1136 fRepeat = FALSE;
1137 ulOfs = 0;
1138 while (xstrFindReplace(&str,
1139 &ulOfs,
1140 &strFind,
1141 &strReplace,
1142 shift, &fRepeat));
1143 ;
1144
1145 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1146
1147 xstrcpy(&strFind, " ", 0);
1148 xstrcpy(&strReplace, ".", 0);
1149
1150 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1151
1152 fRepeat = FALSE;
1153 ulOfs = 0;
1154 while (xstrFindReplace(&str,
1155 &ulOfs,
1156 &strFind,
1157 &strReplace,
1158 shift, &fRepeat));
1159 ;
1160
1161 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1162
1163 xstrcpy(&strFind, ".", 0);
1164 xstrcpy(&strReplace, "*.........................*", 0);
1165
1166 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1167
1168 fRepeat = FALSE;
1169 ulOfs = 0;
1170 while (xstrFindReplace(&str,
1171 &ulOfs,
1172 &strFind,
1173 &strReplace,
1174 shift, &fRepeat));
1175 ;
1176
1177 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1178
1179 printf("Reserving extra mem.\n");
1180
1181 xstrReserve(&str, 6000);
1182 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1183
1184 xstrcpy(&strFind, "..........", 0);
1185 xstrcpy(&strReplace, "@", 0);
1186
1187 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1188
1189 fRepeat = FALSE;
1190 ulOfs = 0;
1191 while (xstrFindReplace(&str,
1192 &ulOfs,
1193 &strFind,
1194 &strReplace,
1195 shift, &fRepeat));
1196 ;
1197
1198 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1199
1200 return (0);
1201}
1202*/
1203
1204
Note: See TracBrowser for help on using the repository browser.