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

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

Updated timers.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 44.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 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#include <ctype.h>
109
110#include "setup.h" // code generation and debugging options
111
112#include "helpers\stringh.h"
113#include "helpers\xstring.h" // extended string helpers
114
115/*
116 *@@category: Helpers\C helpers\String management\XStrings (with memory management)
117 * See xstring.c.
118 */
119
120/*
121 *@@ xstrInit:
122 * initializes an empty XSTRING.
123 *
124 * If (ulPreAllocate != 0), memory is pre-allocated
125 * for the string, but the string will be empty.
126 * This is useful if you plan to add more stuff to
127 * the string later so we don't have to reallocate
128 * all the time in xstrcat.
129 *
130 * Do not use this on an XSTRING which is already
131 * initialized. Use xstrset instead.
132 *
133 *@@added V0.9.6 (2000-11-01) [umoeller]
134 */
135
136void xstrInit(PXSTRING pxstr, // in/out: string
137 ULONG ulPreAllocate) // in: if > 0, memory to allocate
138{
139 memset(pxstr, 0, sizeof(XSTRING));
140 if (ulPreAllocate)
141 {
142 pxstr->psz = (PSZ)malloc(ulPreAllocate);
143 pxstr->cbAllocated = ulPreAllocate;
144 // ulLength is still zero
145 *(pxstr->psz) = 0;
146 }
147}
148
149/*
150 *@@ xstrInitSet:
151 * this can be used instead of xstrInit if you
152 * have a free()'able string you want to initialize
153 * the XSTRING with.
154 *
155 * This does not create a copy of pszNew. Instead,
156 * pszNew is used as the member string in pxstr
157 * directly.
158 *
159 * Do not use this on an XSTRING which is already
160 * initialized. Use xstrset instead.
161 *
162 * Example:
163 *
164 + XSTRING str;
165 + xstrInitSet(&str, strdup("blah"));
166 *
167 *@@added V0.9.6 (2000-11-01) [umoeller]
168 */
169
170void xstrInitSet(PXSTRING pxstr,
171 PSZ pszNew)
172{
173 pxstr->psz = pszNew;
174 if (!pszNew)
175 {
176 pxstr->cbAllocated = 0;
177 pxstr->ulLength = 0;
178 }
179 else
180 {
181 pxstr->ulLength = strlen(pszNew);
182 pxstr->cbAllocated = pxstr->ulLength + 1;
183 }
184}
185
186/*
187 *@@ xstrInitCopy:
188 * this can be used instead of xstrInit if you
189 * want to initialize an XSTRING with a copy
190 * of an existing string. This is a shortcut
191 * for xstrInit() and then xstrcpy().
192 *
193 * As opposed to xstrInitSet, this does create
194 * a copy of pcszSource.
195 *
196 * Do not use this on an XSTRING which is already
197 * initialized. Use xstrcpy instead.
198 *
199 * Example:
200 *
201 + XSTRING str;
202 + xstrInitCopy(&str, "blah");
203 *
204 *@@added V0.9.6 (2000-11-01) [umoeller]
205 *@@changed V0.9.7 (2000-12-31) [umoeller]: added ulExtraAllocate
206 */
207
208void xstrInitCopy(PXSTRING pxstr,
209 const char *pcszSource,
210 ULONG ulExtraAllocate) // in: if > 0, extra memory to allocate
211{
212 if (pxstr)
213 {
214 memset(pxstr, 0, sizeof(XSTRING));
215
216 if (pcszSource)
217 pxstr->ulLength = strlen(pcszSource);
218
219 if (pxstr->ulLength)
220 {
221 // we do have a source string:
222 pxstr->cbAllocated = pxstr->ulLength + 1 + ulExtraAllocate;
223 pxstr->psz = (PSZ)malloc(pxstr->cbAllocated);
224 strcpy(pxstr->psz, pcszSource);
225 }
226 }
227}
228
229/*
230 *@@ xstrClear:
231 * clears the specified stack XSTRING and
232 * frees allocated memory.
233 *
234 * This is the reverse to xstrInit.
235 *
236 *@@added V0.9.6 (2000-11-01) [umoeller]
237 */
238
239void xstrClear(PXSTRING pxstr) // in/out: string
240{
241 if (pxstr->psz)
242 free(pxstr->psz);
243 memset(pxstr, 0, sizeof(XSTRING));
244}
245
246/*
247 *@@ xstrReserve:
248 * this function makes sure that the specified
249 * XSTRING has at least ulBytes bytes allocated.
250 *
251 * This function is useful if you plan to do
252 * a lot of string replacements or appends and
253 * want to avoid that the buffer is reallocated
254 * with each operation. Before those operations,
255 * call this function to make room for the operations.
256 *
257 * If ulBytes is smaller than the current allocation,
258 * this function does nothing.
259 *
260 * The XSTRING must be initialized before the
261 * call.
262 *
263 * Returns the new total no. of allocated bytes.
264 *
265 *@@added V0.9.7 (2001-01-07) [umoeller]
266 */
267
268ULONG xstrReserve(PXSTRING pxstr,
269 ULONG ulBytes)
270{
271 ULONG cbNeeded = ulBytes;
272
273 if (cbNeeded > pxstr->cbAllocated)
274 {
275 // we need more memory than we have previously
276 // allocated:
277 if (pxstr->cbAllocated)
278 // appendee already had memory:
279 // reallocate
280 pxstr->psz = (PSZ)realloc(pxstr->psz,
281 cbNeeded);
282 else
283 {
284 // appendee has no memory:
285 pxstr->psz = (PSZ)malloc(cbNeeded);
286 *(pxstr->psz) = 0;
287 }
288
289 pxstr->cbAllocated = cbNeeded;
290 // ulLength is unchanged
291 }
292
293 return (pxstr->cbAllocated);
294}
295
296/*
297 *@@ xstrCreate:
298 * allocates a new XSTRING from the heap
299 * and calls xstrInit on it.
300 *
301 * Always use xstrFree to free associated
302 * resources.
303 *
304 *@@added V0.9.6 (2000-11-01) [umoeller]
305 */
306
307PXSTRING xstrCreate(ULONG ulPreAllocate)
308{
309 PXSTRING pxstr = (PXSTRING)malloc(sizeof(XSTRING));
310 if (pxstr)
311 xstrInit(pxstr, ulPreAllocate);
312
313 return (pxstr);
314}
315
316/*
317 *@@ xstrFree:
318 * frees the specified heap XSTRING, which must
319 * have been created using xstrCreate.
320 *
321 *@@added V0.9.6 (2000-11-01) [umoeller]
322 */
323
324VOID xstrFree(PXSTRING pxstr) // in/out: string
325{
326 if (pxstr)
327 {
328 xstrClear(pxstr);
329 free(pxstr);
330 }
331}
332
333/*
334 *@@ xstrset:
335 * sets the specified XSTRING to a new string
336 * without copying it.
337 *
338 * pxstr is cleared before the new string is set.
339 *
340 * This ONLY works if pszNew has been allocated from
341 * the heap using malloc() or strdup() and is thus
342 * free()'able.
343 *
344 * This assumes that exactly strlen(pszNew) + 1
345 * bytes have been allocated for pszNew, which
346 * is true if pszNew comes from strdup().
347 *
348 *@@added V0.9.6 (2000-11-01) [umoeller]
349 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
350 */
351
352ULONG xstrset(PXSTRING pxstr, // in/out: string
353 PSZ pszNew) // in: heap PSZ to use
354{
355 if (!pxstr)
356 return (0); // V0.9.9 (2001-02-14) [umoeller]
357
358 xstrClear(pxstr);
359 pxstr->psz = pszNew;
360 if (pszNew)
361 {
362 pxstr->ulLength = strlen(pszNew);
363 pxstr->cbAllocated = pxstr->ulLength + 1;
364 }
365 // else null string: cbAllocated and ulLength are 0 already
366
367 return (pxstr->ulLength);
368}
369
370/*
371 *@@ xstrcpy:
372 * copies pcszSource to pxstr, for which memory is allocated
373 * as necessary.
374 *
375 * If pxstr contains something, its contents are destroyed.
376 *
377 * With ulSourceLength, specify the length of pcszSource.
378 * If you specify 0, this function will run strlen(pcszSource)
379 * itself.
380 *
381 * If you already know the length of pcszSource, you can
382 * speed this function up a bit this way.
383 *
384 * You are required to specify ulSourceLength if you only want
385 * to copy a substring, or pcszSource is not zero-terminated.
386 *
387 * Returns the length of the new string (excluding the null
388 * terminator), or null upon errors.
389 *
390 * Example:
391 *
392 + XSTRING str;
393 + xstrInit(&str, 0);
394 + xstrcpy(&str, "blah", 0);
395 *
396 * This sequence can be abbreviated using xstrInitCopy.
397 *
398 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcpy
399 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
400 *@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
401 *@@changed V0.9.9 (2001-01-28) [lafaix]: fixed memory leak and NULL source behavior
402 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
403 *@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
404 */
405
406ULONG xstrcpy(PXSTRING pxstr, // in/out: string
407 const char *pcszSource, // in: source, can be NULL
408 ULONG ulSourceLength) // in: length of pcszSource or 0
409{
410 // xstrClear(pxstr); NOOOO! this frees the string, we want to keep the memory
411
412 if (!pxstr)
413 return (0); // V0.9.9 (2001-02-14) [umoeller]
414
415 if (pcszSource)
416 {
417 // source specified:
418 if (ulSourceLength == 0)
419 // but not length:
420 ulSourceLength = strlen(pcszSource);
421 }
422 else
423 ulSourceLength = 0;
424
425 if (ulSourceLength)
426 {
427 // we do have a source string:
428 ULONG cbNeeded = ulSourceLength + 1;
429 if (cbNeeded > pxstr->cbAllocated)
430 {
431 // we need more memory than we have previously
432 // allocated:
433 if (pxstr->psz)
434 free(pxstr->psz); // V0.9.9 (2001-01-28) [lafaix]
435 pxstr->cbAllocated = cbNeeded;
436 pxstr->psz = (PSZ)malloc(cbNeeded);
437 }
438 // else: we have enough memory
439
440 memcpy(pxstr->psz,
441 pcszSource,
442 ulSourceLength);
443 *(pxstr->psz + ulSourceLength) = '\0';
444 // V0.9.9 (2001-02-16) [umoeller]
445 // we must do this or otherwise we require pcszSource
446 // to be zero-terminated... not a good idea
447 }
448 else
449 {
450 // no source specified or source is empty:
451 if (pxstr->cbAllocated)
452 // we did have a string: set to empty,
453 // but leave allocated memory intact
454 *(pxstr->psz) = 0;
455 // else
456 // we had no string previously: in that case
457 // psz and ulLength and cbAllocated are all still NULL
458 }
459
460 // in all cases, set new length
461 pxstr->ulLength = ulSourceLength;
462
463 return (pxstr->ulLength);
464}
465
466/*
467 *@@ xstrcpys:
468 * shortcut to xstrcpy if the source is an XSTRING also.
469 *
470 *@@added V0.9.9 (2001-02-14) [umoeller]
471 */
472
473ULONG xstrcpys(PXSTRING pxstr,
474 const XSTRING *pcstrSource)
475{
476 if (!pcstrSource)
477 return (0);
478
479 return (xstrcpy(pxstr, pcstrSource->psz, pcstrSource->ulLength));
480}
481
482/*
483 *@@ xstrcat:
484 * appends pcszSource to pxstr, for which memory is
485 * reallocated if necessary.
486 *
487 * If pxstr is empty, this behaves just like xstrcpy.
488 *
489 * With ulSourceLength, specify the length of pcszSource.
490 * If you specify 0, this function will run strlen(pcszSource)
491 * itself.
492 *
493 * If you already know the length of pcszSource, you can
494 * speed this function up a bit this way.
495 *
496 * You are required to specify ulSourceLength if you only want
497 * to copy a substring, or pcszSource is not zero-terminated.
498 *
499 * Returns the length of the new string (excluding the null
500 * terminator) if the string was changed, or 0 if nothing
501 * happened.
502 *
503 * Note: To append a single character, xstrcatc is faster
504 * than xstrcat.
505 *
506 * Example:
507 *
508 + XSTRING str;
509 + xstrInit(&str, 0);
510 + xstrcpy(&str, "blah", 0);
511 + xstrcat(&str, "blup", 0);
512 *
513 * After this, str.psz points to a new string containing
514 * "blahblup".
515 *
516 *@@changed V0.9.1 (99-12-20) [umoeller]: fixed memory leak
517 *@@changed V0.9.1 (2000-01-03) [umoeller]: crashed if pszString was null; fixed
518 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxcat
519 *@@changed V0.9.3 (2000-05-11) [umoeller]: returned 0 if pszString was initially empty; fixed
520 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
521 *@@changed V0.9.7 (2000-12-10) [umoeller]: return value was wrong
522 *@@changed V0.9.7 (2001-01-15) [umoeller]: added ulSourceLength
523 *@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
524 */
525
526ULONG xstrcat(PXSTRING pxstr, // in/out: string
527 const char *pcszSource, // in: source, can be NULL
528 ULONG ulSourceLength) // in: length of pcszSource or 0
529{
530 ULONG ulrc = 0;
531
532 if (pxstr)
533 {
534 if (pcszSource)
535 {
536 if (ulSourceLength == 0)
537 ulSourceLength = strlen(pcszSource);
538
539 if (ulSourceLength)
540 {
541 // we do have a source string:
542
543 // 1) memory management
544 ULONG cbNeeded = pxstr->ulLength + ulSourceLength + 1;
545 if (cbNeeded > pxstr->cbAllocated)
546 {
547 // we need more memory than we have previously
548 // allocated:
549 if (pxstr->cbAllocated)
550 // appendee already had memory:
551 // reallocate
552 pxstr->psz = (PSZ)realloc(pxstr->psz,
553 cbNeeded);
554 else
555 // appendee has no memory:
556 pxstr->psz = (PSZ)malloc(cbNeeded);
557
558 pxstr->cbAllocated = cbNeeded;
559 // ulLength is unchanged yet
560 }
561 // else: we have enough memory, both if appendee
562 // is empty or not empty
563
564 // now we have:
565 // -- if appendee (pxstr) had enough memory, no problem
566 // -- if appendee (pxstr) needed more memory
567 // -- and was not empty: pxstr->psz now points to a
568 // reallocated copy of the old string
569 // -- and was empty: pxstr->psz now points to a
570 // new (unitialized) buffer
571
572 // 2) append source string:
573 memcpy(pxstr->psz + pxstr->ulLength,
574 pcszSource,
575 ulSourceLength); // null terminator
576
577 *(pxstr->psz + pxstr->ulLength + ulSourceLength) = '\0';
578 // V0.9.9 (2001-02-16) [umoeller]
579 // we must do this or otherwise we require pcszSource
580 // to be zero-terminated... not a good idea
581
582 // in all cases, set new length
583 pxstr->ulLength += ulSourceLength;
584 ulrc = pxstr->ulLength; // V0.9.7 (2000-12-10) [umoeller]
585
586 } // end if (ulSourceLength)
587 }
588
589 // else no source specified or source is empty:
590 // do nothing
591 }
592
593 return (ulrc);
594}
595
596/*
597 *@@ xstrcatc:
598 * this is similar to xstrcat, except that this is
599 * for a single character. This is a bit faster than
600 * xstrcat.
601 *
602 * If "c" is \0, nothing happens.
603 *
604 * If pxstr is empty, this behaves just like xstrcpy.
605 *
606 * Returns the length of the new string (excluding the null
607 * terminator) if the string was changed, or 0 if nothing
608 * happened.
609 *
610 * Example:
611 *
612 + XSTRING str;
613 + xstrInit(&str, 0);
614 + xstrcpy(&str, "blu");
615 + xstrcatc(&str, 'p');
616 *
617 * After this, str.psz points to a new string containing
618 * "blup".
619 *
620 *@@added V0.9.7 (2000-12-10) [umoeller]
621 */
622
623ULONG xstrcatc(PXSTRING pxstr, // in/out: string
624 CHAR c) // in: character to append, can be \0
625{
626 ULONG ulrc = 0;
627
628 if ((pxstr) && (c))
629 {
630 // ULONG ulSourceLength = 1;
631 // 1) memory management
632 ULONG cbNeeded = pxstr->ulLength // existing length, without null terminator
633 + 1 // new character
634 + 1; // null terminator
635 if (cbNeeded > pxstr->cbAllocated)
636 {
637 // we need more memory than we have previously
638 // allocated:
639 if (pxstr->cbAllocated)
640 // appendee already had memory:
641 // reallocate
642 pxstr->psz = (PSZ)realloc(pxstr->psz,
643 cbNeeded);
644 else
645 // appendee has no memory:
646 pxstr->psz = (PSZ)malloc(cbNeeded);
647
648 pxstr->cbAllocated = cbNeeded;
649 // ulLength is unchanged yet
650 }
651 // else: we have enough memory, both if appendee
652 // is empty or not empty
653
654 // now we have:
655 // -- if appendee (pxstr) had enough memory, no problem
656 // -- if appendee (pxstr) needed more memory
657 // -- and was not empty: pxstr->psz now points to a
658 // reallocated copy of the old string
659 // -- and was empty: pxstr->psz now points to a
660 // new (unitialized) buffer
661
662 // 2) append character:
663 pxstr->psz[pxstr->ulLength] = c;
664 pxstr->psz[pxstr->ulLength + 1] = '\0';
665
666 // in all cases, set new length
667 pxstr->ulLength++;
668 ulrc = pxstr->ulLength;
669
670 } // end if ((pxstr) && (c))
671
672 return (ulrc);
673}
674
675/*
676 *@@ xstrcats:
677 * shortcut to xstrcat if the source is an XSTRING also.
678 *
679 *@@added V0.9.9 (2001-02-14) [umoeller]
680 */
681
682ULONG xstrcats(PXSTRING pxstr,
683 const XSTRING *pcstrSource)
684{
685 if (!pcstrSource)
686 return (0);
687
688 return (xstrcat(pxstr, pcstrSource->psz, pcstrSource->ulLength));
689}
690
691/*
692 *@@ xstrrpl:
693 * replaces cReplLen characters in pxstr, starting
694 * at the position ulFirstReplPos, with the string
695 * in pxstrReplaceWith.
696 *
697 * Returns the new length of the string, excluding
698 * the null terminator, or 0 if the replacement failed
699 * (e.g. because the offsets were too large).
700 *
701 * This has been extracted from xstrFindReplace because
702 * if you already know the position of a substring,
703 * you can now call this directly. This properly
704 * reallocates the string if more memory is needed.
705 *
706 * Example:
707 *
708 + XSTRING xstr, xstrReplacement;
709 + xstrInitCopy(&xstr, "This is a test string.");
710 + // positions: 0123456789012345678901
711 + // 1 2
712 +
713 + xstrInitCopy(&xstrReplacement, "stupid");
714 +
715 + xstrrpl(&xstr,
716 + 10, // position of "test"
717 + 4, // length of "test"
718 + &xstrReplacement);
719 *
720 * This would yield "This is a stupid string."
721 *
722 *@@added V0.9.7 (2001-01-15) [umoeller]
723 *@@changed V0.9.9 (2001-01-29) [lafaix]: fixed unnecessary allocation when pxstr was big enough
724 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
725 */
726
727ULONG xstrrpl(PXSTRING pxstr, // in/out: string
728 ULONG ulFirstReplOfs, // in: ofs of first char to replace
729 ULONG cReplLen, // in: no. of chars to replace
730 const XSTRING *pstrReplaceWith) // in: string to replace chars with
731{
732 ULONG ulrc = 0;
733
734 // security checks...
735 if ( (pxstr) // V0.9.9 (2001-02-14) [umoeller]
736 && (ulFirstReplOfs + cReplLen <= pxstr->ulLength)
737 && (pstrReplaceWith)
738 // && (pstrReplaceWith->ulLength) no, this can be empty
739 )
740 {
741 ULONG cReplaceLen = pstrReplaceWith->ulLength;
742 // can be 0!
743
744 // length of new string
745 ULONG cbNeeded = pxstr->ulLength
746 + cReplaceLen
747 - cReplLen
748 + 1; // null terminator
749 // offset where pszSearch was found
750 // ulFirstReplOfs = pFound - pxstr->psz; now ulFirstReplOfs
751 PSZ pFound = pxstr->psz + ulFirstReplOfs;
752
753 // now check if we have enough memory...
754 if (pxstr->cbAllocated < cbNeeded)
755 {
756 // no, we need more memory:
757 // allocate new buffer
758 PSZ pszNew = (PSZ)malloc(cbNeeded);
759
760 if (ulFirstReplOfs)
761 // "found" was not at the beginning:
762 // copy from beginning up to found-offset
763 memcpy(pszNew,
764 pxstr->psz,
765 ulFirstReplOfs); // up to "found"
766
767 if (cReplaceLen)
768 {
769 // we have a replacement:
770 // insert it next
771 memcpy(pszNew + ulFirstReplOfs,
772 pstrReplaceWith->psz,
773 cReplaceLen + 1); // include null terminator
774 }
775
776 // copy rest:
777 // pxstr frontFOUNDtail
778 // 0 1
779 // 01234567890123
780 // ³ ³ ³ ³
781 // ³ ³ ÀÄ ulFirstReplOfs + cReplLen = 10
782 // ³ ³ ³
783 // ³ ÀÄ ulFirstReplOfs = 5
784 // ³ ³
785 // pxstr->ulLength = 14
786 memcpy(pszNew + ulFirstReplOfs + cReplaceLen,
787 pFound + cReplLen,
788 // remaining bytes:
789 pxstr->ulLength - ulFirstReplOfs - cReplLen // 9
790 + 1); // null terminator
791
792 // replace old buffer with new one
793 free(pxstr->psz);
794 pxstr->psz = pszNew;
795 pxstr->ulLength = cbNeeded - 1;
796 pxstr->cbAllocated = cbNeeded;
797 } // end if (pxstr->cbAllocated < cbNeeded)
798 else
799 {
800 // we have enough memory left,
801 // we can just overwrite in the middle...
802 // fixed V0.9.9 (2001-01-29) [lafaix]
803
804 // calc length of string after "found"
805 ULONG cTailLength = pxstr->ulLength - ulFirstReplOfs - cReplLen;
806
807 // first, we move the end to its new location
808 // (memmove handles overlap if needed)
809 memmove(pFound + cReplaceLen,
810 pFound + cReplLen,
811 cTailLength + 1); // including null terminator
812
813 // now overwrite "found" in the middle
814 if (cReplaceLen)
815 {
816 memcpy(pFound,
817 pstrReplaceWith->psz,
818 cReplaceLen); // no null terminator
819 }
820
821 // that's it; adjust the string length now
822 pxstr->ulLength = cbNeeded - 1;
823 }
824
825 ulrc = cbNeeded - 1;
826 } // end checks
827
828 return (ulrc);
829}
830
831/*
832 *@@ xstrFindWord:
833 * searches for pstrFind in pxstr, starting at ulOfs.
834 * However, this only finds pstrFind if it's a "word",
835 * i.e. surrounded by one of the characters in the
836 * pcszBeginChars and pcszEndChars array.
837 *
838 * This is similar to strhFindWord, but this uses
839 * strhmemfind for fast searching, and it doesn't
840 * have to calculate the string lengths because these
841 * already in XSTRING.
842 *
843 * Returns 0 if no "word" was found, or the offset
844 * of the "word" in pxstr if found.
845 *
846 *@@added V0.9.6 (2000-11-12) [umoeller]
847 *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL string crashs
848 */
849
850PSZ xstrFindWord(const XSTRING *pxstr, // in: buffer to search ("haystack")
851 ULONG ulOfs, // in: where to begin search (0 = start)
852 const XSTRING *pstrFind, // in: word to find ("needle")
853 size_t *pShiftTable, // in: shift table (see strhmemfind)
854 PBOOL pfRepeatFind, // in: repeat find? (see strhmemfind)
855 const char *pcszBeginChars, // suggestion: "\x0d\x0a ()/\\-,."
856 const char *pcszEndChars) // suggestion: "\x0d\x0a ()/\\-,.:;"
857{
858 PSZ pReturn = 0;
859
860 if (pxstr && pstrFind) // V0.9.9 (2001-02-14) [umoeller]
861 {
862 ULONG ulFoundLen = pstrFind->ulLength;
863
864 if ((pxstr->ulLength) && (ulFoundLen))
865 {
866 const char *p = pxstr->psz + ulOfs;
867
868 do // while p
869 {
870 p = (PSZ)strhmemfind(p, // in: haystack
871 pxstr->ulLength - (p - pxstr->psz),
872 // remaining length of haystack
873 pstrFind->psz,
874 ulFoundLen,
875 pShiftTable,
876 pfRepeatFind);
877 if (p)
878 {
879 // string found:
880 // check if that's a word
881
882 if (strhIsWord(pxstr->psz,
883 p,
884 ulFoundLen,
885 pcszBeginChars,
886 pcszEndChars))
887 {
888 // valid end char:
889 pReturn = (PSZ)p;
890 break;
891 }
892
893 p += ulFoundLen;
894 }
895 } while (p);
896
897 }
898 }
899
900 return (pReturn);
901}
902
903/*
904 *@@ xstrFindReplace:
905 * replaces the first occurence of pstrSearch with
906 * pstrReplace in pxstr.
907 *
908 * Starting with V0.9.6, this operates entirely on
909 * XSTRING's for speed because we then know the string
910 * lengths already and can use memcpy instead of strcpy.
911 * This new version should be magnitudes faster,
912 * especially with large string bufffers.
913 *
914 * None of the pointers can be NULL, but if pstrReplace
915 * is empty, this effectively erases pstrSearch in pxstr.
916 *
917 * Returns the length of the new string (exclusing the
918 * null terminator) or 0 if pszSearch was not found
919 * (and pxstr was therefore not changed).
920 *
921 * This starts the search at *pulOfs. If
922 * (*pulOfs == 0), this starts from the beginning
923 * of pxstr.
924 *
925 * If the string was found, *pulOfs will be set to the
926 * first character after the new replacement string. This
927 * allows you to call this func again with the same strings
928 * to have several occurences replaced (see the example below).
929 *
930 * There are two wrappers around this function which
931 * work on C strings instead (however, thus losing the
932 * speed advantage):
933 *
934 * -- strhFindReplace operates on C strings only;
935 *
936 * -- xstrFindReplaceC uses C strings for the search and replace
937 * parameters.
938 *
939 * <B>Example usage:</B>
940 *
941 + XSTRING strBuf,
942 + strFind,
943 + strRepl;
944 + size_t ShiftTable[256];
945 + BOOL fRepeat = FALSE;
946 + ULONG ulOffset = 0;
947 +
948 + xstrInitCopy(&strBuf, "Test phrase 1. Test phrase 2.", 0);
949 + xstrInitSet(&strFind, "Test");
950 + xstrInitSet(&strRepl, "Dummy");
951 + while (xstrFindReplace(&str,
952 + &ulPos, // in/out: offset
953 + &strFind, // search
954 + &strRepl, // replace
955 + ShiftTable,
956 + &fRepeat))
957 + ;
958 *
959 * would replace all occurences of "Test" in str with
960 * "Dummy".
961 *
962 *@@changed V0.9.0 [umoeller]: totally rewritten.
963 *@@changed V0.9.0 (99-11-08) [umoeller]: crashed if *ppszBuf was NULL. Fixed.
964 *@@changed V0.9.2 (2000-04-01) [umoeller]: renamed from strhxrpl
965 *@@changed V0.9.6 (2000-11-01) [umoeller]: rewritten
966 *@@changed V0.9.6 (2000-11-12) [umoeller]: now using strhmemfind
967 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrrpl; extracted new xstrrpl
968 */
969
970ULONG xstrFindReplace(PXSTRING pxstr, // in/out: string
971 PULONG pulOfs, // in: where to begin search (0 = start);
972 // out: ofs of first char after replacement string
973 const XSTRING *pstrSearch, // in: search string; cannot be NULL
974 const XSTRING *pstrReplace, // in: replacement string; cannot be NULL
975 size_t *pShiftTable, // in: shift table (see strhmemfind)
976 PBOOL pfRepeatFind) // in: repeat find? (see strhmemfind)
977{
978 ULONG ulrc = 0; // default: not found
979
980 if ((pxstr) && (pstrSearch) && (pstrReplace))
981 {
982 ULONG cSearchLen = pstrSearch->ulLength;
983
984 // can we search this?
985 if ( (*pulOfs < pxstr->ulLength)
986 && (cSearchLen)
987 )
988 {
989 // yes:
990 ULONG ulOfs = *pulOfs;
991 const char *pFound
992 = (const char *)strhmemfind(pxstr->psz + ulOfs, // in: haystack
993 pxstr->ulLength - ulOfs,
994 pstrSearch->psz,
995 cSearchLen,
996 pShiftTable,
997 pfRepeatFind);
998 if (pFound)
999 {
1000 ULONG ulFirstReplOfs = pFound - pxstr->psz;
1001 // found in buffer from ofs:
1002 // replace pFound with pstrReplace
1003 ulrc = xstrrpl(pxstr,
1004 ulFirstReplOfs, // where to start
1005 cSearchLen, // chars to replace
1006 pstrReplace);
1007
1008 // return new length
1009 *pulOfs = ulFirstReplOfs + pstrReplace->ulLength;
1010 } // end if (pFound)
1011 } // end if ( (*pulOfs < pxstr->ulLength) ...
1012 } // end if ((pxstr) && (pstrSearch) && (pstrReplace))
1013
1014 return (ulrc);
1015}
1016
1017/*
1018 *@@ xstrFindReplaceC:
1019 * wrapper around xstrFindReplace() which allows using
1020 * C strings for the find and replace parameters.
1021 *
1022 * This creates two temporary XSTRING's for pcszSearch
1023 * and pcszReplace and thus cannot use the shift table
1024 * for repetitive searches. As a result, this is slower
1025 * than xstrFindReplace.
1026 *
1027 * If you search with the same strings several times,
1028 * you'll be better off using xstrFindReplace() directly.
1029 *
1030 *@@added V0.9.6 (2000-11-01) [umoeller]
1031 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from xstrcrpl
1032 */
1033
1034ULONG xstrFindReplaceC(PXSTRING pxstr, // in/out: string
1035 PULONG pulOfs, // in: where to begin search (0 = start);
1036 // out: ofs of first char after replacement string
1037 const char *pcszSearch, // in: search string; cannot be NULL
1038 const char *pcszReplace) // in: replacement string; cannot be NULL
1039{
1040 XSTRING xstrFind,
1041 xstrReplace;
1042 size_t ShiftTable[256];
1043 BOOL fRepeat = FALSE;
1044 // initialize find/replace strings... note that the
1045 // C strings are not free()'able, so we MUST NOT use xstrClear
1046 // before leaving
1047 xstrInitSet(&xstrFind, (PSZ)pcszSearch);
1048 xstrInitSet(&xstrReplace, (PSZ)pcszReplace);
1049
1050 return (xstrFindReplace(pxstr, pulOfs, &xstrFind, &xstrReplace, ShiftTable, &fRepeat));
1051}
1052
1053/*
1054 *@@ xstrEncode:
1055 * encodes characters in a string.
1056 *
1057 * This searches pxstr for all occurences of the
1058 * characters in pcszEncode (which must be a
1059 * null-terminated list of characters to be
1060 * encoded). Each occurence that is found is
1061 * replaced with "%hh", with "hh" being the
1062 * two-digit hex number of the encoded character.
1063 *
1064 * For example, to encode strings for the XCenter,
1065 * set pcszEncode to "%,();=".
1066 *
1067 * Returns the no. of characters replaced.
1068 *
1069 * NOTE: You must make sure that pcszEncode ALWAYS
1070 * contains the "%" character as well, which must
1071 * always be encoded (i.e. escaped) because it is
1072 * used for encoding the characters. Otherwise
1073 * you won't be able to decode the string again.
1074 *
1075 * Example: To encode all occurences of
1076 * "a", "b", and "c" in a string, do this:
1077 *
1078 + XSTRING str;
1079 + xstrInitCopy(&str, "Sample characters.";
1080 + xstrEncode(&str, "abc%";
1081 *
1082 * would convert str to contain:
1083 *
1084 + S%61mple %63hara%63ters.
1085 *
1086 *@@added V0.9.9 (2001-02-28) [umoeller]
1087 */
1088
1089ULONG xstrEncode(PXSTRING pxstr, // in/out: string to convert
1090 const char *pcszEncode) // in: characters to encode (e.g. "%,();=")
1091{
1092 ULONG ulrc = 0,
1093 ul;
1094
1095 // now encode the widget setup string...
1096 for (ul = 0;
1097 ul < strlen(pcszEncode);
1098 ul++)
1099 {
1100 CHAR szFind[3] = "?",
1101 szReplace[10] = "%xx";
1102 XSTRING strFind,
1103 strReplace;
1104 size_t ShiftTable[256];
1105 BOOL fRepeat = FALSE;
1106 ULONG ulOfs = 0;
1107
1108 // search string:
1109 szFind[0] = pcszEncode[ul];
1110 xstrInitSet(&strFind, szFind);
1111
1112 // replace string: ASCII encoding
1113 sprintf(szReplace, "%%%lX", pcszEncode[ul]);
1114 xstrInitSet(&strReplace, szReplace);
1115
1116 // replace all occurences
1117 while (xstrFindReplace(pxstr,
1118 &ulOfs,
1119 &strFind,
1120 &strReplace,
1121 ShiftTable,
1122 &fRepeat))
1123 ulrc++;
1124
1125 } // for ul; next encoding
1126
1127 return (ulrc);
1128}
1129
1130/*
1131 *@@ xstrDecode:
1132 * decodes a string previously encoded by xstrEncode.
1133 *
1134 * This simply assumes that all '%' characters in
1135 * pxstr contain encodings and the next two characters
1136 * after '%' always are a hex character code. This
1137 * only recognizes hex in upper case.
1138 *
1139 * Returns the no. of characters replaced.
1140 *
1141 *@@added V0.9.9 (2001-02-28) [umoeller]
1142 */
1143
1144ULONG xstrDecode(PXSTRING pxstr) // in/out: string to be decoded
1145{
1146 ULONG ulrc = 0;
1147
1148 if ( (pxstr)
1149 && (pxstr->ulLength)
1150 )
1151 {
1152 ULONG cbAllocated = pxstr->ulLength + 1;
1153 // decoded string cannot be longer than source
1154 PSZ pszDest = (PSZ)malloc(cbAllocated);
1155
1156 if (pszDest)
1157 {
1158 const char *pSource = pxstr->psz;
1159 PSZ pDest = pszDest;
1160
1161 CHAR c;
1162
1163 while ((c = *pSource++))
1164 {
1165 // pSource points to next char now
1166
1167 if (c == '%')
1168 {
1169 static char ach[] = "01234567989ABCDEF";
1170
1171 // convert two chars after '%'
1172 CHAR c2, // first char after '%'
1173 c3; // second char after '%'
1174 const char *p2, // for first char: points into ach or is NULL
1175 *p3; // for second char: points into ach or is NULL
1176 if ( (c2 = *pSource)
1177 && (p2 = strchr(ach, c2))
1178 && (c3 = *(pSource + 1))
1179 && (p3 = strchr(ach, c3))
1180 )
1181 {
1182 // both chars after '%' were valid:
1183 *pDest++ = (p2 - ach) // 0 for '0', 10 for 'A', ...
1184 + ((p3 - ach) << 4);
1185 // go on after that
1186 pSource += 2;
1187 // raise return count
1188 ulrc++;
1189 // next in loop
1190 continue;
1191 }
1192 }
1193
1194 // not encoding, or null after '%', or invalid encoding:
1195 // just copy this
1196 *pDest++ = c;
1197 } // while ((ch = *pSource++))
1198
1199 if (ulrc)
1200 {
1201 // any encodings found:
1202 // terminate target
1203 *pDest = 0;
1204
1205 // replace source with target
1206 free(pxstr->psz);
1207 pxstr->psz = pszDest;
1208 pxstr->cbAllocated = cbAllocated;
1209 pxstr->ulLength = (pDest - pszDest);
1210 }
1211 else
1212 // no encodings found:
1213 free(pszDest);
1214 }
1215 }
1216
1217 return (ulrc);
1218}
1219
1220/*
1221 *@@ xstrConvertLineFormat:
1222 * converts between line formats.
1223 *
1224 * If (fToCFormat == CRLF2LF), all \r\n pairs are replaced
1225 * with \n chars (UNIX or C format).
1226 *
1227 * Reversely, if (fToCFormat == LF2CRLF), all \n chars
1228 * are converted to \r\n pairs (DOS and OS/2 formats).
1229 * No check is made whether this has already been done.
1230 *
1231 *@@added V0.9.7 (2001-01-15) [umoeller]
1232 */
1233
1234VOID xstrConvertLineFormat(PXSTRING pxstr,
1235 BOOL fToCFormat) // in: if CRLF2LF, to C format; if LF2CRLF, to OS/2 format.
1236{
1237 XSTRING strFind,
1238 strRepl;
1239 size_t ShiftTable[256];
1240 BOOL fRepeat = FALSE;
1241 ULONG ulOfs = 0;
1242
1243 if (fToCFormat)
1244 {
1245 // OS/2 to C:
1246 xstrInitSet(&strFind, "\r\n");
1247 xstrInitSet(&strRepl, "\n");
1248 }
1249 else
1250 {
1251 // C to OS/2:
1252 xstrInitSet(&strFind, "\n");
1253 xstrInitSet(&strRepl, "\r\n");
1254 }
1255
1256 while (xstrFindReplace(pxstr,
1257 &ulOfs,
1258 &strFind,
1259 &strRepl,
1260 ShiftTable,
1261 &fRepeat))
1262 ;
1263}
1264
1265// test case
1266
1267/* int main(void)
1268{
1269 XSTRING str,
1270 strFind,
1271 strReplace;
1272 size_t shift[256];
1273 BOOL fRepeat = FALSE;
1274 ULONG ulOfs = 0;
1275
1276 xstrInit(&str, 100);
1277 xstrInit(&strFind, 0);
1278 xstrInit(&strReplace, 0);
1279
1280 xstrcpy(&str, "Test string 1. Test string 2. Test string 3. !", 0);
1281 xstrcpy(&strFind, "Test", 0);
1282 xstrcpy(&strReplace, "Dummy", 0);
1283
1284 printf("Old string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1285
1286 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1287
1288 fRepeat = FALSE;
1289 ulOfs = 0;
1290 while (xstrFindReplace(&str,
1291 &ulOfs,
1292 &strFind,
1293 &strReplace,
1294 shift, &fRepeat));
1295 ;
1296
1297 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1298
1299 xstrcpy(&strFind, strReplace.psz, 0);
1300 xstrClear(&strReplace);
1301
1302 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1303
1304 fRepeat = FALSE;
1305 ulOfs = 0;
1306 while (xstrFindReplace(&str,
1307 &ulOfs,
1308 &strFind,
1309 &strReplace,
1310 shift, &fRepeat));
1311 ;
1312
1313 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1314
1315 xstrcpy(&strFind, " ", 0);
1316 xstrcpy(&strReplace, ".", 0);
1317
1318 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1319
1320 fRepeat = FALSE;
1321 ulOfs = 0;
1322 while (xstrFindReplace(&str,
1323 &ulOfs,
1324 &strFind,
1325 &strReplace,
1326 shift, &fRepeat));
1327 ;
1328
1329 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1330
1331 xstrcpy(&strFind, ".", 0);
1332 xstrcpy(&strReplace, "*.........................*", 0);
1333
1334 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1335
1336 fRepeat = FALSE;
1337 ulOfs = 0;
1338 while (xstrFindReplace(&str,
1339 &ulOfs,
1340 &strFind,
1341 &strReplace,
1342 shift, &fRepeat));
1343 ;
1344
1345 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1346
1347 printf("Reserving extra mem.\n");
1348
1349 xstrReserve(&str, 6000);
1350 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1351
1352 xstrcpy(&strFind, "..........", 0);
1353 xstrcpy(&strReplace, "@", 0);
1354
1355 printf("Replacing \"%s\" with \"%s\".\n", strFind.psz, strReplace.psz);
1356
1357 fRepeat = FALSE;
1358 ulOfs = 0;
1359 while (xstrFindReplace(&str,
1360 &ulOfs,
1361 &strFind,
1362 &strReplace,
1363 shift, &fRepeat));
1364 ;
1365
1366 printf("New string is: \"%s\" (%d/%d)\n", str.psz, str.ulLength, str.cbAllocated);
1367
1368 return (0);
1369}
1370*/
1371
1372
Note: See TracBrowser for help on using the repository browser.