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