e2dae77306fc523734fb38282cc505de2b34f8aa
[citadel.git] / libcitadel / lib / stringbuf.c
1 #include <ctype.h>
2 #include <errno.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <sys/select.h>
8 #include <fcntl.h>
9 #define SHOW_ME_VAPPEND_PRINTF
10 #include <stdarg.h>
11 #include "libcitadel.h"
12
13
14 /**
15  * Private Structure for the Stringbuffer
16  */
17 struct StrBuf {
18         char *buf;         /**< the pointer to the dynamic buffer */
19         long BufSize;      /**< how many spcae do we optain */
20         long BufUsed;      /**< Number of Chars used excluding the trailing \0 */
21         int ConstBuf;      /**< are we just a wrapper arround a static buffer and musn't we be changed? */
22 };
23
24
25 /** 
26  * \Brief Cast operator to Plain String 
27  * Note: if the buffer is altered by StrBuf operations, this pointer may become 
28  *  invalid. So don't lean on it after altering the buffer!
29  *  Since this operation is considered cheap, rather call it often than risking
30  *  your pointer to become invalid!
31  * \param Str the string we want to get the c-string representation for
32  * \returns the Pointer to the Content. Don't mess with it!
33  */
34 inline const char *ChrPtr(const StrBuf *Str)
35 {
36         if (Str == NULL)
37                 return "";
38         return Str->buf;
39 }
40
41 /**
42  * \brief since we know strlen()'s result, provide it here.
43  * \param Str the string to return the length to
44  * \returns contentlength of the buffer
45  */
46 inline int StrLength(const StrBuf *Str)
47 {
48         return (Str != NULL) ? Str->BufUsed : 0;
49 }
50
51 /**
52  * \brief local utility function to resize the buffer
53  * \param Buf the buffer whichs storage we should increase
54  * \param KeepOriginal should we copy the original buffer or just start over with a new one
55  * \param DestSize what should fit in after?
56  */
57 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
58 {
59         char *NewBuf;
60         size_t NewSize = Buf->BufSize * 2;
61
62         if (Buf->ConstBuf)
63                 return -1;
64                 
65         if (DestSize > 0)
66                 while (NewSize < DestSize)
67                         NewSize *= 2;
68
69         NewBuf= (char*) malloc(NewSize);
70         if (KeepOriginal && (Buf->BufUsed > 0))
71         {
72                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
73         }
74         else
75         {
76                 NewBuf[0] = '\0';
77                 Buf->BufUsed = 0;
78         }
79         free (Buf->buf);
80         Buf->buf = NewBuf;
81         Buf->BufSize *= 2;
82         return Buf->BufSize;
83 }
84
85 /**
86  * Allocate a new buffer with default buffer size
87  * \returns the new stringbuffer
88  */
89 StrBuf* NewStrBuf(void)
90 {
91         StrBuf *NewBuf;
92
93         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
94         NewBuf->buf = (char*) malloc(SIZ);
95         NewBuf->buf[0] = '\0';
96         NewBuf->BufSize = SIZ;
97         NewBuf->BufUsed = 0;
98         NewBuf->ConstBuf = 0;
99         return NewBuf;
100 }
101
102 /** 
103  * \brief Copy Constructor; returns a duplicate of CopyMe
104  * \params CopyMe Buffer to faxmilate
105  * \returns the new stringbuffer
106  */
107 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
108 {
109         StrBuf *NewBuf;
110         
111         if (CopyMe == NULL)
112                 return NewStrBuf();
113
114         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
115         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
116         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
117         NewBuf->BufUsed = CopyMe->BufUsed;
118         NewBuf->BufSize = CopyMe->BufSize;
119         NewBuf->ConstBuf = 0;
120         return NewBuf;
121 }
122
123 /**
124  * \brief create a new Buffer using an existing c-string
125  * this function should also be used if you want to pre-suggest
126  * the buffer size to allocate in conjunction with ptr == NULL
127  * \param ptr the c-string to copy; may be NULL to create a blank instance
128  * \param nChars How many chars should we copy; -1 if we should measure the length ourselves
129  * \returns the new stringbuffer
130  */
131 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
132 {
133         StrBuf *NewBuf;
134         size_t Siz = SIZ;
135         size_t CopySize;
136
137         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
138         if (nChars < 0)
139                 CopySize = strlen((ptr != NULL)?ptr:"");
140         else
141                 CopySize = nChars;
142
143         while (Siz <= CopySize)
144                 Siz *= 2;
145
146         NewBuf->buf = (char*) malloc(Siz);
147         NewBuf->BufSize = Siz;
148         if (ptr != NULL) {
149                 memcpy(NewBuf->buf, ptr, CopySize);
150                 NewBuf->buf[CopySize] = '\0';
151                 NewBuf->BufUsed = CopySize;
152         }
153         else {
154                 NewBuf->buf[0] = '\0';
155                 NewBuf->BufUsed = 0;
156         }
157         NewBuf->ConstBuf = 0;
158         return NewBuf;
159 }
160
161 /**
162  * \brief Set an existing buffer from a c-string
163  * \param ptr c-string to put into 
164  * \param nChars set to -1 if we should work 0-terminated
165  * \returns the new length of the string
166  */
167 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
168 {
169         size_t Siz = Buf->BufSize;
170         size_t CopySize;
171
172         if (nChars < 0)
173                 CopySize = strlen(ptr);
174         else
175                 CopySize = nChars;
176
177         while (Siz <= CopySize)
178                 Siz *= 2;
179
180         if (Siz != Buf->BufSize)
181                 IncreaseBuf(Buf, 0, Siz);
182         memcpy(Buf->buf, ptr, CopySize);
183         Buf->buf[CopySize] = '\0';
184         Buf->BufUsed = CopySize;
185         Buf->ConstBuf = 0;
186         return CopySize;
187 }
188
189
190 /**
191  * \brief use strbuf as wrapper for a string constant for easy handling
192  * \param StringConstant a string to wrap
193  * \param SizeOfConstant should be sizeof(StringConstant)-1
194  */
195 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
196 {
197         StrBuf *NewBuf;
198
199         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
200         NewBuf->buf = (char*) StringConstant;
201         NewBuf->BufSize = SizeOfStrConstant;
202         NewBuf->BufUsed = SizeOfStrConstant;
203         NewBuf->ConstBuf = 1;
204         return NewBuf;
205 }
206
207
208 /**
209  * \brief flush the content of a Buf; keep its struct
210  * \param buf Buffer to flush
211  */
212 int FlushStrBuf(StrBuf *buf)
213 {
214         if (buf->ConstBuf)
215                 return -1;       
216         buf->buf[0] ='\0';
217         buf->BufUsed = 0;
218         return 0;
219 }
220
221 /**
222  * \brief Release a Buffer
223  * Its a double pointer, so it can NULL your pointer
224  * so fancy SIG11 appear instead of random results
225  * \param FreeMe Pointer Pointer to the buffer to free
226  */
227 void FreeStrBuf (StrBuf **FreeMe)
228 {
229         if (*FreeMe == NULL)
230                 return;
231         if (!(*FreeMe)->ConstBuf) 
232                 free((*FreeMe)->buf);
233         free(*FreeMe);
234         *FreeMe = NULL;
235 }
236
237 /**
238  * \brief Release the buffer
239  * If you want put your StrBuf into a Hash, use this as Destructor.
240  * \param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
241  */
242 void HFreeStrBuf (void *VFreeMe)
243 {
244         StrBuf *FreeMe = (StrBuf*)VFreeMe;
245         if (FreeMe == NULL)
246                 return;
247         if (!FreeMe->ConstBuf) 
248                 free(FreeMe->buf);
249         free(FreeMe);
250 }
251
252 /**
253  * \brief Wrapper around atol
254  */
255 long StrTol(const StrBuf *Buf)
256 {
257         if(Buf->BufUsed > 0)
258                 return atol(Buf->buf);
259         else
260                 return 0;
261 }
262
263 /**
264  * \brief Wrapper around atoi
265  */
266 int StrToi(const StrBuf *Buf)
267 {
268         if(Buf->BufUsed > 0)
269                 return atoi(Buf->buf);
270         else
271                 return 0;
272 }
273
274 /**
275  * \brief modifies a Single char of the Buf
276  * You can point to it via char* or a zero-based integer
277  * \param ptr char* to zero; use NULL if unused
278  * \param nThChar zero based pointer into the string; use -1 if unused
279  * \param PeekValue The Character to place into the position
280  */
281 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
282 {
283         if (Buf == NULL)
284                 return -1;
285         if (ptr != NULL)
286                 nThChar = ptr - Buf->buf;
287         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
288                 return -1;
289         Buf->buf[nThChar] = PeekValue;
290         return nThChar;
291 }
292
293 /**
294  * \brief Append a StringBuffer to the buffer
295  * \param Buf Buffer to modify
296  * \param AppendBuf Buffer to copy at the end of our buffer
297  * \param Offset Should we start copying from an offset?
298  */
299 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, size_t Offset)
300 {
301         if ((AppendBuf == NULL) || (Buf == NULL))
302                 return;
303
304         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed)
305                 IncreaseBuf(Buf, 
306                             (Buf->BufUsed > 0), 
307                             AppendBuf->BufUsed + Buf->BufUsed);
308
309         memcpy(Buf->buf + Buf->BufUsed, 
310                AppendBuf->buf + Offset, 
311                AppendBuf->BufUsed - Offset);
312         Buf->BufUsed += AppendBuf->BufUsed - Offset;
313         Buf->buf[Buf->BufUsed] = '\0';
314 }
315
316
317 /**
318  * \brief Append a C-String to the buffer
319  * \param Buf Buffer to modify
320  * \param AppendBuf Buffer to copy at the end of our buffer
321  * \param AppendSize number of bytes to copy; set to -1 if we should count it in advance
322  * \param Offset Should we start copying from an offset?
323  */
324 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, size_t Offset)
325 {
326         long aps;
327
328         if ((AppendBuf == NULL) || (Buf == NULL))
329                 return;
330
331         if (AppendSize < 0 )
332                 aps = strlen(AppendBuf + Offset);
333         else
334                 aps = AppendSize - Offset;
335
336         if (Buf->BufSize < Buf->BufUsed + aps)
337                 IncreaseBuf(Buf, (Buf->BufUsed > 0), Buf->BufUsed + aps);
338
339         memcpy(Buf->buf + Buf->BufUsed, 
340                AppendBuf + Offset, 
341                aps);
342         Buf->BufUsed += aps;
343         Buf->buf[Buf->BufUsed] = '\0';
344 }
345
346
347 /** 
348  * \brief Escape a string for feeding out as a URL while appending it to a Buffer
349  * \param outbuf the output buffer
350  * \param oblen the size of outbuf to sanitize
351  * \param strbuf the input buffer
352  */
353 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
354 {
355         const char *pch, *pche;
356         char *pt, *pte;
357         int b, c, len;
358         const char ec[] = " +#&;`'|*?-~<>^()[]{}/$\"\\";
359         int eclen = sizeof(ec) -1;
360
361         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
362                 return;
363         if (PlainIn != NULL) {
364                 len = strlen(PlainIn);
365                 pch = PlainIn;
366                 pche = pch + len;
367         }
368         else {
369                 pch = In->buf;
370                 pche = pch + In->BufUsed;
371                 len = In->BufUsed;
372         }
373
374         if (len == 0) 
375                 return;
376
377         pt = OutBuf->buf + OutBuf->BufUsed;
378         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
379
380         while (pch < pche) {
381                 if (pt >= pte) {
382                         IncreaseBuf(OutBuf, 1, -1);
383                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
384                         pt = OutBuf->buf + OutBuf->BufUsed;
385                 }
386                 
387                 c = 0;
388                 for (b = 0; b < eclen; ++b) {
389                         if (*pch == ec[b]) {
390                                 c = 1;
391                                 b += eclen;
392                         }
393                 }
394                 if (c == 1) {
395                         sprintf(pt,"%%%02X", *pch);
396                         pt += 3;
397                         OutBuf->BufUsed += 3;
398                         pch ++;
399                 }
400                 else {
401                         *(pt++) = *(pch++);
402                         OutBuf->BufUsed++;
403                 }
404         }
405         *pt = '\0';
406 }
407
408 /*
409  * \brief Append a string, escaping characters which have meaning in HTML.  
410  *
411  * \param Target        target buffer
412  * \param Source        source buffer; set to NULL if you just have a C-String
413  * \param PlainIn       Plain-C string to append; set to NULL if unused
414  * \param nbsp          If nonzero, spaces are converted to non-breaking spaces.
415  * \param nolinebreaks  if set, linebreaks are removed from the string.
416  */
417 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
418 {
419         const char *aptr, *eiptr;
420         char *bptr, *eptr;
421         long len;
422
423         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
424                 return -1;
425
426         if (PlainIn != NULL) {
427                 aptr = PlainIn;
428                 len = strlen(PlainIn);
429                 eiptr = aptr + len;
430         }
431         else {
432                 aptr = Source->buf;
433                 eiptr = aptr + Source->BufUsed;
434                 len = Source->BufUsed;
435         }
436
437         if (len == 0) 
438                 return -1;
439
440         bptr = Target->buf + Target->BufUsed;
441         eptr = Target->buf + Target->BufSize - 6; /* our biggest unit to put in...  */
442
443         while (aptr < eiptr){
444                 if(bptr >= eptr) {
445                         IncreaseBuf(Target, 1, -1);
446                         eptr = Target->buf + Target->BufSize - 6; 
447                         bptr = Target->buf + Target->BufUsed;
448                 }
449                 if (*aptr == '<') {
450                         memcpy(bptr, "&lt;", 4);
451                         bptr += 4;
452                         Target->BufUsed += 4;
453                 }
454                 else if (*aptr == '>') {
455                         memcpy(bptr, "&gt;", 4);
456                         bptr += 4;
457                         Target->BufUsed += 4;
458                 }
459                 else if (*aptr == '&') {
460                         memcpy(bptr, "&amp;", 5);
461                         bptr += 5;
462                         Target->BufUsed += 5;
463                 }
464                 else if (*aptr == '\"') {
465                         memcpy(bptr, "&quot;", 6);
466                         bptr += 6;
467                         Target->BufUsed += 6;
468                 }
469                 else if (*aptr == '\'') {
470                         memcpy(bptr, "&#39;", 5);
471                         bptr += 5;
472                         Target->BufUsed += 5;
473                 }
474                 else if (*aptr == LB) {
475                         *bptr = '<';
476                         bptr ++;
477                         Target->BufUsed ++;
478                 }
479                 else if (*aptr == RB) {
480                         *bptr = '>';
481                         bptr ++;
482                         Target->BufUsed ++;
483                 }
484                 else if (*aptr == QU) {
485                         *bptr ='"';
486                         bptr ++;
487                         Target->BufUsed ++;
488                 }
489                 else if ((*aptr == 32) && (nbsp == 1)) {
490                         memcpy(bptr, "&nbsp;", 6);
491                         bptr += 6;
492                         Target->BufUsed += 6;
493                 }
494                 else if ((*aptr == '\n') && (nolinebreaks)) {
495                         *bptr='\0';     /* nothing */
496                 }
497                 else if ((*aptr == '\r') && (nolinebreaks)) {
498                         *bptr='\0';     /* nothing */
499                 }
500                 else{
501                         *bptr = *aptr;
502                         bptr++;
503                         Target->BufUsed ++;
504                 }
505                 aptr ++;
506         }
507         *bptr = '\0';
508         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
509                 return -1;
510         return Target->BufUsed;
511 }
512
513 /*
514  * \brief Append a string, escaping characters which have meaning in HTML.  
515  * Converts linebreaks into blanks; escapes single quotes
516  * \param Target        target buffer
517  * \param Source        source buffer; set to NULL if you just have a C-String
518  * \param PlainIn       Plain-C string to append; set to NULL if unused
519  */
520 void StrMsgEscAppend(StrBuf *Target, StrBuf *Source, const char *PlainIn)
521 {
522         const char *aptr, *eiptr;
523         char *tptr, *eptr;
524         long len;
525
526         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
527                 return ;
528
529         if (PlainIn != NULL) {
530                 aptr = PlainIn;
531                 len = strlen(PlainIn);
532                 eiptr = aptr + len;
533         }
534         else {
535                 aptr = Source->buf;
536                 eiptr = aptr + Source->BufUsed;
537                 len = Source->BufUsed;
538         }
539
540         if (len == 0) 
541                 return;
542
543         eptr = Target->buf + Target->BufSize - 6; 
544         tptr = Target->buf + Target->BufUsed;
545         
546         while (aptr < eiptr){
547                 if(tptr >= eptr) {
548                         IncreaseBuf(Target, 1, -1);
549                         eptr = Target->buf + Target->BufSize - 6; 
550                         tptr = Target->buf + Target->BufUsed;
551                 }
552                
553                 if (*aptr == '\n') {
554                         *tptr = ' ';
555                         Target->BufUsed++;
556                 }
557                 else if (*aptr == '\r') {
558                         *tptr = ' ';
559                         Target->BufUsed++;
560                 }
561                 else if (*aptr == '\'') {
562                         *(tptr++) = '&';
563                         *(tptr++) = '#';
564                         *(tptr++) = '3';
565                         *(tptr++) = '9';
566                         *tptr = ';';
567                         Target->BufUsed += 5;
568                 } else {
569                         *tptr = *aptr;
570                         Target->BufUsed++;
571                 }
572                 tptr++; aptr++;
573         }
574         *tptr = '\0';
575 }
576
577
578 /**
579  * \brief extracts a substring from Source into dest
580  * \param dest buffer to place substring into
581  * \param Source string to copy substring from
582  * \param Offset chars to skip from start
583  * \param nChars number of chars to copy
584  * \returns the number of chars copied; may be different from nChars due to the size of Source
585  */
586 int StrBufSub(StrBuf *dest, const StrBuf *Source, size_t Offset, size_t nChars)
587 {
588         size_t NCharsRemain;
589         if (Offset > Source->BufUsed)
590         {
591                 FlushStrBuf(dest);
592                 return 0;
593         }
594         if (Offset + nChars < Source->BufUsed)
595         {
596                 if (nChars > dest->BufSize)
597                         IncreaseBuf(dest, 0, nChars + 1);
598                 memcpy(dest->buf, Source->buf + Offset, nChars);
599                 dest->BufUsed = nChars;
600                 dest->buf[dest->BufUsed] = '\0';
601                 return nChars;
602         }
603         NCharsRemain = Source->BufUsed - Offset;
604         if (NCharsRemain > dest->BufSize)
605                 IncreaseBuf(dest, 0, NCharsRemain + 1);
606         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
607         dest->BufUsed = NCharsRemain;
608         dest->buf[dest->BufUsed] = '\0';
609         return NCharsRemain;
610 }
611
612 /**
613  * \brief sprintf like function appending the formated string to the buffer
614  * vsnprintf version to wrap into own calls
615  * \param Buf Buffer to extend by format and params
616  * \param format printf alike format to add
617  * \param ap va_list containing the items for format
618  */
619 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
620 {
621         va_list apl;
622         size_t BufSize = Buf->BufSize;
623         size_t nWritten = Buf->BufSize + 1;
624         size_t Offset = Buf->BufUsed;
625         size_t newused = Offset + nWritten;
626         
627         while (newused >= BufSize) {
628                 va_copy(apl, ap);
629                 nWritten = vsnprintf(Buf->buf + Offset, 
630                                      Buf->BufSize - Offset, 
631                                      format, apl);
632                 va_end(apl);
633                 newused = Offset + nWritten;
634                 if (newused >= Buf->BufSize) {
635                         IncreaseBuf(Buf, 1, newused);
636                 }
637                 else {
638                         Buf->BufUsed = Offset + nWritten;
639                         BufSize = Buf->BufSize;
640                 }
641
642         }
643 }
644
645 /**
646  * \brief sprintf like function appending the formated string to the buffer
647  * \param Buf Buffer to extend by format and params
648  * \param format printf alike format to add
649  * \param ap va_list containing the items for format
650  */
651 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
652 {
653         size_t BufSize = Buf->BufSize;
654         size_t nWritten = Buf->BufSize + 1;
655         size_t Offset = Buf->BufUsed;
656         size_t newused = Offset + nWritten;
657         va_list arg_ptr;
658         
659         while (newused >= BufSize) {
660                 va_start(arg_ptr, format);
661                 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, 
662                                      Buf->BufSize - Buf->BufUsed, 
663                                      format, arg_ptr);
664                 va_end(arg_ptr);
665                 newused = Buf->BufUsed + nWritten;
666                 if (newused >= Buf->BufSize) {
667                         IncreaseBuf(Buf, 1, newused);
668                 }
669                 else {
670                         Buf->BufUsed += nWritten;
671                         BufSize = Buf->BufSize;
672                 }
673
674         }
675 }
676
677 /**
678  * \brief sprintf like function putting the formated string into the buffer
679  * \param Buf Buffer to extend by format and params
680  * \param format printf alike format to add
681  * \param ap va_list containing the items for format
682  */
683 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
684 {
685         size_t nWritten = Buf->BufSize + 1;
686         va_list arg_ptr;
687         
688         while (nWritten >= Buf->BufSize) {
689                 va_start(arg_ptr, format);
690                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
691                 va_end(arg_ptr);
692                 Buf->BufUsed = nWritten ;
693                 if (nWritten >= Buf->BufSize)
694                         IncreaseBuf(Buf, 0, 0);
695         }
696 }
697
698
699 /**
700  * \brief Counts the numbmer of tokens in a buffer
701  * \param Source String to count tokens in
702  * \param tok    Tokenizer char to count
703  * \returns numbers of tokenizer chars found
704  */
705 inline int StrBufNum_tokens(const StrBuf *source, char tok)
706 {
707         if (source == NULL)
708                 return 0;
709         return num_tokens(source->buf, tok);
710 }
711
712 /*
713  * remove_token() - a tokenizer that kills, maims, and destroys
714  */
715 /**
716  * \brief a string tokenizer
717  * \param Source StringBuffer to read into
718  * \param parmnum n'th parameter to remove
719  * \param separator tokenizer param
720  * \returns -1 if not found, else length of token.
721  */
722 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
723 {
724         int ReducedBy;
725         char *d, *s;            /* dest, source */
726         int count = 0;
727
728         /* Find desired parameter */
729         d = Source->buf;
730         while (count < parmnum) {
731                 /* End of string, bail! */
732                 if (!*d) {
733                         d = NULL;
734                         break;
735                 }
736                 if (*d == separator) {
737                         count++;
738                 }
739                 d++;
740         }
741         if (!d) return 0;               /* Parameter not found */
742
743         /* Find next parameter */
744         s = d;
745         while (*s && *s != separator) {
746                 s++;
747         }
748
749         ReducedBy = d - s;
750
751         /* Hack and slash */
752         if (*s) {
753                 memmove(d, s, Source->BufUsed - (s - Source->buf));
754                 Source->BufUsed -= (ReducedBy + 1);
755         }
756         else if (d == Source->buf) {
757                 *d = 0;
758                 Source->BufUsed = 0;
759         }
760         else {
761                 *--d = 0;
762                 Source->BufUsed -= (ReducedBy + 1);
763         }
764         /*
765         while (*s) {
766                 *d++ = *s++;
767         }
768         *d = 0;
769         */
770         return ReducedBy;
771 }
772
773
774 /**
775  * \brief a string tokenizer
776  * \param dest Destination StringBuffer
777  * \param Source StringBuffer to read into
778  * \param parmnum n'th parameter to extract
779  * \param separator tokenizer param
780  * \returns -1 if not found, else length of token.
781  */
782 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
783 {
784         const char *s, *e;              //* source * /
785         int len = 0;                    //* running total length of extracted string * /
786         int current_token = 0;          //* token currently being processed * /
787
788         if ((Source == NULL) || (Source->BufUsed ==0)) {
789                 return(-1);
790         }
791         s = Source->buf;
792         e = s + Source->BufUsed;
793         if (dest == NULL) {
794                 return(-1);
795         }
796
797         //cit_backtrace();
798         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
799         dest->buf[0] = '\0';
800         dest->BufUsed = 0;
801
802         while ((s<e) && !IsEmptyStr(s)) {
803                 if (*s == separator) {
804                         ++current_token;
805                 }
806                 if (len >= dest->BufSize)
807                         if (!IncreaseBuf(dest, 1, -1))
808                                 break;
809                 if ( (current_token == parmnum) && 
810                      (*s != separator)) {
811                         dest->buf[len] = *s;
812                         ++len;
813                 }
814                 else if (current_token > parmnum) {
815                         break;
816                 }
817                 ++s;
818         }
819         
820         dest->buf[len] = '\0';
821         dest->BufUsed = len;
822                 
823         if (current_token < parmnum) {
824                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
825                 return(-1);
826         }
827         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
828         return(len);
829 }
830
831
832 /**
833  * \brief a string tokenizer to fetch an integer
834  * \param dest Destination StringBuffer
835  * \param parmnum n'th parameter to extract
836  * \param separator tokenizer param
837  * \returns 0 if not found, else integer representation of the token
838  */
839 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
840 {
841         StrBuf tmp;
842         char buf[64];
843         
844         tmp.buf = buf;
845         buf[0] = '\0';
846         tmp.BufSize = 64;
847         tmp.BufUsed = 0;
848         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
849                 return(atoi(buf));
850         else
851                 return 0;
852 }
853
854 /**
855  * \brief a string tokenizer to fetch a long integer
856  * \param dest Destination StringBuffer
857  * \param parmnum n'th parameter to extract
858  * \param separator tokenizer param
859  * \returns 0 if not found, else long integer representation of the token
860  */
861 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
862 {
863         StrBuf tmp;
864         char buf[64];
865         
866         tmp.buf = buf;
867         buf[0] = '\0';
868         tmp.BufSize = 64;
869         tmp.BufUsed = 0;
870         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
871                 return(atoi(buf));
872         else
873                 return 0;
874 }
875
876
877 /**
878  * \brief a string tokenizer to fetch an unsigned long
879  * \param dest Destination StringBuffer
880  * \param parmnum n'th parameter to extract
881  * \param separator tokenizer param
882  * \returns 0 if not found, else unsigned long representation of the token
883  */
884 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
885 {
886         StrBuf tmp;
887         char buf[64];
888         char *pnum;
889         
890         tmp.buf = buf;
891         buf[0] = '\0';
892         tmp.BufSize = 64;
893         tmp.BufUsed = 0;
894         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
895                 pnum = &buf[0];
896                 if (*pnum == '-')
897                         pnum ++;
898                 return (unsigned long) atol(pnum);
899         }
900         else 
901                 return 0;
902 }
903
904
905
906 /**
907  * \brief Read a line from socket
908  * flushes and closes the FD on error
909  * \param buf the buffer to get the input to
910  * \param fd pointer to the filedescriptor to read
911  * \param append Append to an existing string or replace?
912  * \param Error strerror() on error 
913  * \returns numbers of chars read
914  */
915 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
916 {
917         int len, rlen, slen;
918
919         if (!append)
920                 FlushStrBuf(buf);
921
922         slen = len = buf->BufUsed;
923         while (1) {
924                 rlen = read(*fd, &buf->buf[len], 1);
925                 if (rlen < 1) {
926                         *Error = strerror(errno);
927                         
928                         close(*fd);
929                         *fd = -1;
930                         
931                         return -1;
932                 }
933                 if (buf->buf[len] == '\n')
934                         break;
935                 if (buf->buf[len] != '\r')
936                         len ++;
937                 if (!(len < buf->BufSize)) {
938                         buf->BufUsed = len;
939                         buf->buf[len+1] = '\0';
940                         IncreaseBuf(buf, 1, -1);
941                 }
942         }
943         buf->BufUsed = len;
944         buf->buf[len] = '\0';
945         return len - slen;
946 }
947
948 /**
949  * \brief Read a line from socket
950  * flushes and closes the FD on error
951  * \param buf the buffer to get the input to
952  * \param fd pointer to the filedescriptor to read
953  * \param append Append to an existing string or replace?
954  * \param Error strerror() on error 
955  * \returns numbers of chars read
956  */
957 int StrBufTCP_read_buffered_line(StrBuf *Line, 
958                                  StrBuf *buf, 
959                                  int *fd, 
960                                  int timeout, 
961                                  int selectresolution, 
962                                  const char **Error)
963 {
964         int len, rlen;
965         int nSuccessLess = 0;
966         fd_set rfds;
967         char *pch = NULL;
968         int fdflags;
969         struct timeval tv;
970
971         if (buf->BufUsed > 0) {
972                 pch = strchr(buf->buf, '\n');
973                 if (pch != NULL) {
974                         rlen = 0;
975                         len = pch - buf->buf;
976                         if (len > 0 && (*(pch - 1) == '\r') )
977                                 rlen ++;
978                         StrBufSub(Line, buf, 0, len - rlen);
979                         StrBufCutLeft(buf, len + 1);
980                         return len - rlen;
981                 }
982         }
983         
984         if (buf->BufSize - buf->BufUsed < 10)
985                 IncreaseBuf(buf, 1, -1);
986
987         fdflags = fcntl(*fd, F_GETFL);
988         if ((fdflags & O_NONBLOCK) == O_NONBLOCK)
989                 return -1;
990
991         while ((nSuccessLess < timeout) && (pch == NULL)) {
992                 tv.tv_sec = selectresolution;
993                 tv.tv_usec = 0;
994                 
995                 FD_ZERO(&rfds);
996                 FD_SET(*fd, &rfds);
997                 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
998                         *Error = strerror(errno);
999                         close (*fd);
1000                         *fd = -1;
1001                         return -1;
1002                 }               
1003                 if (FD_ISSET(*fd, &rfds)) {
1004                         rlen = read(*fd, 
1005                                     &buf->buf[buf->BufUsed], 
1006                                     buf->BufSize - buf->BufUsed - 1);
1007                         if (rlen < 1) {
1008                                 *Error = strerror(errno);
1009                                 close(*fd);
1010                                 *fd = -1;
1011                                 return -1;
1012                         }
1013                         else if (rlen > 0) {
1014                                 nSuccessLess = 0;
1015                                 buf->BufUsed += rlen;
1016                                 buf->buf[buf->BufUsed] = '\0';
1017                                 if (buf->BufUsed + 10 > buf->BufSize) {
1018                                         IncreaseBuf(buf, 1, -1);
1019                                 }
1020                                 pch = strchr(buf->buf, '\n');
1021                                 continue;
1022                         }
1023                 }
1024                 nSuccessLess ++;
1025         }
1026         if (pch != NULL) {
1027                 rlen = 0;
1028                 len = pch - buf->buf;
1029                 if (len > 0 && (*(pch - 1) == '\r') )
1030                         rlen ++;
1031                 StrBufSub(Line, buf, 0, len - rlen);
1032                 StrBufCutLeft(buf, len + 1);
1033                 return len - rlen;
1034         }
1035         return -1;
1036
1037 }
1038
1039 /**
1040  * \brief Input binary data from socket
1041  * flushes and closes the FD on error
1042  * \param buf the buffer to get the input to
1043  * \param fd pointer to the filedescriptor to read
1044  * \param append Append to an existing string or replace?
1045  * \param nBytes the maximal number of bytes to read
1046  * \param Error strerror() on error 
1047  * \returns numbers of chars read
1048  */
1049 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
1050 {
1051         fd_set wset;
1052         int fdflags;
1053         int len, rlen, slen;
1054         int nRead = 0;
1055         char *ptr;
1056
1057         if ((Buf == NULL) || (*fd == -1))
1058                 return -1;
1059         if (!append)
1060                 FlushStrBuf(Buf);
1061         if (Buf->BufUsed + nBytes > Buf->BufSize)
1062                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
1063
1064         ptr = Buf->buf + Buf->BufUsed;
1065
1066         slen = len = Buf->BufUsed;
1067
1068         fdflags = fcntl(*fd, F_GETFL);
1069
1070         while (nRead < nBytes) {
1071                if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
1072                         FD_ZERO(&wset);
1073                         FD_SET(*fd, &wset);
1074                         if (select(*fd + 1, NULL, &wset, NULL, NULL) == -1) {
1075                                 *Error = strerror(errno);
1076                                 return -1;
1077                         }
1078                 }
1079
1080                 if ((rlen = read(*fd, 
1081                                  ptr,
1082                                  nBytes - nRead)) == -1) {
1083                         close(*fd);
1084                         *fd = -1;
1085                         *Error = strerror(errno);
1086                         return rlen;
1087                 }
1088                 nRead += rlen;
1089                 ptr += rlen;
1090                 Buf->BufUsed += rlen;
1091         }
1092         Buf->buf[Buf->BufUsed] = '\0';
1093         return nRead;
1094 }
1095
1096 /**
1097  * \brief Cut nChars from the start of the string
1098  * \param Buf Buffer to modify
1099  * \param nChars how many chars should be skipped?
1100  */
1101 void StrBufCutLeft(StrBuf *Buf, int nChars)
1102 {
1103         if (nChars >= Buf->BufUsed) {
1104                 FlushStrBuf(Buf);
1105                 return;
1106         }
1107         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1108         Buf->BufUsed -= nChars;
1109         Buf->buf[Buf->BufUsed] = '\0';
1110 }
1111
1112 /**
1113  * \brief Cut the trailing n Chars from the string
1114  * \param Buf Buffer to modify
1115  * \param nChars how many chars should be trunkated?
1116  */
1117 void StrBufCutRight(StrBuf *Buf, int nChars)
1118 {
1119         if (nChars >= Buf->BufUsed) {
1120                 FlushStrBuf(Buf);
1121                 return;
1122         }
1123         Buf->BufUsed -= nChars;
1124         Buf->buf[Buf->BufUsed] = '\0';
1125 }
1126
1127
1128 void StrBufUpCase(StrBuf *Buf) 
1129 {
1130         char *pch, *pche;
1131
1132         pch = Buf->buf;
1133         pche = pch + Buf->BufUsed;
1134         while (pch < pche) {
1135                 *pch = toupper(*pch);
1136                 pch ++;
1137         }
1138 }
1139
1140
1141 /**
1142  * \brief unhide special chars hidden to the HTML escaper
1143  * \param target buffer to put the unescaped string in
1144  * \param source buffer to unescape
1145  */
1146 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
1147 {
1148         int a, b, len;
1149         char hex[3];
1150
1151         if (target != NULL)
1152                 FlushStrBuf(target);
1153
1154         if (source == NULL ||target == NULL)
1155         {
1156                 return;
1157         }
1158
1159         len = source->BufUsed;
1160         for (a = 0; a < len; ++a) {
1161                 if (target->BufUsed >= target->BufSize)
1162                         IncreaseBuf(target, 1, -1);
1163
1164                 if (source->buf[a] == '=') {
1165                         hex[0] = source->buf[a + 1];
1166                         hex[1] = source->buf[a + 2];
1167                         hex[2] = 0;
1168                         b = 0;
1169                         sscanf(hex, "%02x", &b);
1170                         target->buf[target->BufUsed] = b;
1171                         target->buf[++target->BufUsed] = 0;
1172                         a += 2;
1173                 }
1174                 else {
1175                         target->buf[target->BufUsed] = source->buf[a];
1176                         target->buf[++target->BufUsed] = 0;
1177                 }
1178         }
1179 }
1180
1181
1182 /**
1183  * \brief hide special chars from the HTML escapers and friends
1184  * \param target buffer to put the escaped string in
1185  * \param source buffer to escape
1186  */
1187 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
1188 {
1189         int i, len;
1190
1191         if (target != NULL)
1192                 FlushStrBuf(target);
1193
1194         if (source == NULL ||target == NULL)
1195         {
1196                 return;
1197         }
1198
1199         len = source->BufUsed;
1200         for (i=0; i<len; ++i) {
1201                 if (target->BufUsed + 4 >= target->BufSize)
1202                         IncreaseBuf(target, 1, -1);
1203                 if ( (isalnum(source->buf[i])) || 
1204                      (source->buf[i]=='-') || 
1205                      (source->buf[i]=='_') ) {
1206                         target->buf[target->BufUsed++] = source->buf[i];
1207                 }
1208                 else {
1209                         sprintf(&target->buf[target->BufUsed], 
1210                                 "=%02X", 
1211                                 (0xFF &source->buf[i]));
1212                         target->BufUsed += 3;
1213                 }
1214         }
1215         target->buf[target->BufUsed + 1] = '\0';
1216 }
1217
1218 /*
1219  * \brief uses the same calling syntax as compress2(), but it
1220  * creates a stream compatible with HTTP "Content-encoding: gzip"
1221  */
1222 #ifdef HAVE_ZLIB
1223 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
1224 #define OS_CODE 0x03    /*< unix */
1225 int ZEXPORT compress_gzip(Bytef * dest,         /*< compressed buffer*/
1226                           size_t * destLen,     /*< length of the compresed data */
1227                           const Bytef * source, /*< source to encode */
1228                           uLong sourceLen,      /*< length of source to encode */
1229                           int level)            /*< compression level */
1230 {
1231         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
1232
1233         /* write gzip header */
1234         snprintf((char *) dest, *destLen, 
1235                  "%c%c%c%c%c%c%c%c%c%c",
1236                  gz_magic[0], gz_magic[1], Z_DEFLATED,
1237                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
1238                  OS_CODE);
1239
1240         /* normal deflate */
1241         z_stream stream;
1242         int err;
1243         stream.next_in = (Bytef *) source;
1244         stream.avail_in = (uInt) sourceLen;
1245         stream.next_out = dest + 10L;   // after header
1246         stream.avail_out = (uInt) * destLen;
1247         if ((uLong) stream.avail_out != *destLen)
1248                 return Z_BUF_ERROR;
1249
1250         stream.zalloc = (alloc_func) 0;
1251         stream.zfree = (free_func) 0;
1252         stream.opaque = (voidpf) 0;
1253
1254         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
1255                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
1256         if (err != Z_OK)
1257                 return err;
1258
1259         err = deflate(&stream, Z_FINISH);
1260         if (err != Z_STREAM_END) {
1261                 deflateEnd(&stream);
1262                 return err == Z_OK ? Z_BUF_ERROR : err;
1263         }
1264         *destLen = stream.total_out + 10L;
1265
1266         /* write CRC and Length */
1267         uLong crc = crc32(0L, source, sourceLen);
1268         int n;
1269         for (n = 0; n < 4; ++n, ++*destLen) {
1270                 dest[*destLen] = (int) (crc & 0xff);
1271                 crc >>= 8;
1272         }
1273         uLong len = stream.total_in;
1274         for (n = 0; n < 4; ++n, ++*destLen) {
1275                 dest[*destLen] = (int) (len & 0xff);
1276                 len >>= 8;
1277         }
1278         err = deflateEnd(&stream);
1279         return err;
1280 }
1281 #endif
1282
1283
1284 /**
1285  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
1286  */
1287 int CompressBuffer(StrBuf *Buf)
1288 {
1289 #ifdef HAVE_ZLIB
1290         char *compressed_data = NULL;
1291         size_t compressed_len, bufsize;
1292         
1293         bufsize = compressed_len = ((Buf->BufUsed * 101) / 100) + 100;
1294         compressed_data = malloc(compressed_len);
1295         
1296         if (compress_gzip((Bytef *) compressed_data,
1297                           &compressed_len,
1298                           (Bytef *) Buf->buf,
1299                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
1300                 if (!ConstBuf)
1301                         free(Buf->buf);
1302                 Buf->buf = compressed_data;
1303                 Buf->BufUsed = compressed_len;
1304                 Buf->BufSize = bufsize;
1305                 return 1;
1306         } else {
1307                 free(compressed_data);
1308         }
1309 #endif  /* HAVE_ZLIB */
1310         return 0;
1311 }
1312
1313 /**
1314  * \brief decode a buffer from base 64 encoding; destroys original
1315  * \param Buf Buffor to transform
1316  */
1317 int StrBufDecodeBase64(StrBuf *Buf)
1318 {
1319         char *xferbuf;
1320         size_t siz;
1321         if (Buf == NULL) return -1;
1322
1323         xferbuf = (char*) malloc(Buf->BufSize);
1324         siz = CtdlDecodeBase64(xferbuf,
1325                                Buf->buf,
1326                                Buf->BufUsed);
1327         free(Buf->buf);
1328         Buf->buf = xferbuf;
1329         Buf->BufUsed = siz;
1330         return siz;
1331 }
1332
1333
1334 /**
1335  * \brief  remove escaped strings from i.e. the url string (like %20 for blanks)
1336  * \param Buf Buffer to translate
1337  * \param StripBlanks Reduce several blanks to one?
1338  */
1339 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
1340 {
1341         int a, b;
1342         char hex[3];
1343         long len;
1344
1345         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
1346                 Buf->buf[Buf->BufUsed - 1] = '\0';
1347                 Buf->BufUsed --;
1348         }
1349
1350         a = 0; 
1351         while (a < Buf->BufUsed) {
1352                 if (Buf->buf[a] == '+')
1353                         Buf->buf[a] = ' ';
1354                 else if (Buf->buf[a] == '%') {
1355                         /* don't let % chars through, rather truncate the input. */
1356                         if (a + 2 > Buf->BufUsed) {
1357                                 Buf->buf[a] = '\0';
1358                                 Buf->BufUsed = a;
1359                         }
1360                         else {                  
1361                                 hex[0] = Buf->buf[a + 1];
1362                                 hex[1] = Buf->buf[a + 2];
1363                                 hex[2] = 0;
1364                                 b = 0;
1365                                 sscanf(hex, "%02x", &b);
1366                                 Buf->buf[a] = (char) b;
1367                                 len = Buf->BufUsed - a - 2;
1368                                 if (len > 0)
1369                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
1370                         
1371                                 Buf->BufUsed -=2;
1372                         }
1373                 }
1374                 a++;
1375         }
1376         return a;
1377 }
1378
1379
1380 /**
1381  * \brief       RFC2047-encode a header field if necessary.
1382  *              If no non-ASCII characters are found, the string
1383  *              will be copied verbatim without encoding.
1384  *
1385  * \param       target          Target buffer.
1386  * \param       source          Source string to be encoded.
1387  * \returns     encoded length; -1 if non success.
1388  */
1389 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
1390 {
1391         const char headerStr[] = "=?UTF-8?Q?";
1392         int need_to_encode = 0;
1393         int i = 0;
1394         unsigned char ch;
1395
1396         if ((source == NULL) || 
1397             (target == NULL))
1398             return -1;
1399
1400         while ((i < source->BufUsed) &&
1401                (!IsEmptyStr (&source->buf[i])) &&
1402                (need_to_encode == 0)) {
1403                 if (((unsigned char) source->buf[i] < 32) || 
1404                     ((unsigned char) source->buf[i] > 126)) {
1405                         need_to_encode = 1;
1406                 }
1407                 i++;
1408         }
1409
1410         if (!need_to_encode) {
1411                 if (*target == NULL) {
1412                         *target = NewStrBufPlain(source->buf, source->BufUsed);
1413                 }
1414                 else {
1415                         FlushStrBuf(*target);
1416                         StrBufAppendBuf(*target, source, 0);
1417                 }
1418                 return (*target)->BufUsed;
1419         }
1420         if (*target == NULL)
1421                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
1422         else if (sizeof(headerStr) + source->BufUsed > (*target)->BufSize)
1423                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
1424         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
1425         (*target)->BufUsed = sizeof(headerStr) - 1;
1426         for (i=0; (i < source->BufUsed); ++i) {
1427                 if ((*target)->BufUsed + 4 > (*target)->BufSize)
1428                         IncreaseBuf(*target, 1, 0);
1429                 ch = (unsigned char) source->buf[i];
1430                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
1431                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
1432                         (*target)->BufUsed += 3;
1433                 }
1434                 else {
1435                         (*target)->buf[(*target)->BufUsed] = ch;
1436                         (*target)->BufUsed++;
1437                 }
1438         }
1439         
1440         if ((*target)->BufUsed + 4 > (*target)->BufSize)
1441                 IncreaseBuf(*target, 1, 0);
1442
1443         (*target)->buf[(*target)->BufUsed++] = '?';
1444         (*target)->buf[(*target)->BufUsed++] = '=';
1445         (*target)->buf[(*target)->BufUsed] = '\0';
1446         return (*target)->BufUsed;;
1447 }
1448
1449 /**
1450  * \brief replaces all occurances of 'search' by 'replace'
1451  * \param buf Buffer to modify
1452  * \param search character to search
1453  * \param relpace character to replace search by
1454  */
1455 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
1456 {
1457         long i;
1458         if (buf == NULL)
1459                 return;
1460         for (i=0; i<buf->BufUsed; i++)
1461                 if (buf->buf[i] == search)
1462                         buf->buf[i] = replace;
1463
1464 }