]> code.citadel.org Git - citadel.git/blob - libcitadel/lib/stringbuf.c
Added GPL declaration to source files in libcitadel
[citadel.git] / libcitadel / lib / stringbuf.c
1 /*
2  * Copyright (c) 1987-2011 by the citadel.org team
3  *
4  * This program is open source software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18
19 #include "sysdep.h"
20 #include <ctype.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <sys/select.h>
27 #include <fcntl.h>
28 #include <sys/types.h>
29 #define SHOW_ME_VAPPEND_PRINTF
30 #include <stdarg.h>
31 #include "libcitadel.h"
32
33 #ifdef HAVE_ICONV
34 #include <iconv.h>
35 #endif
36
37 #ifdef HAVE_BACKTRACE
38 #include <execinfo.h>
39 #endif
40
41 #ifdef HAVE_ZLIB
42 #include <zlib.h>
43 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
44                           const Bytef * source, uLong sourceLen, int level);
45 #endif
46 int BaseStrBufSize = 64;
47
48 const char *StrBufNOTNULL = ((char*) NULL) - 1;
49
50 const char HexList[256][3] = {
51         "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
52         "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
53         "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
54         "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
55         "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
56         "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
57         "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
58         "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
59         "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
60         "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
61         "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
62         "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
63         "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
64         "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
65         "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
66         "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
67
68 /**
69  * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
70  * StrBuf is a versatile class, aiding the handling of dynamic strings
71  *  * reduce de/reallocations
72  *  * reduce the need to remeasure it
73  *  * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
74  *  * allow asyncroneous IO for line and Blob based operations
75  *  * reduce the use of memove in those
76  *  * Quick filling in several operations with append functions
77  */
78
79 /**
80  * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
81  * @ingroup StrBuf
82  */
83
84 /**
85  * @defgroup StrBuf_Cast Cast operators to interact with char* based code
86  * @ingroup StrBuf
87  * use these operators to interfere with code demanding char*; 
88  * if you need to own the content, smash me. Avoid, since we loose the length information.
89  */
90
91 /**
92  * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
93  * @ingroup StrBuf
94  * operations to get your Strings into a StrBuf, manipulating them, or appending
95  */
96 /**
97  * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence 
98  * @ingroup StrBuf
99  * Quick tokenizer; demands of the user to pull its tokens in sequence
100  */
101
102 /**
103  * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
104  * @ingroup StrBuf
105  * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
106  */
107
108 /**
109  * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
110  * @ingroup StrBuf
111  * File IO to fill StrBufs; Works with work-buffer shared across several calls;
112  * External Cursor to maintain the current read position inside of the buffer
113  * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower) 
114  */
115
116 /**
117  * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
118  * @ingroup StrBuf
119  * Slow I/O; avoid.
120  */
121
122 /**
123  * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
124  * @ingroup StrBuf
125  * these functions translate the content of a buffer into another representation;
126  * some are combined Fillers and encoders
127  */
128
129 /**
130  * Private Structure for the Stringbuffer
131  */
132 struct StrBuf {
133         char *buf;         /**< the pointer to the dynamic buffer */
134         long BufSize;      /**< how many spcae do we optain */
135         long BufUsed;      /**< StNumber of Chars used excluding the trailing \\0 */
136         int ConstBuf;      /**< are we just a wrapper arround a static buffer and musn't we be changed? */
137 #ifdef SIZE_DEBUG
138         long nIncreases;   /**< for profiling; cound how many times we needed more */
139         char bt [SIZ];     /**< Stacktrace of last increase */
140         char bt_lastinc [SIZ]; /**< How much did we increase last time? */
141 #endif
142 };
143
144
145 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
146 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
147
148 #ifdef SIZE_DEBUG
149 #ifdef HAVE_BACKTRACE
150 static void StrBufBacktrace(StrBuf *Buf, int which)
151 {
152         int n;
153         char *pstart, *pch;
154         void *stack_frames[50];
155         size_t size, i;
156         char **strings;
157
158         if (which)
159                 pstart = pch = Buf->bt;
160         else
161                 pstart = pch = Buf->bt_lastinc;
162         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
163         strings = backtrace_symbols(stack_frames, size);
164         for (i = 0; i < size; i++) {
165                 if (strings != NULL)
166                         n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
167                 else
168                         n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
169                 pch += n;
170         }
171         free(strings);
172
173
174 }
175 #endif
176
177 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
178 {
179         if (hFreeDbglog == -1){
180                 pid_t pid = getpid();
181                 char path [SIZ];
182                 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
183                 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
184         }
185         if ((*FreeMe)->nIncreases > 0)
186         {
187                 char buf[SIZ * 3];
188                 long n;
189                 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
190                              FromWhere,
191                              (*FreeMe)->nIncreases,
192                              (*FreeMe)->BufUsed,
193                              (*FreeMe)->BufSize,
194                              (*FreeMe)->bt,
195                              (*FreeMe)->bt_lastinc);
196                 n = write(hFreeDbglog, buf, n);
197         }
198         else
199         {
200                 char buf[128];
201                 long n;
202                 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
203                              FromWhere,
204                              (*FreeMe)->BufUsed,
205                              (*FreeMe)->BufSize);
206                 n = write(hFreeDbglog, buf, n);
207         }
208 }
209
210 void dbg_IncreaseBuf(StrBuf *IncMe)
211 {
212         Buf->nIncreases++;
213 #ifdef HAVE_BACKTRACE
214         StrBufBacktrace(Buf, 1);
215 #endif
216 }
217
218 void dbg_Init(StrBuf *Buf)
219 {
220         Buf->nIncreases = 0;
221         Buf->bt[0] = '\0';
222         Buf->bt_lastinc[0] = '\0';
223 #ifdef HAVE_BACKTRACE
224         StrBufBacktrace(Buf, 0);
225 #endif
226 }
227
228 #else
229 /* void it... */
230 #define dbg_FreeStrBuf(a, b)
231 #define dbg_IncreaseBuf(a)
232 #define dbg_Init(a)
233
234 #endif
235
236 /**
237  * @ingroup StrBuf
238  * @brief swaps the contents of two StrBufs
239  * this is to be used to have cheap switched between a work-buffer and a target buffer 
240  * @param A First one
241  * @param B second one
242  */
243 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
244 {
245         StrBuf C;
246
247         memcpy(&C, A, sizeof(*A));
248         memcpy(A, B, sizeof(*B));
249         memcpy(B, &C, sizeof(C));
250
251 }
252
253 /** 
254  * @ingroup StrBuf_Cast
255  * @brief Cast operator to Plain String 
256  * @note if the buffer is altered by StrBuf operations, this pointer may become 
257  *  invalid. So don't lean on it after altering the buffer!
258  *  Since this operation is considered cheap, rather call it often than risking
259  *  your pointer to become invalid!
260  * @param Str the string we want to get the c-string representation for
261  * @returns the Pointer to the Content. Don't mess with it!
262  */
263 inline const char *ChrPtr(const StrBuf *Str)
264 {
265         if (Str == NULL)
266                 return "";
267         return Str->buf;
268 }
269
270 /**
271  * @ingroup StrBuf_Cast
272  * @brief since we know strlen()'s result, provide it here.
273  * @param Str the string to return the length to
274  * @returns contentlength of the buffer
275  */
276 inline int StrLength(const StrBuf *Str)
277 {
278         return (Str != NULL) ? Str->BufUsed : 0;
279 }
280
281 /**
282  * @ingroup StrBuf_DeConstructors
283  * @brief local utility function to resize the buffer
284  * @param Buf the buffer whichs storage we should increase
285  * @param KeepOriginal should we copy the original buffer or just start over with a new one
286  * @param DestSize what should fit in after?
287  */
288 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
289 {
290         char *NewBuf;
291         size_t NewSize = Buf->BufSize * 2;
292
293         if (Buf->ConstBuf)
294                 return -1;
295                 
296         if (DestSize > 0)
297                 while (NewSize <= DestSize)
298                         NewSize *= 2;
299
300         NewBuf= (char*) malloc(NewSize);
301         if (NewBuf == NULL)
302                 return -1;
303
304         if (KeepOriginal && (Buf->BufUsed > 0))
305         {
306                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
307         }
308         else
309         {
310                 NewBuf[0] = '\0';
311                 Buf->BufUsed = 0;
312         }
313         free (Buf->buf);
314         Buf->buf = NewBuf;
315         Buf->BufSize = NewSize;
316
317         dbg_IncreaseBuf(Buf);
318
319         return Buf->BufSize;
320 }
321
322 /**
323  * @ingroup StrBuf_DeConstructors
324  * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
325  * @param Buf Buffer to shrink (has to be empty)
326  * @param ThreshHold if the buffer is bigger then this, its readjusted
327  * @param NewSize if we Shrink it, how big are we going to be afterwards?
328  */
329 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
330 {
331         if ((Buf != NULL) && 
332             (Buf->BufUsed == 0) &&
333             (Buf->BufSize < ThreshHold)) {
334                 free(Buf->buf);
335                 Buf->buf = (char*) malloc(NewSize);
336                 Buf->BufUsed = 0;
337                 Buf->BufSize = NewSize;
338         }
339 }
340
341 /**
342  * @ingroup StrBuf_DeConstructors
343  * @brief shrink long term buffers to their real size so they don't waste memory
344  * @param Buf buffer to shrink
345  * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
346  * @returns physical size of the buffer
347  */
348 long StrBufShrinkToFit(StrBuf *Buf, int Force)
349 {
350         if (Buf == NULL)
351                 return -1;
352         if (Force || 
353             (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
354         {
355                 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
356                 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
357                 Buf->BufSize = Buf->BufUsed + 1;
358                 free(Buf->buf);
359                 Buf->buf = TmpBuf;
360         }
361         return Buf->BufUsed;
362 }
363
364 /**
365  * @ingroup StrBuf_DeConstructors
366  * @brief Allocate a new buffer with default buffer size
367  * @returns the new stringbuffer
368  */
369 StrBuf* NewStrBuf(void)
370 {
371         StrBuf *NewBuf;
372
373         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
374         NewBuf->buf = (char*) malloc(BaseStrBufSize);
375         NewBuf->buf[0] = '\0';
376         NewBuf->BufSize = BaseStrBufSize;
377         NewBuf->BufUsed = 0;
378         NewBuf->ConstBuf = 0;
379
380         dbg_Init (NewBuf);
381
382         return NewBuf;
383 }
384
385 /** 
386  * @ingroup StrBuf_DeConstructors
387  * @brief Copy Constructor; returns a duplicate of CopyMe
388  * @param CopyMe Buffer to faxmilate
389  * @returns the new stringbuffer
390  */
391 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
392 {
393         StrBuf *NewBuf;
394         
395         if (CopyMe == NULL)
396                 return NewStrBuf();
397
398         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
399         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
400         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
401         NewBuf->BufUsed = CopyMe->BufUsed;
402         NewBuf->BufSize = CopyMe->BufSize;
403         NewBuf->ConstBuf = 0;
404
405         dbg_Init(NewBuf);
406
407         return NewBuf;
408 }
409
410 /** 
411  * @ingroup StrBuf_DeConstructors
412  * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
413  * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
414  * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
415  * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe 
416  * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
417  * @returns the new stringbuffer
418  */
419 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
420 {
421         StrBuf *NewBuf;
422         
423         if (CreateRelpaceMe == NULL)
424                 return;
425
426         if (NoMe != NULL)
427         {
428                 if (*CreateRelpaceMe != NULL)
429                         StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
430                 else 
431                         *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
432                 return;
433         }
434
435         if (CopyFlushMe == NULL)
436         {
437                 if (*CreateRelpaceMe != NULL)
438                         FlushStrBuf(*CreateRelpaceMe);
439                 else 
440                         *CreateRelpaceMe = NewStrBuf();
441                 return;
442         }
443
444         /* 
445          * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
446          * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
447          * be a big IO-Buffer...
448          */
449         if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
450         {
451                 if (*CreateRelpaceMe == NULL)
452                 {
453                         *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
454                         dbg_Init(NewBuf);
455                 }
456                 else 
457                 {
458                         NewBuf = *CreateRelpaceMe;
459                         FlushStrBuf(NewBuf);
460                 }
461                 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
462         }
463         else
464         {
465                 if (*CreateRelpaceMe == NULL)
466                 {
467                         *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
468                         dbg_Init(NewBuf);
469                 }
470                 else 
471                         NewBuf = *CreateRelpaceMe;
472                 SwapBuffers (NewBuf, CopyFlushMe);
473         }
474         if (!KeepOriginal)
475                 FlushStrBuf(CopyFlushMe);
476         return;
477 }
478
479 /**
480  * @ingroup StrBuf_DeConstructors
481  * @brief create a new Buffer using an existing c-string
482  * this function should also be used if you want to pre-suggest
483  * the buffer size to allocate in conjunction with ptr == NULL
484  * @param ptr the c-string to copy; may be NULL to create a blank instance
485  * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
486  * @returns the new stringbuffer
487  */
488 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
489 {
490         StrBuf *NewBuf;
491         size_t Siz = BaseStrBufSize;
492         size_t CopySize;
493
494         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
495         if (nChars < 0)
496                 CopySize = strlen((ptr != NULL)?ptr:"");
497         else
498                 CopySize = nChars;
499
500         while (Siz <= CopySize)
501                 Siz *= 2;
502
503         NewBuf->buf = (char*) malloc(Siz);
504         if (NewBuf->buf == NULL)
505         {
506                 free(NewBuf);
507                 return NULL;
508         }
509         NewBuf->BufSize = Siz;
510         if (ptr != NULL) {
511                 memcpy(NewBuf->buf, ptr, CopySize);
512                 NewBuf->buf[CopySize] = '\0';
513                 NewBuf->BufUsed = CopySize;
514         }
515         else {
516                 NewBuf->buf[0] = '\0';
517                 NewBuf->BufUsed = 0;
518         }
519         NewBuf->ConstBuf = 0;
520
521         dbg_Init(NewBuf);
522
523         return NewBuf;
524 }
525
526 /**
527  * @ingroup StrBuf_DeConstructors
528  * @brief Set an existing buffer from a c-string
529  * @param Buf buffer to load
530  * @param ptr c-string to put into 
531  * @param nChars set to -1 if we should work 0-terminated
532  * @returns the new length of the string
533  */
534 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
535 {
536         size_t Siz;
537         size_t CopySize;
538
539         if (Buf == NULL)
540                 return -1;
541         if (ptr == NULL) {
542                 FlushStrBuf(Buf);
543                 return -1;
544         }
545
546         Siz = Buf->BufSize;
547
548         if (nChars < 0)
549                 CopySize = strlen(ptr);
550         else
551                 CopySize = nChars;
552
553         while (Siz <= CopySize)
554                 Siz *= 2;
555
556         if (Siz != Buf->BufSize)
557                 IncreaseBuf(Buf, 0, Siz);
558         memcpy(Buf->buf, ptr, CopySize);
559         Buf->buf[CopySize] = '\0';
560         Buf->BufUsed = CopySize;
561         Buf->ConstBuf = 0;
562         return CopySize;
563 }
564
565
566 /**
567  * @ingroup StrBuf_DeConstructors
568  * @brief use strbuf as wrapper for a string constant for easy handling
569  * @param StringConstant a string to wrap
570  * @param SizeOfStrConstant should be sizeof(StringConstant)-1
571  */
572 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
573 {
574         StrBuf *NewBuf;
575
576         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
577         NewBuf->buf = (char*) StringConstant;
578         NewBuf->BufSize = SizeOfStrConstant;
579         NewBuf->BufUsed = SizeOfStrConstant;
580         NewBuf->ConstBuf = 1;
581
582         dbg_Init(NewBuf);
583
584         return NewBuf;
585 }
586
587
588 /**
589  * @ingroup StrBuf_DeConstructors
590  * @brief flush the content of a Buf; keep its struct
591  * @param buf Buffer to flush
592  */
593 int FlushStrBuf(StrBuf *buf)
594 {
595         if (buf == NULL)
596                 return -1;
597         if (buf->ConstBuf)
598                 return -1;       
599         buf->buf[0] ='\0';
600         buf->BufUsed = 0;
601         return 0;
602 }
603
604 /**
605  * @ingroup StrBuf_DeConstructors
606  * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
607  * @param buf Buffer to wipe
608  */
609 int FLUSHStrBuf(StrBuf *buf)
610 {
611         if (buf == NULL)
612                 return -1;
613         if (buf->ConstBuf)
614                 return -1;
615         if (buf->BufUsed > 0) {
616                 memset(buf->buf, 0, buf->BufUsed);
617                 buf->BufUsed = 0;
618         }
619         return 0;
620 }
621
622 #ifdef SIZE_DEBUG
623 int hFreeDbglog = -1;
624 #endif
625 /**
626  * @ingroup StrBuf_DeConstructors
627  * @brief Release a Buffer
628  * Its a double pointer, so it can NULL your pointer
629  * so fancy SIG11 appear instead of random results
630  * @param FreeMe Pointer Pointer to the buffer to free
631  */
632 void FreeStrBuf (StrBuf **FreeMe)
633 {
634         if (*FreeMe == NULL)
635                 return;
636
637         dbg_FreeStrBuf(FreeMe, 'F');
638
639         if (!(*FreeMe)->ConstBuf) 
640                 free((*FreeMe)->buf);
641         free(*FreeMe);
642         *FreeMe = NULL;
643 }
644
645 /**
646  * @ingroup StrBuf_DeConstructors
647  * @brief flatten a Buffer to the Char * we return 
648  * Its a double pointer, so it can NULL your pointer
649  * so fancy SIG11 appear instead of random results
650  * The Callee then owns the buffer and is responsible for freeing it.
651  * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
652  * @returns the pointer of the buffer; Callee owns the memory thereafter.
653  */
654 char *SmashStrBuf (StrBuf **SmashMe)
655 {
656         char *Ret;
657
658         if ((SmashMe == NULL) || (*SmashMe == NULL))
659                 return NULL;
660         
661         dbg_FreeStrBuf(SmashMe, 'S');
662
663         Ret = (*SmashMe)->buf;
664         free(*SmashMe);
665         *SmashMe = NULL;
666         return Ret;
667 }
668
669 /**
670  * @ingroup StrBuf_DeConstructors
671  * @brief Release the buffer
672  * If you want put your StrBuf into a Hash, use this as Destructor.
673  * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
674  */
675 void HFreeStrBuf (void *VFreeMe)
676 {
677         StrBuf *FreeMe = (StrBuf*)VFreeMe;
678         if (FreeMe == NULL)
679                 return;
680
681         dbg_FreeStrBuf(SmashMe, 'H');
682
683         if (!FreeMe->ConstBuf) 
684                 free(FreeMe->buf);
685         free(FreeMe);
686 }
687
688
689 /*******************************************************************************
690  *                      Simple string transformations                          *
691  *******************************************************************************/
692
693 /**
694  * @ingroup StrBuf
695  * @brief Wrapper around atol
696  */
697 long StrTol(const StrBuf *Buf)
698 {
699         if (Buf == NULL)
700                 return 0;
701         if(Buf->BufUsed > 0)
702                 return atol(Buf->buf);
703         else
704                 return 0;
705 }
706
707 /**
708  * @ingroup StrBuf
709  * @brief Wrapper around atoi
710  */
711 int StrToi(const StrBuf *Buf)
712 {
713         if (Buf == NULL)
714                 return 0;
715         if (Buf->BufUsed > 0)
716                 return atoi(Buf->buf);
717         else
718                 return 0;
719 }
720
721 /**
722  * @ingroup StrBuf
723  * @brief Checks to see if the string is a pure number 
724  * @param Buf The buffer to inspect
725  * @returns 1 if its a pure number, 0, if not.
726  */
727 int StrBufIsNumber(const StrBuf *Buf) {
728         char * pEnd;
729         if ((Buf == NULL) || (Buf->BufUsed == 0)) {
730                 return 0;
731         }
732         strtoll(Buf->buf, &pEnd, 10);
733         if (pEnd == Buf->buf)
734                 return 0;
735         if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
736                 return 1;
737         if (Buf->buf == pEnd)
738                 return 0;
739         return 0;
740
741
742 /**
743  * @ingroup StrBuf_Filler
744  * @brief modifies a Single char of the Buf
745  * You can point to it via char* or a zero-based integer
746  * @param Buf The buffer to manipulate
747  * @param ptr char* to zero; use NULL if unused
748  * @param nThChar zero based pointer into the string; use -1 if unused
749  * @param PeekValue The Character to place into the position
750  */
751 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
752 {
753         if (Buf == NULL)
754                 return -1;
755         if (ptr != NULL)
756                 nThChar = ptr - Buf->buf;
757         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
758                 return -1;
759         Buf->buf[nThChar] = PeekValue;
760         return nThChar;
761 }
762
763 /**
764  * @ingroup StrBuf_Filler
765  * @brief modifies a range of chars of the Buf
766  * You can point to it via char* or a zero-based integer
767  * @param Buf The buffer to manipulate
768  * @param ptr char* to zero; use NULL if unused
769  * @param nThChar zero based pointer into the string; use -1 if unused
770  * @param nChars how many chars are to be flushed?
771  * @param PookValue The Character to place into that area
772  */
773 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
774 {
775         if (Buf == NULL)
776                 return -1;
777         if (ptr != NULL)
778                 nThChar = ptr - Buf->buf;
779         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
780                 return -1;
781         if (nThChar + nChars > Buf->BufUsed)
782                 nChars =  Buf->BufUsed - nThChar;
783
784         memset(Buf->buf + nThChar, PookValue, nChars);
785         /* just to be shure... */
786         Buf->buf[Buf->BufUsed] = 0;
787         return nChars;
788 }
789
790 /**
791  * @ingroup StrBuf_Filler
792  * @brief Append a StringBuffer to the buffer
793  * @param Buf Buffer to modify
794  * @param AppendBuf Buffer to copy at the end of our buffer
795  * @param Offset Should we start copying from an offset?
796  */
797 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
798 {
799         if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
800                 return;
801
802         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
803                 IncreaseBuf(Buf, 
804                             (Buf->BufUsed > 0), 
805                             AppendBuf->BufUsed + Buf->BufUsed);
806
807         memcpy(Buf->buf + Buf->BufUsed, 
808                AppendBuf->buf + Offset, 
809                AppendBuf->BufUsed - Offset);
810         Buf->BufUsed += AppendBuf->BufUsed - Offset;
811         Buf->buf[Buf->BufUsed] = '\0';
812 }
813
814
815 /**
816  * @ingroup StrBuf_Filler
817  * @brief Append a C-String to the buffer
818  * @param Buf Buffer to modify
819  * @param AppendBuf Buffer to copy at the end of our buffer
820  * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
821  * @param Offset Should we start copying from an offset?
822  */
823 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
824 {
825         long aps;
826         long BufSizeRequired;
827
828         if ((AppendBuf == NULL) || (Buf == NULL))
829                 return;
830
831         if (AppendSize < 0 )
832                 aps = strlen(AppendBuf + Offset);
833         else
834                 aps = AppendSize - Offset;
835
836         BufSizeRequired = Buf->BufUsed + aps + 1;
837         if (Buf->BufSize <= BufSizeRequired)
838                 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
839
840         memcpy(Buf->buf + Buf->BufUsed, 
841                AppendBuf + Offset, 
842                aps);
843         Buf->BufUsed += aps;
844         Buf->buf[Buf->BufUsed] = '\0';
845 }
846
847 /**
848  * @ingroup StrBuf_Filler
849  * @brief sprintf like function appending the formated string to the buffer
850  * vsnprintf version to wrap into own calls
851  * @param Buf Buffer to extend by format and Params
852  * @param format printf alike format to add
853  * @param ap va_list containing the items for format
854  */
855 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
856 {
857         va_list apl;
858         size_t BufSize;
859         size_t nWritten;
860         size_t Offset;
861         size_t newused;
862
863         if ((Buf == NULL)  || (format == NULL))
864                 return;
865
866         BufSize = Buf->BufSize;
867         nWritten = Buf->BufSize + 1;
868         Offset = Buf->BufUsed;
869         newused = Offset + nWritten;
870         
871         while (newused >= BufSize) {
872                 va_copy(apl, ap);
873                 nWritten = vsnprintf(Buf->buf + Offset, 
874                                      Buf->BufSize - Offset, 
875                                      format, apl);
876                 va_end(apl);
877                 newused = Offset + nWritten;
878                 if (newused >= Buf->BufSize) {
879                         IncreaseBuf(Buf, 1, newused);
880                         newused = Buf->BufSize + 1;
881                 }
882                 else {
883                         Buf->BufUsed = Offset + nWritten;
884                         BufSize = Buf->BufSize;
885                 }
886
887         }
888 }
889
890 /**
891  * @ingroup StrBuf_Filler
892  * @brief sprintf like function appending the formated string to the buffer
893  * @param Buf Buffer to extend by format and Params
894  * @param format printf alike format to add
895  */
896 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
897 {
898         size_t BufSize;
899         size_t nWritten;
900         size_t Offset;
901         size_t newused;
902         va_list arg_ptr;
903         
904         if ((Buf == NULL)  || (format == NULL))
905                 return;
906
907         BufSize = Buf->BufSize;
908         nWritten = Buf->BufSize + 1;
909         Offset = Buf->BufUsed;
910         newused = Offset + nWritten;
911
912         while (newused >= BufSize) {
913                 va_start(arg_ptr, format);
914                 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, 
915                                      Buf->BufSize - Buf->BufUsed, 
916                                      format, arg_ptr);
917                 va_end(arg_ptr);
918                 newused = Buf->BufUsed + nWritten;
919                 if (newused >= Buf->BufSize) {
920                         IncreaseBuf(Buf, 1, newused);
921                         newused = Buf->BufSize + 1;
922                 }
923                 else {
924                         Buf->BufUsed += nWritten;
925                         BufSize = Buf->BufSize;
926                 }
927
928         }
929 }
930
931 /**
932  * @ingroup StrBuf_Filler
933  * @brief sprintf like function putting the formated string into the buffer
934  * @param Buf Buffer to extend by format and Parameters
935  * @param format printf alike format to add
936  */
937 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
938 {
939         size_t nWritten;
940         va_list arg_ptr;
941         
942         if ((Buf == NULL)  || (format == NULL))
943                 return;
944
945         nWritten = Buf->BufSize + 1;
946         while (nWritten >= Buf->BufSize) {
947                 va_start(arg_ptr, format);
948                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
949                 va_end(arg_ptr);
950                 if (nWritten >= Buf->BufSize) {
951                         IncreaseBuf(Buf, 0, 0);
952                         nWritten = Buf->BufSize + 1;
953                         continue;
954                 }
955                 Buf->BufUsed = nWritten ;
956         }
957 }
958
959 /**
960  * @ingroup StrBuf_Filler
961  * @brief Callback for cURL to append the webserver reply to a buffer
962  * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
963  * @param size pre-defined by the cURL API; see man 3 curl for mre info
964  * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
965  * @param stream pre-defined by the cURL API; see man 3 curl for mre info
966  */
967 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
968 {
969
970         StrBuf *Target;
971
972         Target = stream;
973         if (ptr == NULL)
974                 return 0;
975
976         StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
977         return size * nmemb;
978 }
979
980
981 /**
982  * @ingroup StrBuf
983  * @brief extracts a substring from Source into dest
984  * @param dest buffer to place substring into
985  * @param Source string to copy substring from
986  * @param Offset chars to skip from start
987  * @param nChars number of chars to copy
988  * @returns the number of chars copied; may be different from nChars due to the size of Source
989  */
990 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
991 {
992         size_t NCharsRemain;
993         if (Offset > Source->BufUsed)
994         {
995                 if (dest != NULL)
996                         FlushStrBuf(dest);
997                 return 0;
998         }
999         if (Offset + nChars < Source->BufUsed)
1000         {
1001                 if (nChars >= dest->BufSize)
1002                         IncreaseBuf(dest, 0, nChars + 1);
1003                 memcpy(dest->buf, Source->buf + Offset, nChars);
1004                 dest->BufUsed = nChars;
1005                 dest->buf[dest->BufUsed] = '\0';
1006                 return nChars;
1007         }
1008         NCharsRemain = Source->BufUsed - Offset;
1009         if (NCharsRemain  >= dest->BufSize)
1010                 IncreaseBuf(dest, 0, NCharsRemain + 1);
1011         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1012         dest->BufUsed = NCharsRemain;
1013         dest->buf[dest->BufUsed] = '\0';
1014         return NCharsRemain;
1015 }
1016
1017 /**
1018  * @ingroup StrBuf
1019  * @brief Cut nChars from the start of the string
1020  * @param Buf Buffer to modify
1021  * @param nChars how many chars should be skipped?
1022  */
1023 void StrBufCutLeft(StrBuf *Buf, int nChars)
1024 {
1025         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1026         if (nChars >= Buf->BufUsed) {
1027                 FlushStrBuf(Buf);
1028                 return;
1029         }
1030         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1031         Buf->BufUsed -= nChars;
1032         Buf->buf[Buf->BufUsed] = '\0';
1033 }
1034
1035 /**
1036  * @ingroup StrBuf
1037  * @brief Cut the trailing n Chars from the string
1038  * @param Buf Buffer to modify
1039  * @param nChars how many chars should be trunkated?
1040  */
1041 void StrBufCutRight(StrBuf *Buf, int nChars)
1042 {
1043         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1044         if (nChars >= Buf->BufUsed) {
1045                 FlushStrBuf(Buf);
1046                 return;
1047         }
1048         Buf->BufUsed -= nChars;
1049         Buf->buf[Buf->BufUsed] = '\0';
1050 }
1051
1052 /**
1053  * @ingroup StrBuf
1054  * @brief Cut the string after n Chars
1055  * @param Buf Buffer to modify
1056  * @param AfternChars after how many chars should we trunkate the string?
1057  * @param At if non-null and points inside of our string, cut it there.
1058  */
1059 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1060 {
1061         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1062         if (At != NULL){
1063                 AfternChars = At - Buf->buf;
1064         }
1065
1066         if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1067                 return;
1068         Buf->BufUsed = AfternChars;
1069         Buf->buf[Buf->BufUsed] = '\0';
1070 }
1071
1072
1073 /**
1074  * @ingroup StrBuf
1075  * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1076  * @param Buf the string to modify
1077  */
1078 void StrBufTrim(StrBuf *Buf)
1079 {
1080         int delta = 0;
1081         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1082
1083         while ((Buf->BufUsed > 0) &&
1084                isspace(Buf->buf[Buf->BufUsed - 1]))
1085         {
1086                 Buf->BufUsed --;
1087         }
1088         Buf->buf[Buf->BufUsed] = '\0';
1089
1090         if (Buf->BufUsed == 0) return;
1091
1092         while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1093                 delta ++;
1094         }
1095         if (delta > 0) StrBufCutLeft(Buf, delta);
1096 }
1097 /**
1098  * @ingroup StrBuf
1099  * @brief changes all spaces in the string  (tab, linefeed...) to Blank (0x20)
1100  * @param Buf the string to modify
1101  */
1102 void StrBufSpaceToBlank(StrBuf *Buf)
1103 {
1104         char *pche, *pch;
1105
1106         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1107
1108         pch = Buf->buf;
1109         pche = pch + Buf->BufUsed;
1110         while (pch < pche) 
1111         {
1112                 if (isspace(*pch))
1113                         *pch = ' ';
1114                 pch ++;
1115         }
1116 }
1117
1118 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1119 {
1120         const char *pBuff;
1121         const char *pLeft;
1122         const char *pRight;
1123
1124         if (Buf == NULL)
1125                 return;
1126         pLeft = pBuff = Buf->buf;
1127         while (pBuff != NULL) {
1128                 pLeft = pBuff;
1129                 pBuff = strchr(pBuff, leftboundary);
1130                 if (pBuff != NULL)
1131                         pBuff++;
1132         }
1133                 
1134         if (pLeft != NULL)
1135                 pBuff = pLeft;
1136         else
1137                 pBuff = Buf->buf;
1138         pRight = strchr(pBuff, rightboundary);
1139         if (pRight != NULL)
1140                 StrBufCutAt(Buf, 0, pRight);
1141         if (pLeft != NULL)
1142                 StrBufCutLeft(Buf, pLeft - Buf->buf);
1143 }
1144
1145
1146 /**
1147  * @ingroup StrBuf_Filler
1148  * @brief uppercase the contents of a buffer
1149  * @param Buf the buffer to translate
1150  */
1151 void StrBufUpCase(StrBuf *Buf) 
1152 {
1153         char *pch, *pche;
1154
1155         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1156
1157         pch = Buf->buf;
1158         pche = pch + Buf->BufUsed;
1159         while (pch < pche) {
1160                 *pch = toupper(*pch);
1161                 pch ++;
1162         }
1163 }
1164
1165
1166 /**
1167  * @ingroup StrBuf_Filler
1168  * @brief lowercase the contents of a buffer
1169  * @param Buf the buffer to translate
1170  */
1171 void StrBufLowerCase(StrBuf *Buf) 
1172 {
1173         char *pch, *pche;
1174
1175         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1176
1177         pch = Buf->buf;
1178         pche = pch + Buf->BufUsed;
1179         while (pch < pche) {
1180                 *pch = tolower(*pch);
1181                 pch ++;
1182         }
1183 }
1184
1185
1186 /*******************************************************************************
1187  *           a tokenizer that kills, maims, and destroys                       *
1188  *******************************************************************************/
1189
1190 /**
1191  * @ingroup StrBuf_Tokenizer
1192  * @brief Replace a token at a given place with a given length by another token with given length
1193  * @param Buf String where to work on
1194  * @param where where inside of the Buf is the search-token
1195  * @param HowLong How long is the token to be replaced
1196  * @param Repl Token to insert at 'where'
1197  * @param ReplLen Length of repl
1198  * @returns -1 if fail else length of resulting Buf
1199  */
1200 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, 
1201                        const char *Repl, long ReplLen)
1202 {
1203
1204         if ((Buf == NULL) || 
1205             (where > Buf->BufUsed) ||
1206             (where + HowLong > Buf->BufUsed))
1207                 return -1;
1208
1209         if (where + ReplLen - HowLong > Buf->BufSize)
1210                 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1211                         return -1;
1212
1213         memmove(Buf->buf + where + ReplLen, 
1214                 Buf->buf + where + HowLong,
1215                 Buf->BufUsed - where - HowLong);
1216                                                 
1217         memcpy(Buf->buf + where, 
1218                Repl, ReplLen);
1219
1220         Buf->BufUsed += ReplLen - HowLong;
1221
1222         return Buf->BufUsed;
1223 }
1224
1225 /**
1226  * @ingroup StrBuf_Tokenizer
1227  * @brief Counts the numbmer of tokens in a buffer
1228  * @param source String to count tokens in
1229  * @param tok    Tokenizer char to count
1230  * @returns numbers of tokenizer chars found
1231  */
1232 int StrBufNum_tokens(const StrBuf *source, char tok)
1233 {
1234         char *pch, *pche;
1235         long NTokens;
1236         if ((source == NULL) || (source->BufUsed == 0))
1237                 return 0;
1238         if ((source->BufUsed == 1) && (*source->buf == tok))
1239                 return 2;
1240         NTokens = 1;
1241         pch = source->buf;
1242         pche = pch + source->BufUsed;
1243         while (pch < pche)
1244         {
1245                 if (*pch == tok)
1246                         NTokens ++;
1247                 pch ++;
1248         }
1249         return NTokens;
1250 }
1251
1252 /**
1253  * @ingroup StrBuf_Tokenizer
1254  * @brief a string tokenizer
1255  * @param Source StringBuffer to read into
1256  * @param parmnum n'th Parameter to remove
1257  * @param separator tokenizer character
1258  * @returns -1 if not found, else length of token.
1259  */
1260 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1261 {
1262         int ReducedBy;
1263         char *d, *s, *end;              /* dest, source */
1264         int count = 0;
1265
1266         /* Find desired @parameter */
1267         end = Source->buf + Source->BufUsed;
1268         d = Source->buf;
1269         while ((d <= end) && 
1270                (count < parmnum))
1271         {
1272                 /* End of string, bail! */
1273                 if (!*d) {
1274                         d = NULL;
1275                         break;
1276                 }
1277                 if (*d == separator) {
1278                         count++;
1279                 }
1280                 d++;
1281         }
1282         if ((d == NULL) || (d >= end))
1283                 return 0;               /* @Parameter not found */
1284
1285         /* Find next @parameter */
1286         s = d;
1287         while ((s <= end) && 
1288                (*s && *s != separator))
1289         {
1290                 s++;
1291         }
1292         if (*s == separator)
1293                 s++;
1294         ReducedBy = d - s;
1295
1296         /* Hack and slash */
1297         if (s >= end) {
1298                 return 0;
1299         }
1300         else if (*s) {
1301                 memmove(d, s, Source->BufUsed - (s - Source->buf));
1302                 Source->BufUsed += ReducedBy;
1303                 Source->buf[Source->BufUsed] = '\0';
1304         }
1305         else if (d == Source->buf) {
1306                 *d = 0;
1307                 Source->BufUsed = 0;
1308         }
1309         else {
1310                 *--d = '\0';
1311                 Source->BufUsed += ReducedBy;
1312         }
1313         /*
1314         while (*s) {
1315                 *d++ = *s++;
1316         }
1317         *d = 0;
1318         */
1319         return ReducedBy;
1320 }
1321
1322
1323 /**
1324  * @ingroup StrBuf_Tokenizer
1325  * @brief a string tokenizer
1326  * @param dest Destination StringBuffer
1327  * @param Source StringBuffer to read into
1328  * @param parmnum n'th Parameter to extract
1329  * @param separator tokenizer character
1330  * @returns -1 if not found, else length of token.
1331  */
1332 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1333 {
1334         const char *s, *e;              //* source * /
1335         int len = 0;                    //* running total length of extracted string * /
1336         int current_token = 0;          //* token currently being processed * /
1337          
1338         if (dest != NULL) {
1339                 dest->buf[0] = '\0';
1340                 dest->BufUsed = 0;
1341         }
1342         else
1343                 return(-1);
1344
1345         if ((Source == NULL) || (Source->BufUsed ==0)) {
1346                 return(-1);
1347         }
1348         s = Source->buf;
1349         e = s + Source->BufUsed;
1350
1351         //cit_backtrace();
1352         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1353
1354         while ((s<e) && !IsEmptyStr(s)) {
1355                 if (*s == separator) {
1356                         ++current_token;
1357                 }
1358                 if (len >= dest->BufSize) {
1359                         dest->BufUsed = len;
1360                         if (IncreaseBuf(dest, 1, -1) < 0) {
1361                                 dest->BufUsed --;
1362                                 break;
1363                         }
1364                 }
1365                 if ( (current_token == parmnum) && 
1366                      (*s != separator)) {
1367                         dest->buf[len] = *s;
1368                         ++len;
1369                 }
1370                 else if (current_token > parmnum) {
1371                         break;
1372                 }
1373                 ++s;
1374         }
1375         
1376         dest->buf[len] = '\0';
1377         dest->BufUsed = len;
1378                 
1379         if (current_token < parmnum) {
1380                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1381                 return(-1);
1382         }
1383         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1384         return(len);
1385 }
1386
1387
1388
1389
1390
1391 /**
1392  * @ingroup StrBuf_Tokenizer
1393  * @brief a string tokenizer to fetch an integer
1394  * @param Source String containing tokens
1395  * @param parmnum n'th Parameter to extract
1396  * @param separator tokenizer character
1397  * @returns 0 if not found, else integer representation of the token
1398  */
1399 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1400 {
1401         StrBuf tmp;
1402         char buf[64];
1403         
1404         tmp.buf = buf;
1405         buf[0] = '\0';
1406         tmp.BufSize = 64;
1407         tmp.BufUsed = 0;
1408         tmp.ConstBuf = 1;
1409         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1410                 return(atoi(buf));
1411         else
1412                 return 0;
1413 }
1414
1415 /**
1416  * @ingroup StrBuf_Tokenizer
1417  * @brief a string tokenizer to fetch a long integer
1418  * @param Source String containing tokens
1419  * @param parmnum n'th Parameter to extract
1420  * @param separator tokenizer character
1421  * @returns 0 if not found, else long integer representation of the token
1422  */
1423 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1424 {
1425         StrBuf tmp;
1426         char buf[64];
1427         
1428         tmp.buf = buf;
1429         buf[0] = '\0';
1430         tmp.BufSize = 64;
1431         tmp.BufUsed = 0;
1432         tmp.ConstBuf = 1;
1433         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1434                 return(atoi(buf));
1435         else
1436                 return 0;
1437 }
1438
1439
1440 /**
1441  * @ingroup StrBuf_Tokenizer
1442  * @brief a string tokenizer to fetch an unsigned long
1443  * @param Source String containing tokens
1444  * @param parmnum n'th Parameter to extract
1445  * @param separator tokenizer character
1446  * @returns 0 if not found, else unsigned long representation of the token
1447  */
1448 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1449 {
1450         StrBuf tmp;
1451         char buf[64];
1452         char *pnum;
1453         
1454         tmp.buf = buf;
1455         buf[0] = '\0';
1456         tmp.BufSize = 64;
1457         tmp.BufUsed = 0;
1458         tmp.ConstBuf = 1;
1459         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1460                 pnum = &buf[0];
1461                 if (*pnum == '-')
1462                         pnum ++;
1463                 return (unsigned long) atol(pnum);
1464         }
1465         else 
1466                 return 0;
1467 }
1468
1469
1470
1471 /**
1472  * @ingroup StrBuf_NextTokenizer
1473  * @brief a string tokenizer; Bounds checker
1474  *  function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1475  * @param Source our tokenbuffer
1476  * @param pStart the token iterator pointer to inspect
1477  * @returns whether the revolving pointer is inside of the search range
1478  */
1479 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1480 {
1481         if ((Source == NULL) || 
1482             (*pStart == StrBufNOTNULL) ||
1483             (Source->BufUsed == 0))
1484         {
1485                 return 0;
1486         }
1487         if (*pStart == NULL)
1488         {
1489                 return 1;
1490         }
1491         else if (*pStart > Source->buf + Source->BufUsed)
1492         {
1493                 return 0;
1494         }
1495         else if (*pStart <= Source->buf)
1496         {
1497                 return 0;
1498         }
1499
1500         return 1;
1501 }
1502
1503 /**
1504  * @ingroup StrBuf_NextTokenizer
1505  * @brief a string tokenizer
1506  * @param dest Destination StringBuffer
1507  * @param Source StringBuffer to read into
1508  * @param pStart pointer to the end of the last token. Feed with NULL on start.
1509  * @param separator tokenizer 
1510  * @returns -1 if not found, else length of token.
1511  */
1512 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1513 {
1514         const char *s;          /* source */
1515         const char *EndBuffer;  /* end stop of source buffer */
1516         int current_token = 0;  /* token currently being processed */
1517         int len = 0;            /* running total length of extracted string */
1518
1519         if ((Source          == NULL) || 
1520             (Source->BufUsed == 0)      ) 
1521         {
1522                 *pStart = StrBufNOTNULL;
1523                 return -1;
1524         }
1525          
1526         EndBuffer = Source->buf + Source->BufUsed;
1527
1528         if (dest != NULL) 
1529         {
1530                 dest->buf[0] = '\0';
1531                 dest->BufUsed = 0;
1532         }
1533         else
1534         {
1535                 *pStart = EndBuffer + 1;
1536                 return -1;
1537         }
1538
1539         if (*pStart == NULL)
1540         {
1541                 *pStart = Source->buf; /* we're starting to examine this buffer. */
1542         }
1543         else if ((*pStart < Source->buf) || 
1544                  (*pStart > EndBuffer  )   ) 
1545         {
1546                 return -1; /* no more tokens to find. */
1547         }
1548
1549         s = *pStart;
1550         /* start to find the next token */
1551         while ((s <= EndBuffer)      && 
1552                (current_token == 0) ) 
1553         {
1554                 if (*s == separator) 
1555                 {
1556                         /* we found the next token */
1557                         ++current_token;
1558                 }
1559
1560                 if (len >= dest->BufSize) 
1561                 {
1562                         /* our Dest-buffer isn't big enough, increase it. */
1563                         dest->BufUsed = len;
1564
1565                         if (IncreaseBuf(dest, 1, -1) < 0) {
1566                                 /* WHUT? no more mem? bail out. */
1567                                 s = EndBuffer;
1568                                 dest->BufUsed --;
1569                                 break;
1570                         }
1571                 }
1572
1573                 if ( (current_token == 0 ) &&   /* are we in our target token? */
1574                      (!IsEmptyStr(s)     ) &&
1575                      (separator     != *s)    ) /* don't copy the token itself */
1576                 {
1577                         dest->buf[len] = *s;    /* Copy the payload */
1578                         ++len;                  /* remember the bigger size. */
1579                 }
1580
1581                 ++s;
1582         }
1583
1584         /* did we reach the end? */
1585         if ((s > EndBuffer)) {
1586                 EndBuffer = StrBufNOTNULL;
1587                 *pStart = EndBuffer;
1588         }
1589         else {
1590                 *pStart = s;  /* remember the position for the next run */
1591         }
1592
1593         /* sanitize our extracted token */
1594         dest->buf[len] = '\0';
1595         dest->BufUsed  = len;
1596
1597         return (len);
1598 }
1599
1600
1601 /**
1602  * @ingroup StrBuf_NextTokenizer
1603  * @brief a string tokenizer
1604  * @param Source StringBuffer to read from
1605  * @param pStart pointer to the end of the last token. Feed with NULL.
1606  * @param separator tokenizer character
1607  * @param nTokens number of tokens to fastforward over
1608  * @returns -1 if not found, else length of token.
1609  */
1610 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1611 {
1612         const char *s, *EndBuffer;      //* source * /
1613         int len = 0;                    //* running total length of extracted string * /
1614         int current_token = 0;          //* token currently being processed * /
1615
1616         if ((Source == NULL) || 
1617             (Source->BufUsed ==0)) {
1618                 return(-1);
1619         }
1620         if (nTokens == 0)
1621                 return Source->BufUsed;
1622
1623         if (*pStart == NULL)
1624                 *pStart = Source->buf;
1625
1626         EndBuffer = Source->buf + Source->BufUsed;
1627
1628         if ((*pStart < Source->buf) || 
1629             (*pStart >  EndBuffer)) {
1630                 return (-1);
1631         }
1632
1633
1634         s = *pStart;
1635
1636         //cit_backtrace();
1637         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1638
1639         while ((s<EndBuffer) && !IsEmptyStr(s)) {
1640                 if (*s == separator) {
1641                         ++current_token;
1642                 }
1643                 if (current_token >= nTokens) {
1644                         break;
1645                 }
1646                 ++s;
1647         }
1648         *pStart = s;
1649         (*pStart) ++;
1650
1651         return(len);
1652 }
1653
1654 /**
1655  * @ingroup StrBuf_NextTokenizer
1656  * @brief a string tokenizer to fetch an integer
1657  * @param Source StringBuffer to read from
1658  * @param pStart Cursor on the tokenstring
1659  * @param separator tokenizer character
1660  * @returns 0 if not found, else integer representation of the token
1661  */
1662 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1663 {
1664         StrBuf tmp;
1665         char buf[64];
1666         
1667         tmp.buf = buf;
1668         buf[0] = '\0';
1669         tmp.BufSize = 64;
1670         tmp.BufUsed = 0;
1671         tmp.ConstBuf = 1;
1672         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1673                 return(atoi(buf));
1674         else
1675                 return 0;
1676 }
1677
1678 /**
1679  * @ingroup StrBuf_NextTokenizer
1680  * @brief a string tokenizer to fetch a long integer
1681  * @param Source StringBuffer to read from
1682  * @param pStart Cursor on the tokenstring
1683  * @param separator tokenizer character
1684  * @returns 0 if not found, else long integer representation of the token
1685  */
1686 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1687 {
1688         StrBuf tmp;
1689         char buf[64];
1690         
1691         tmp.buf = buf;
1692         buf[0] = '\0';
1693         tmp.BufSize = 64;
1694         tmp.BufUsed = 0;
1695         tmp.ConstBuf = 1;
1696         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1697                 return(atoi(buf));
1698         else
1699                 return 0;
1700 }
1701
1702
1703 /**
1704  * @ingroup StrBuf_NextTokenizer
1705  * @brief a string tokenizer to fetch an unsigned long
1706  * @param Source StringBuffer to read from
1707  * @param pStart Cursor on the tokenstring
1708  * @param separator tokenizer character
1709  * @returns 0 if not found, else unsigned long representation of the token
1710  */
1711 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1712 {
1713         StrBuf tmp;
1714         char buf[64];
1715         char *pnum;
1716         
1717         tmp.buf = buf;
1718         buf[0] = '\0';
1719         tmp.BufSize = 64;
1720         tmp.BufUsed = 0;
1721         tmp.ConstBuf = 1;
1722         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1723                 pnum = &buf[0];
1724                 if (*pnum == '-')
1725                         pnum ++;
1726                 return (unsigned long) atol(pnum);
1727         }
1728         else 
1729                 return 0;
1730 }
1731
1732
1733
1734
1735
1736 /*******************************************************************************
1737  *                             Escape Appending                                *
1738  *******************************************************************************/
1739
1740 /** 
1741  * @ingroup StrBuf_DeEnCoder
1742  * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1743  * @param OutBuf the output buffer
1744  * @param In Buffer to encode
1745  * @param PlainIn way in from plain old c strings
1746  */
1747 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1748 {
1749         const char *pch, *pche;
1750         char *pt, *pte;
1751         int len;
1752         
1753         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1754                 return;
1755         if (PlainIn != NULL) {
1756                 len = strlen(PlainIn);
1757                 pch = PlainIn;
1758                 pche = pch + len;
1759         }
1760         else {
1761                 pch = In->buf;
1762                 pche = pch + In->BufUsed;
1763                 len = In->BufUsed;
1764         }
1765
1766         if (len == 0) 
1767                 return;
1768
1769         pt = OutBuf->buf + OutBuf->BufUsed;
1770         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1771
1772         while (pch < pche) {
1773                 if (pt >= pte) {
1774                         IncreaseBuf(OutBuf, 1, -1);
1775                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1776                         pt = OutBuf->buf + OutBuf->BufUsed;
1777                 }
1778
1779                 if((*pch >= 'a' && *pch <= 'z') ||
1780                    (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1781                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1782                    (*pch == '!') || (*pch == '_') || 
1783                    (*pch == ',') || (*pch == '.'))
1784                 {
1785                         *(pt++) = *(pch++);
1786                         OutBuf->BufUsed++;
1787                 }                       
1788                 else {
1789                         *pt = '%';
1790                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1791                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1792                         pt += 3;
1793                         OutBuf->BufUsed += 3;
1794                         pch ++;
1795                 }
1796         }
1797         *pt = '\0';
1798 }
1799
1800 /** 
1801  * @ingroup StrBuf_DeEnCoder
1802  * @brief append a string in hex encoding to the buffer
1803  * @param OutBuf the output buffer
1804  * @param In Buffer to encode
1805  * @param PlainIn way in from plain old c strings
1806  */
1807 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1808 {
1809         const char *pch, *pche;
1810         char *pt, *pte;
1811         int len;
1812         
1813         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1814                 return;
1815         if (PlainIn != NULL) {
1816                 len = strlen(PlainIn);
1817                 pch = PlainIn;
1818                 pche = pch + len;
1819         }
1820         else {
1821                 pch = In->buf;
1822                 pche = pch + In->BufUsed;
1823                 len = In->BufUsed;
1824         }
1825
1826         if (len == 0) 
1827                 return;
1828
1829         pt = OutBuf->buf + OutBuf->BufUsed;
1830         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1831
1832         while (pch < pche) {
1833                 if (pt >= pte) {
1834                         IncreaseBuf(OutBuf, 1, -1);
1835                         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1836                         pt = OutBuf->buf + OutBuf->BufUsed;
1837                 }
1838
1839                 *pt = HexList[(unsigned char)*pch][0];
1840                 pt ++;
1841                 *pt = HexList[(unsigned char)*pch][1];
1842                 pt ++; pch ++; OutBuf->BufUsed += 2;
1843         }
1844         *pt = '\0';
1845 }
1846
1847 /**
1848  * @ingroup StrBuf_DeEnCoder
1849  * @brief Append a string, escaping characters which have meaning in HTML.  
1850  *
1851  * @param Target        target buffer
1852  * @param Source        source buffer; set to NULL if you just have a C-String
1853  * @param PlainIn       Plain-C string to append; set to NULL if unused
1854  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
1855  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
1856  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
1857  */
1858 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1859 {
1860         const char *aptr, *eiptr;
1861         char *bptr, *eptr;
1862         long len;
1863
1864         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1865                 return -1;
1866
1867         if (PlainIn != NULL) {
1868                 aptr = PlainIn;
1869                 len = strlen(PlainIn);
1870                 eiptr = aptr + len;
1871         }
1872         else {
1873                 aptr = Source->buf;
1874                 eiptr = aptr + Source->BufUsed;
1875                 len = Source->BufUsed;
1876         }
1877
1878         if (len == 0) 
1879                 return -1;
1880
1881         bptr = Target->buf + Target->BufUsed;
1882         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1883
1884         while (aptr < eiptr){
1885                 if(bptr >= eptr) {
1886                         IncreaseBuf(Target, 1, -1);
1887                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
1888                         bptr = Target->buf + Target->BufUsed;
1889                 }
1890                 if (*aptr == '<') {
1891                         memcpy(bptr, "&lt;", 4);
1892                         bptr += 4;
1893                         Target->BufUsed += 4;
1894                 }
1895                 else if (*aptr == '>') {
1896                         memcpy(bptr, "&gt;", 4);
1897                         bptr += 4;
1898                         Target->BufUsed += 4;
1899                 }
1900                 else if (*aptr == '&') {
1901                         memcpy(bptr, "&amp;", 5);
1902                         bptr += 5;
1903                         Target->BufUsed += 5;
1904                 }
1905                 else if (*aptr == '"') {
1906                         memcpy(bptr, "&quot;", 6);
1907                         bptr += 6;
1908                         Target->BufUsed += 6;
1909                 }
1910                 else if (*aptr == '\'') {
1911                         memcpy(bptr, "&#39;", 5);
1912                         bptr += 5;
1913                         Target->BufUsed += 5;
1914                 }
1915                 else if (*aptr == LB) {
1916                         *bptr = '<';
1917                         bptr ++;
1918                         Target->BufUsed ++;
1919                 }
1920                 else if (*aptr == RB) {
1921                         *bptr = '>';
1922                         bptr ++;
1923                         Target->BufUsed ++;
1924                 }
1925                 else if (*aptr == QU) {
1926                         *bptr ='"';
1927                         bptr ++;
1928                         Target->BufUsed ++;
1929                 }
1930                 else if ((*aptr == 32) && (nbsp == 1)) {
1931                         memcpy(bptr, "&nbsp;", 6);
1932                         bptr += 6;
1933                         Target->BufUsed += 6;
1934                 }
1935                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1936                         *bptr='\0';     /* nothing */
1937                 }
1938                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1939                         memcpy(bptr, "&lt;br/&gt;", 11);
1940                         bptr += 11;
1941                         Target->BufUsed += 11;
1942                 }
1943
1944
1945                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1946                         *bptr='\0';     /* nothing */
1947                 }
1948                 else{
1949                         *bptr = *aptr;
1950                         bptr++;
1951                         Target->BufUsed ++;
1952                 }
1953                 aptr ++;
1954         }
1955         *bptr = '\0';
1956         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
1957                 return -1;
1958         return Target->BufUsed;
1959 }
1960
1961 /**
1962  * @ingroup StrBuf_DeEnCoder
1963  * @brief Append a string, escaping characters which have meaning in HTML.  
1964  * Converts linebreaks into blanks; escapes single quotes
1965  * @param Target        target buffer
1966  * @param Source        source buffer; set to NULL if you just have a C-String
1967  * @param PlainIn       Plain-C string to append; set to NULL if unused
1968  */
1969 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
1970 {
1971         const char *aptr, *eiptr;
1972         char *tptr, *eptr;
1973         long len;
1974
1975         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1976                 return ;
1977
1978         if (PlainIn != NULL) {
1979                 aptr = PlainIn;
1980                 len = strlen(PlainIn);
1981                 eiptr = aptr + len;
1982         }
1983         else {
1984                 aptr = Source->buf;
1985                 eiptr = aptr + Source->BufUsed;
1986                 len = Source->BufUsed;
1987         }
1988
1989         if (len == 0) 
1990                 return;
1991
1992         eptr = Target->buf + Target->BufSize - 8; 
1993         tptr = Target->buf + Target->BufUsed;
1994         
1995         while (aptr < eiptr){
1996                 if(tptr >= eptr) {
1997                         IncreaseBuf(Target, 1, -1);
1998                         eptr = Target->buf + Target->BufSize - 8; 
1999                         tptr = Target->buf + Target->BufUsed;
2000                 }
2001                
2002                 if (*aptr == '\n') {
2003                         *tptr = ' ';
2004                         Target->BufUsed++;
2005                 }
2006                 else if (*aptr == '\r') {
2007                         *tptr = ' ';
2008                         Target->BufUsed++;
2009                 }
2010                 else if (*aptr == '\'') {
2011                         *(tptr++) = '&';
2012                         *(tptr++) = '#';
2013                         *(tptr++) = '3';
2014                         *(tptr++) = '9';
2015                         *tptr = ';';
2016                         Target->BufUsed += 5;
2017                 } else {
2018                         *tptr = *aptr;
2019                         Target->BufUsed++;
2020                 }
2021                 tptr++; aptr++;
2022         }
2023         *tptr = '\0';
2024 }
2025
2026
2027
2028 /**
2029  * @ingroup StrBuf_DeEnCoder
2030  * @brief Append a string, escaping characters which have meaning in ICAL.  
2031  * [\n,] 
2032  * @param Target        target buffer
2033  * @param Source        source buffer; set to NULL if you just have a C-String
2034  * @param PlainIn       Plain-C string to append; set to NULL if unused
2035  */
2036 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2037 {
2038         const char *aptr, *eiptr;
2039         char *tptr, *eptr;
2040         long len;
2041
2042         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2043                 return ;
2044
2045         if (PlainIn != NULL) {
2046                 aptr = PlainIn;
2047                 len = strlen(PlainIn);
2048                 eiptr = aptr + len;
2049         }
2050         else {
2051                 aptr = Source->buf;
2052                 eiptr = aptr + Source->BufUsed;
2053                 len = Source->BufUsed;
2054         }
2055
2056         if (len == 0) 
2057                 return;
2058
2059         eptr = Target->buf + Target->BufSize - 8; 
2060         tptr = Target->buf + Target->BufUsed;
2061         
2062         while (aptr < eiptr){
2063                 if(tptr + 3 >= eptr) {
2064                         IncreaseBuf(Target, 1, -1);
2065                         eptr = Target->buf + Target->BufSize - 8; 
2066                         tptr = Target->buf + Target->BufUsed;
2067                 }
2068                
2069                 if (*aptr == '\n') {
2070                         *tptr = '\\';
2071                         Target->BufUsed++;
2072                         tptr++;
2073                         *tptr = 'n';
2074                         Target->BufUsed++;
2075                 }
2076                 else if (*aptr == '\r') {
2077                         *tptr = '\\';
2078                         Target->BufUsed++;
2079                         tptr++;
2080                         *tptr = 'r';
2081                         Target->BufUsed++;
2082                 }
2083                 else if (*aptr == ',') {
2084                         *tptr = '\\';
2085                         Target->BufUsed++;
2086                         tptr++;
2087                         *tptr = ',';
2088                         Target->BufUsed++;
2089                 } else {
2090                         *tptr = *aptr;
2091                         Target->BufUsed++;
2092                 }
2093                 tptr++; aptr++;
2094         }
2095         *tptr = '\0';
2096 }
2097
2098 /**
2099  * @ingroup StrBuf_DeEnCoder
2100  * @brief Append a string, escaping characters which have meaning in JavaScript strings .  
2101  *
2102  * @param Target        target buffer
2103  * @param Source        source buffer; set to NULL if you just have a C-String
2104  * @param PlainIn       Plain-C string to append; set to NULL if unused
2105  * @returns size of result or -1
2106  */
2107 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2108 {
2109         const char *aptr, *eiptr;
2110         char *bptr, *eptr;
2111         long len;
2112
2113         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2114                 return -1;
2115
2116         if (PlainIn != NULL) {
2117                 aptr = PlainIn;
2118                 len = strlen(PlainIn);
2119                 eiptr = aptr + len;
2120         }
2121         else {
2122                 aptr = Source->buf;
2123                 eiptr = aptr + Source->BufUsed;
2124                 len = Source->BufUsed;
2125         }
2126
2127         if (len == 0) 
2128                 return -1;
2129
2130         bptr = Target->buf + Target->BufUsed;
2131         eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in...  */
2132
2133         while (aptr < eiptr){
2134                 if(bptr >= eptr) {
2135                         IncreaseBuf(Target, 1, -1);
2136                         eptr = Target->buf + Target->BufSize - 3; 
2137                         bptr = Target->buf + Target->BufUsed;
2138                 }
2139                 if (*aptr == '"') {
2140                         *bptr = '\\';
2141                         bptr ++;
2142                         *bptr = '"';
2143                         bptr ++;
2144                         Target->BufUsed += 2;
2145                 } else if (*aptr == '\\') {
2146                         *bptr = '\\';
2147                         bptr ++;
2148                         *bptr = '\\';
2149                         bptr ++;
2150                         Target->BufUsed += 2;
2151                 }
2152                 else{
2153                         *bptr = *aptr;
2154                         bptr++;
2155                         Target->BufUsed ++;
2156                 }
2157                 aptr ++;
2158         }
2159         *bptr = '\0';
2160         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2161                 return -1;
2162         return Target->BufUsed;
2163 }
2164
2165 /**
2166  * @ingroup StrBuf_DeEnCoder
2167  * @brief Append a string, escaping characters which have meaning in HTML + json.  
2168  *
2169  * @param Target        target buffer
2170  * @param Source        source buffer; set to NULL if you just have a C-String
2171  * @param PlainIn       Plain-C string to append; set to NULL if unused
2172  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2173  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2174  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2175  */
2176 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2177 {
2178         const char *aptr, *eiptr;
2179         char *bptr, *eptr;
2180         long len;
2181         int IsUtf8Sequence = 0;
2182
2183         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2184                 return -1;
2185
2186         if (PlainIn != NULL) {
2187                 aptr = PlainIn;
2188                 len = strlen(PlainIn);
2189                 eiptr = aptr + len;
2190         }
2191         else {
2192                 aptr = Source->buf;
2193                 eiptr = aptr + Source->BufUsed;
2194                 len = Source->BufUsed;
2195         }
2196
2197         if (len == 0) 
2198                 return -1;
2199
2200         bptr = Target->buf + Target->BufUsed;
2201         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2202
2203         while (aptr < eiptr){
2204                 if(bptr >= eptr) {
2205                         IncreaseBuf(Target, 1, -1);
2206                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2207                         bptr = Target->buf + Target->BufUsed;
2208                 }
2209                 if (*aptr == '<') {
2210                         memcpy(bptr, "&lt;", 4);
2211                         bptr += 4;
2212                         Target->BufUsed += 4;
2213                 }
2214                 else if (*aptr == '>') {
2215                         memcpy(bptr, "&gt;", 4);
2216                         bptr += 4;
2217                         Target->BufUsed += 4;
2218                 }
2219                 else if (*aptr == '&') {
2220                         memcpy(bptr, "&amp;", 5);
2221                         bptr += 5;
2222                         Target->BufUsed += 5;
2223                 }
2224                 else if (*aptr == LB) {
2225                         *bptr = '<';
2226                         bptr ++;
2227                         Target->BufUsed ++;
2228                 }
2229                 else if (*aptr == RB) {
2230                         *bptr = '>';
2231                         bptr ++;
2232                         Target->BufUsed ++;
2233                 }
2234                 else if ((*aptr == 32) && (nbsp == 1)) {
2235                         memcpy(bptr, "&nbsp;", 6);
2236                         bptr += 6;
2237                         Target->BufUsed += 6;
2238                 }
2239                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2240                         *bptr='\0';     /* nothing */
2241                 }
2242                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2243                         memcpy(bptr, "&lt;br/&gt;", 11);
2244                         bptr += 11;
2245                         Target->BufUsed += 11;
2246                 }
2247
2248                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2249                         *bptr='\0';     /* nothing */
2250                 }
2251
2252                 else if ((*aptr == '"') || (*aptr == QU)) {
2253                         *bptr = '\\';
2254                         bptr ++;
2255                         *bptr = '"';
2256                         bptr ++;
2257                         Target->BufUsed += 2;
2258                 } else if (*aptr == '\\') {
2259                         *bptr = '\\';
2260                         bptr ++;
2261                         *bptr = '\\';
2262                         bptr ++;
2263                         Target->BufUsed += 2;
2264                 }
2265                 else {
2266                         if (((unsigned char)*aptr) >= 0x20)
2267                         {
2268                                 IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2269                                 
2270                                 *bptr = *aptr;
2271                                 Target->BufUsed ++;
2272                                 while (IsUtf8Sequence > 1){
2273                                         if(bptr + IsUtf8Sequence >= eptr) {
2274                                                 IncreaseBuf(Target, 1, -1);
2275                                                 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2276                                                 bptr = Target->buf + Target->BufUsed - 1;
2277                                         }
2278                                         bptr++; aptr++;
2279                                         IsUtf8Sequence --;
2280                                         *bptr = *aptr;
2281                                         Target->BufUsed ++;
2282                                 }
2283                                 bptr++;
2284                         }
2285
2286                 }
2287                 aptr ++;
2288         }
2289         *bptr = '\0';
2290         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2291                 return -1;
2292         return Target->BufUsed;
2293 }
2294
2295 /**
2296  * @ingroup StrBuf_DeEnCoder
2297  * @brief unhide special chars hidden to the HTML escaper
2298  * @param target buffer to put the unescaped string in
2299  * @param source buffer to unescape
2300  */
2301 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
2302 {
2303         int a, b, len;
2304         char hex[3];
2305
2306         if (target != NULL)
2307                 FlushStrBuf(target);
2308
2309         if (source == NULL ||target == NULL)
2310         {
2311                 return;
2312         }
2313
2314         len = source->BufUsed;
2315         for (a = 0; a < len; ++a) {
2316                 if (target->BufUsed >= target->BufSize)
2317                         IncreaseBuf(target, 1, -1);
2318
2319                 if (source->buf[a] == '=') {
2320                         hex[0] = source->buf[a + 1];
2321                         hex[1] = source->buf[a + 2];
2322                         hex[2] = 0;
2323                         b = 0;
2324                         sscanf(hex, "%02x", &b);
2325                         target->buf[target->BufUsed] = b;
2326                         target->buf[++target->BufUsed] = 0;
2327                         a += 2;
2328                 }
2329                 else {
2330                         target->buf[target->BufUsed] = source->buf[a];
2331                         target->buf[++target->BufUsed] = 0;
2332                 }
2333         }
2334 }
2335
2336
2337 /**
2338  * @ingroup StrBuf_DeEnCoder
2339  * @brief hide special chars from the HTML escapers and friends
2340  * @param target buffer to put the escaped string in
2341  * @param source buffer to escape
2342  */
2343 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
2344 {
2345         int i, len;
2346
2347         if (target != NULL)
2348                 FlushStrBuf(target);
2349
2350         if (source == NULL ||target == NULL)
2351         {
2352                 return;
2353         }
2354
2355         len = source->BufUsed;
2356         for (i=0; i<len; ++i) {
2357                 if (target->BufUsed + 4 >= target->BufSize)
2358                         IncreaseBuf(target, 1, -1);
2359                 if ( (isalnum(source->buf[i])) || 
2360                      (source->buf[i]=='-') || 
2361                      (source->buf[i]=='_') ) {
2362                         target->buf[target->BufUsed++] = source->buf[i];
2363                 }
2364                 else {
2365                         sprintf(&target->buf[target->BufUsed], 
2366                                 "=%02X", 
2367                                 (0xFF &source->buf[i]));
2368                         target->BufUsed += 3;
2369                 }
2370         }
2371         target->buf[target->BufUsed + 1] = '\0';
2372 }
2373
2374
2375 /*******************************************************************************
2376  *                      Quoted Printable de/encoding                           *
2377  *******************************************************************************/
2378
2379 /**
2380  * @ingroup StrBuf_DeEnCoder
2381  * @brief decode a buffer from base 64 encoding; destroys original
2382  * @param Buf Buffor to transform
2383  */
2384 int StrBufDecodeBase64(StrBuf *Buf)
2385 {
2386         char *xferbuf;
2387         size_t siz;
2388         if (Buf == NULL) return -1;
2389
2390         xferbuf = (char*) malloc(Buf->BufSize);
2391         *xferbuf = '\0';
2392         siz = CtdlDecodeBase64(xferbuf,
2393                                Buf->buf,
2394                                Buf->BufUsed);
2395         free(Buf->buf);
2396         Buf->buf = xferbuf;
2397         Buf->BufUsed = siz;
2398         return siz;
2399 }
2400
2401 /**
2402  * @ingroup StrBuf_DeEnCoder
2403  * @brief decode a buffer from base 64 encoding; destroys original
2404  * @param Buf Buffor to transform
2405  */
2406 int StrBufDecodeHex(StrBuf *Buf)
2407 {
2408         unsigned int ch;
2409         char *pch, *pche, *pchi;
2410
2411         if (Buf == NULL) return -1;
2412
2413         pch = pchi = Buf->buf;
2414         pche = pch + Buf->BufUsed;
2415
2416         while (pchi < pche){
2417                 ch = decode_hex(pchi);
2418                 *pch = ch;
2419                 pch ++;
2420                 pchi += 2;
2421         }
2422
2423         *pch = '\0';
2424         Buf->BufUsed = pch - Buf->buf;
2425         return Buf->BufUsed;
2426 }
2427
2428 /**
2429  * @ingroup StrBuf_DeEnCoder
2430  * @brief replace all chars >0x20 && < 0x7F with Mute
2431  * @param Mute char to put over invalid chars
2432  * @param Buf Buffor to transform
2433  */
2434 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2435 {
2436         unsigned char *pch;
2437
2438         if (Buf == NULL) return -1;
2439         pch = (unsigned char *)Buf->buf;
2440         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2441                 if ((*pch < 0x20) || (*pch > 0x7F))
2442                         *pch = Mute;
2443                 pch ++;
2444         }
2445         return Buf->BufUsed;
2446 }
2447
2448
2449 /**
2450  * @ingroup StrBuf_DeEnCoder
2451  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2452  * @param Buf Buffer to translate
2453  * @param StripBlanks Reduce several blanks to one?
2454  */
2455 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2456 {
2457         int a, b;
2458         char hex[3];
2459         long len;
2460
2461         if (Buf == NULL)
2462                 return -1;
2463
2464         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2465                 Buf->buf[Buf->BufUsed - 1] = '\0';
2466                 Buf->BufUsed --;
2467         }
2468
2469         a = 0; 
2470         while (a < Buf->BufUsed) {
2471                 if (Buf->buf[a] == '+')
2472                         Buf->buf[a] = ' ';
2473                 else if (Buf->buf[a] == '%') {
2474                         /* don't let % chars through, rather truncate the input. */
2475                         if (a + 2 > Buf->BufUsed) {
2476                                 Buf->buf[a] = '\0';
2477                                 Buf->BufUsed = a;
2478                         }
2479                         else {                  
2480                                 hex[0] = Buf->buf[a + 1];
2481                                 hex[1] = Buf->buf[a + 2];
2482                                 hex[2] = 0;
2483                                 b = 0;
2484                                 sscanf(hex, "%02x", &b);
2485                                 Buf->buf[a] = (char) b;
2486                                 len = Buf->BufUsed - a - 2;
2487                                 if (len > 0)
2488                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2489                         
2490                                 Buf->BufUsed -=2;
2491                         }
2492                 }
2493                 a++;
2494         }
2495         return a;
2496 }
2497
2498
2499 /**
2500  * @ingroup StrBuf_DeEnCoder
2501  * @brief       RFC2047-encode a header field if necessary.
2502  *              If no non-ASCII characters are found, the string
2503  *              will be copied verbatim without encoding.
2504  *
2505  * @param       target          Target buffer.
2506  * @param       source          Source string to be encoded.
2507  * @returns     encoded length; -1 if non success.
2508  */
2509 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2510 {
2511         const char headerStr[] = "=?UTF-8?Q?";
2512         int need_to_encode = 0;
2513         int i = 0;
2514         unsigned char ch;
2515
2516         if ((source == NULL) || 
2517             (target == NULL))
2518             return -1;
2519
2520         while ((i < source->BufUsed) &&
2521                (!IsEmptyStr (&source->buf[i])) &&
2522                (need_to_encode == 0)) {
2523                 if (((unsigned char) source->buf[i] < 32) || 
2524                     ((unsigned char) source->buf[i] > 126)) {
2525                         need_to_encode = 1;
2526                 }
2527                 i++;
2528         }
2529
2530         if (!need_to_encode) {
2531                 if (*target == NULL) {
2532                         *target = NewStrBufPlain(source->buf, source->BufUsed);
2533                 }
2534                 else {
2535                         FlushStrBuf(*target);
2536                         StrBufAppendBuf(*target, source, 0);
2537                 }
2538                 return (*target)->BufUsed;
2539         }
2540         if (*target == NULL)
2541                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2542         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2543                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2544         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2545         (*target)->BufUsed = sizeof(headerStr) - 1;
2546         for (i=0; (i < source->BufUsed); ++i) {
2547                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2548                         IncreaseBuf(*target, 1, 0);
2549                 ch = (unsigned char) source->buf[i];
2550                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
2551                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2552                         (*target)->BufUsed += 3;
2553                 }
2554                 else {
2555                         (*target)->buf[(*target)->BufUsed] = ch;
2556                         (*target)->BufUsed++;
2557                 }
2558         }
2559         
2560         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2561                 IncreaseBuf(*target, 1, 0);
2562
2563         (*target)->buf[(*target)->BufUsed++] = '?';
2564         (*target)->buf[(*target)->BufUsed++] = '=';
2565         (*target)->buf[(*target)->BufUsed] = '\0';
2566         return (*target)->BufUsed;;
2567 }
2568
2569
2570
2571 static void AddRecipient(StrBuf *Target, 
2572                          StrBuf *UserName, 
2573                          StrBuf *EmailAddress, 
2574                          StrBuf *EncBuf)
2575 {
2576         int QuoteMe = 0;
2577
2578         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2579         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2580
2581         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
2582         StrBufRFC2047encode(&EncBuf, UserName);
2583         StrBufAppendBuf(Target, EncBuf, 0);
2584         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2585         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
2586
2587         if (StrLength(EmailAddress) > 0){
2588                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2589                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2590                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2591         }
2592 }
2593
2594
2595 /**
2596  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2597  * \param Recp Source list of email recipients
2598  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2599  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2600  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2601  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2602  */
2603 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
2604                                            StrBuf *UserName, 
2605                                            StrBuf *EmailAddress,
2606                                            StrBuf *EncBuf)
2607 {
2608         StrBuf *Target;
2609         int need_to_encode;
2610
2611         const char *pch, *pche;
2612         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2613
2614         if ((Recp == NULL) || (StrLength(Recp) == 0))
2615                 return NULL;
2616
2617         need_to_encode = 0;
2618         pch = ChrPtr(Recp);
2619         pche = pch + StrLength(Recp);
2620
2621         if (!CheckEncode(pch, -1, pche))
2622                 return NewStrBufDup(Recp);
2623
2624         Target = NewStrBufPlain(NULL, StrLength(Recp));
2625
2626         while ((pch != NULL) && (pch < pche))
2627         {
2628                 int ColonOk = 0;
2629
2630                 while (isspace(*pch)) pch++;
2631                 UserStart = UserEnd = EmailStart = EmailEnd = NULL;
2632                 
2633                 if ((*pch == '"') || (*pch == '\'')) {
2634                         UserStart = pch + 1;
2635                         
2636                         UserEnd = strchr(UserStart, *pch);
2637                         if (UserEnd == NULL) 
2638                                 break; ///TODO: Userfeedback??
2639                         EmailStart = UserEnd + 1;
2640                         while (isspace(*EmailStart))
2641                                 EmailStart++;
2642                         if (UserEnd == UserStart) {
2643                                 UserStart = UserEnd = NULL;
2644                         }
2645                         
2646                         if (*EmailStart == '<') {
2647                                 EmailStart++;
2648                                 EmailEnd = strchr(EmailStart, '>');
2649                                 if (EmailEnd == NULL)
2650                                         EmailEnd = strchr(EmailStart, ',');
2651                                 
2652                         }
2653                         else {
2654                                 EmailEnd = strchr(EmailStart, ',');
2655                         }
2656                         if (EmailEnd == NULL)
2657                                 EmailEnd = pche;
2658                         pch = EmailEnd + 1;
2659                         ColonOk = 1;
2660                 }
2661                 else {
2662                         int gt = 0;
2663                         UserStart = pch;
2664                         EmailEnd = strchr(UserStart, ',');
2665                         if (EmailEnd == NULL) {
2666                                 EmailEnd = strchr(pch, '>');
2667                                 pch = NULL;
2668                                 if (EmailEnd != NULL) {
2669                                         gt = 1;
2670                                 }
2671                                 else {
2672                                         EmailEnd = pche;
2673                                 }
2674                         }
2675                         else {
2676
2677                                 pch = EmailEnd + 1;
2678                                 while ((EmailEnd > UserStart) && !gt &&
2679                                        ((*EmailEnd == ',') ||
2680                                         (*EmailEnd == '>') ||
2681                                         (isspace(*EmailEnd))))
2682                                 {
2683                                         if (*EmailEnd == '>')
2684                                                 gt = 1;
2685                                         else 
2686                                                 EmailEnd--;
2687                                 }
2688                                 if (EmailEnd == UserStart)
2689                                         break;
2690                         }
2691                         if (gt) {
2692                                 EmailStart = strchr(UserStart, '<');
2693                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2694                                         break;
2695                                 UserEnd = EmailStart;
2696
2697                                 while ((UserEnd > UserStart) && 
2698                                        isspace (*(UserEnd - 1)))
2699                                         UserEnd --;
2700                                 EmailStart ++;
2701                                 if (UserStart >= UserEnd)
2702                                         UserStart = UserEnd = NULL;
2703                                 At = strchr(EmailStart, '@');
2704                         }
2705                         else { /* this is a local recipient... no domain, just a realname */
2706                                 EmailStart = UserStart;
2707                                 At = strchr(EmailStart, '@');
2708                                 if (At == NULL) {
2709                                         UserEnd = EmailEnd;
2710                                         EmailEnd = NULL;
2711                                 }
2712                                 else {
2713                                         EmailStart = UserStart;
2714                                         UserStart = NULL;
2715                                 }
2716                         }
2717                 }
2718
2719                 if ((UserStart != NULL) && (UserEnd != NULL))
2720                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2721                 else if ((UserStart != NULL) && (UserEnd == NULL))
2722                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2723                 else
2724                         FlushStrBuf(UserName);
2725
2726                 if ((EmailStart != NULL) && (EmailEnd != NULL))
2727                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2728                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2729                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2730                 else 
2731                         FlushStrBuf(EmailAddress);
2732
2733                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2734
2735                 if (pch == NULL)
2736                         break;
2737                 
2738                 if ((pch != NULL) && (*pch == ','))
2739                         pch ++;
2740                 if (pch != NULL) while (isspace(*pch))
2741                         pch ++;
2742         }
2743         return Target;
2744 }
2745
2746
2747 /**
2748  * @ingroup StrBuf
2749  * @brief replaces all occurances of 'search' by 'replace'
2750  * @param buf Buffer to modify
2751  * @param search character to search
2752  * @param replace character to replace search by
2753  */
2754 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2755 {
2756         long i;
2757         if (buf == NULL)
2758                 return;
2759         for (i=0; i<buf->BufUsed; i++)
2760                 if (buf->buf[i] == search)
2761                         buf->buf[i] = replace;
2762
2763 }
2764
2765 /**
2766  * @ingroup StrBuf
2767  * @brief removes all \r s from the string, or replaces them with \n if its not a combination of both.
2768  * @param buf Buffer to modify
2769  */
2770 void StrBufToUnixLF(StrBuf *buf)
2771 {
2772         char *pche, *pchS, *pchT;
2773         if (buf == NULL)
2774                 return;
2775
2776         pche = buf->buf + buf->BufUsed;
2777         pchS = pchT = buf->buf;
2778         while (pchS < pche)
2779         {
2780                 if (*pchS == '\r')
2781                 {
2782                         pchS ++;
2783                         if (*pchS != '\n') {
2784                                 *pchT = '\n';
2785                                 pchT++;
2786                         }
2787                 }
2788                 *pchT = *pchS;
2789                 pchT++; pchS++;
2790         }
2791         *pchT = '\0';
2792         buf->BufUsed = pchT - buf->buf;
2793 }
2794
2795
2796 /*******************************************************************************
2797  *                 Iconv Wrapper; RFC822 de/encoding                           *
2798  *******************************************************************************/
2799
2800 /**
2801  * @ingroup StrBuf_DeEnCoder
2802  * @brief Wrapper around iconv_open()
2803  * Our version adds aliases for non-standard Microsoft charsets
2804  * such as 'MS950', aliasing them to names like 'CP950'
2805  *
2806  * @param tocode        Target encoding
2807  * @param fromcode      Source encoding
2808  * @param pic           anonimized pointer to iconv struct
2809  */
2810 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2811 {
2812 #ifdef HAVE_ICONV
2813         iconv_t ic = (iconv_t)(-1) ;
2814         ic = iconv_open(tocode, fromcode);
2815         if (ic == (iconv_t)(-1) ) {
2816                 char alias_fromcode[64];
2817                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2818                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2819                         alias_fromcode[0] = 'C';
2820                         alias_fromcode[1] = 'P';
2821                         ic = iconv_open(tocode, alias_fromcode);
2822                 }
2823         }
2824         *(iconv_t *)pic = ic;
2825 #endif
2826 }
2827
2828
2829 /**
2830  * @ingroup StrBuf_DeEnCoder
2831  * @brief find one chunk of a RFC822 encoded string
2832  * @param Buffer where to search
2833  * @param bptr where to start searching
2834  * @returns found position, NULL if none.
2835  */
2836 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
2837 {
2838         const char * end;
2839         /* Find the next ?Q? */
2840         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
2841                 return NULL;
2842
2843         end = strchr(bptr + 2, '?');
2844
2845         if (end == NULL)
2846                 return NULL;
2847
2848         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2849             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
2850              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
2851             (*(end + 2) == '?')) {
2852                 /* skip on to the end of the cluster, the next ?= */
2853                 end = strstr(end + 3, "?=");
2854         }
2855         else
2856                 /* sort of half valid encoding, try to find an end. */
2857                 end = strstr(bptr, "?=");
2858         return end;
2859 }
2860
2861
2862
2863 /**
2864  * @ingroup StrBuf_DeEnCoder
2865  * @brief convert one buffer according to the preselected iconv pointer PIC
2866  * @param ConvertBuf buffer we need to translate
2867  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
2868  * @param pic Pointer to the iconv-session Object
2869  */
2870 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
2871 {
2872 #ifdef HAVE_ICONV
2873         long trycount = 0;
2874         size_t siz;
2875         iconv_t ic;
2876         char *ibuf;                     /**< Buffer of characters to be converted */
2877         char *obuf;                     /**< Buffer for converted characters */
2878         size_t ibuflen;                 /**< Length of input buffer */
2879         size_t obuflen;                 /**< Length of output buffer */
2880
2881
2882         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
2883         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
2884                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
2885 TRYAGAIN:
2886         ic = *(iconv_t*)pic;
2887         ibuf = ConvertBuf->buf;
2888         ibuflen = ConvertBuf->BufUsed;
2889         obuf = TmpBuf->buf;
2890         obuflen = TmpBuf->BufSize;
2891         
2892         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
2893
2894         if (siz < 0) {
2895                 if (errno == E2BIG) {
2896                         trycount ++;                    
2897                         IncreaseBuf(TmpBuf, 0, 0);
2898                         if (trycount < 5) 
2899                                 goto TRYAGAIN;
2900
2901                 }
2902                 else if (errno == EILSEQ){ 
2903                         /* hm, invalid utf8 sequence... what to do now? */
2904                         /* An invalid multibyte sequence has been encountered in the input */
2905                 }
2906                 else if (errno == EINVAL) {
2907                         /* An incomplete multibyte sequence has been encountered in the input. */
2908                 }
2909
2910                 FlushStrBuf(TmpBuf);
2911         }
2912         else {
2913                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
2914                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
2915                 
2916                 /* little card game: wheres the red lady? */
2917                 SwapBuffers(ConvertBuf, TmpBuf);
2918                 FlushStrBuf(TmpBuf);
2919         }
2920 #endif
2921 }
2922
2923
2924 /**
2925  * @ingroup StrBuf_DeEnCoder
2926  * @brief catches one RFC822 encoded segment, and decodes it.
2927  * @param Target buffer to fill with result
2928  * @param DecodeMe buffer with stuff to process
2929  * @param SegmentStart points to our current segment in DecodeMe
2930  * @param SegmentEnd Points to the end of our current segment in DecodeMe
2931  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
2932  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
2933  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
2934  */
2935 inline static void DecodeSegment(StrBuf *Target, 
2936                                  const StrBuf *DecodeMe, 
2937                                  const char *SegmentStart, 
2938                                  const char *SegmentEnd, 
2939                                  StrBuf *ConvertBuf,
2940                                  StrBuf *ConvertBuf2, 
2941                                  StrBuf *FoundCharset)
2942 {
2943         StrBuf StaticBuf;
2944         char charset[128];
2945         char encoding[16];
2946 #ifdef HAVE_ICONV
2947         iconv_t ic = (iconv_t)(-1);
2948 #else
2949         void *ic = NULL;
2950 #endif
2951         /* Now we handle foreign character sets properly encoded
2952          * in RFC2047 format.
2953          */
2954         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
2955         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
2956         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
2957         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
2958         if (FoundCharset != NULL) {
2959                 FlushStrBuf(FoundCharset);
2960                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
2961         }
2962         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
2963         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
2964         
2965         *encoding = toupper(*encoding);
2966         if (*encoding == 'B') { /**< base64 */
2967                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
2968                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
2969                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
2970                                                         ConvertBuf->buf, 
2971                                                         ConvertBuf->BufUsed);
2972         }
2973         else if (*encoding == 'Q') {    /**< quoted-printable */
2974                 long pos;
2975                 
2976                 pos = 0;
2977                 while (pos < ConvertBuf->BufUsed)
2978                 {
2979                         if (ConvertBuf->buf[pos] == '_') 
2980                                 ConvertBuf->buf[pos] = ' ';
2981                         pos++;
2982                 }
2983                 
2984                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
2985                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
2986
2987                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
2988                         ConvertBuf2->buf, 
2989                         ConvertBuf->buf,
2990                         ConvertBuf->BufUsed);
2991         }
2992         else {
2993                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
2994         }
2995 #ifdef HAVE_ICONV
2996         ctdl_iconv_open("UTF-8", charset, &ic);
2997         if (ic != (iconv_t)(-1) ) {             
2998 #endif
2999                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3000                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3001 #ifdef HAVE_ICONV
3002                 iconv_close(ic);
3003         }
3004         else {
3005                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3006         }
3007 #endif
3008 }
3009
3010 /**
3011  * @ingroup StrBuf_DeEnCoder
3012  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3013  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3014  * @param Target where to put the decoded string to 
3015  * @param DecodeMe buffer with encoded string
3016  * @param DefaultCharset if we don't find one, which should we use?
3017  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3018  *        put it here for later use where no string might be known.
3019  */
3020 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3021 {
3022         StrBuf *ConvertBuf;
3023         StrBuf *ConvertBuf2;
3024         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3025         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3026         
3027         StrBuf_RFC822_2_Utf8(Target, 
3028                              DecodeMe, 
3029                              DefaultCharset, 
3030                              FoundCharset, 
3031                              ConvertBuf, 
3032                              ConvertBuf2);
3033         FreeStrBuf(&ConvertBuf);
3034         FreeStrBuf(&ConvertBuf2);
3035 }
3036
3037 /**
3038  * @ingroup StrBuf_DeEnCoder
3039  * @brief Handle subjects with RFC2047 encoding such as:
3040  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3041  * @param Target where to put the decoded string to 
3042  * @param DecodeMe buffer with encoded string
3043  * @param DefaultCharset if we don't find one, which should we use?
3044  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3045  *        put it here for later use where no string might be known.
3046  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3047  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3048  */
3049 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3050                           const StrBuf *DecodeMe, 
3051                           const StrBuf* DefaultCharset, 
3052                           StrBuf *FoundCharset, 
3053                           StrBuf *ConvertBuf, 
3054                           StrBuf *ConvertBuf2)
3055 {
3056         StrBuf *DecodedInvalidBuf = NULL;
3057         const StrBuf *DecodeMee = DecodeMe;
3058         const char *start, *end, *next, *nextend, *ptr = NULL;
3059 #ifdef HAVE_ICONV
3060         iconv_t ic = (iconv_t)(-1) ;
3061 #endif
3062         const char *eptr;
3063         int passes = 0;
3064         int i, len;
3065         int illegal_non_rfc2047_encoding = 0;
3066
3067         /* Sometimes, badly formed messages contain strings which were simply
3068          *  written out directly in some foreign character set instead of
3069          *  using RFC2047 encoding.  This is illegal but we will attempt to
3070          *  handle it anyway by converting from a user-specified default
3071          *  charset to UTF-8 if we see any nonprintable characters.
3072          */
3073         
3074         len = StrLength(DecodeMe);
3075         for (i=0; i<DecodeMe->BufUsed; ++i) {
3076                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3077                         illegal_non_rfc2047_encoding = 1;
3078                         break;
3079                 }
3080         }
3081
3082         if ((illegal_non_rfc2047_encoding) &&
3083             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3084             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3085         {
3086 #ifdef HAVE_ICONV
3087                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3088                 if (ic != (iconv_t)(-1) ) {
3089                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3090                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3091                         DecodeMee = DecodedInvalidBuf;
3092                         iconv_close(ic);
3093                 }
3094 #endif
3095         }
3096
3097         /* pre evaluate the first pair */
3098         nextend = end = NULL;
3099         len = StrLength(DecodeMee);
3100         start = strstr(DecodeMee->buf, "=?");
3101         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3102         if (start != NULL) 
3103                 end = FindNextEnd (DecodeMee, start + 2);
3104         else {
3105                 StrBufAppendBuf(Target, DecodeMee, 0);
3106                 FreeStrBuf(&DecodedInvalidBuf);
3107                 return;
3108         }
3109
3110
3111         if (start != DecodeMee->buf) {
3112                 long nFront;
3113                 
3114                 nFront = start - DecodeMee->buf;
3115                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3116                 len -= nFront;
3117         }
3118         /*
3119          * Since spammers will go to all sorts of absurd lengths to get their
3120          * messages through, there are LOTS of corrupt headers out there.
3121          * So, prevent a really badly formed RFC2047 header from throwing
3122          * this function into an infinite loop.
3123          */
3124         while ((start != NULL) && 
3125                (end != NULL) && 
3126                (start < eptr) && 
3127                (end < eptr) && 
3128                (passes < 20))
3129         {
3130                 passes++;
3131                 DecodeSegment(Target, 
3132                               DecodeMee, 
3133                               start, 
3134                               end, 
3135                               ConvertBuf,
3136                               ConvertBuf2,
3137                               FoundCharset);
3138                 
3139                 next = strstr(end, "=?");
3140                 nextend = NULL;
3141                 if ((next != NULL) && 
3142                     (next < eptr))
3143                         nextend = FindNextEnd(DecodeMee, next);
3144                 if (nextend == NULL)
3145                         next = NULL;
3146
3147                 /* did we find two partitions */
3148                 if ((next != NULL) && 
3149                     ((next - end) > 2))
3150                 {
3151                         ptr = end + 2;
3152                         while ((ptr < next) && 
3153                                (isspace(*ptr) ||
3154                                 (*ptr == '\r') ||
3155                                 (*ptr == '\n') || 
3156                                 (*ptr == '\t')))
3157                                 ptr ++;
3158                         /* 
3159                          * did we find a gab just filled with blanks?
3160                          * if not, copy its stuff over.
3161                          */
3162                         if (ptr != next)
3163                         {
3164                                 StrBufAppendBufPlain(Target, 
3165                                                      end + 2, 
3166                                                      next - end - 2,
3167                                                      0);
3168                         }
3169                 }
3170                 /* our next-pair is our new first pair now. */
3171                 ptr = end + 2;
3172                 start = next;
3173                 end = nextend;
3174         }
3175         end = ptr;
3176         nextend = DecodeMee->buf + DecodeMee->BufUsed;
3177         if ((end != NULL) && (end < nextend)) {
3178                 ptr = end;
3179                 while ( (ptr < nextend) &&
3180                         (isspace(*ptr) ||
3181                          (*ptr == '\r') ||
3182                          (*ptr == '\n') || 
3183                          (*ptr == '\t')))
3184                         ptr ++;
3185                 if (ptr < nextend)
3186                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
3187         }
3188         FreeStrBuf(&DecodedInvalidBuf);
3189 }
3190
3191 /*******************************************************************************
3192  *                   Manipulating UTF-8 Strings                                *
3193  *******************************************************************************/
3194
3195 /**
3196  * @ingroup StrBuf
3197  * @brief evaluate the length of an utf8 special character sequence
3198  * @param Char the character to examine
3199  * @returns width of utf8 chars in bytes
3200  */
3201 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3202 {
3203         int n = 1;
3204         char test = (1<<7);
3205         
3206         while ((n < 8) && ((test & *CharS) != 0)) {
3207                 test = test << 1;
3208                 n ++;
3209         }
3210         if ((n > 6) || ((CharE - CharS) < n))
3211                 n = 1;
3212         return n;
3213 }
3214
3215 /**
3216  * @ingroup StrBuf
3217  * @brief detect whether this char starts an utf-8 encoded char
3218  * @param Char character to inspect
3219  * @returns yes or no
3220  */
3221 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3222 {
3223 /** 11??.???? indicates an UTF8 Sequence. */
3224         return ((Char & 0xC0) != 0);
3225 }
3226
3227 /**
3228  * @ingroup StrBuf
3229  * @brief measure the number of glyphs in an UTF8 string...
3230  * @param Buf string to measure
3231  * @returns the number of glyphs in Buf
3232  */
3233 long StrBuf_Utf8StrLen(StrBuf *Buf)
3234 {
3235         int n = 0;
3236         int m = 0;
3237         char *aptr, *eptr;
3238
3239         if ((Buf == NULL) || (Buf->BufUsed == 0))
3240                 return 0;
3241         aptr = Buf->buf;
3242         eptr = Buf->buf + Buf->BufUsed;
3243         while ((aptr < eptr) && (*aptr != '\0')) {
3244                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3245                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3246                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3247                         n ++;
3248                 }
3249                 else {
3250                         n++;
3251                         aptr++;
3252                 }
3253         }
3254         return n;
3255 }
3256
3257 /**
3258  * @ingroup StrBuf
3259  * @brief cuts a string after maxlen glyphs
3260  * @param Buf string to cut to maxlen glyphs
3261  * @param maxlen how long may the string become?
3262  * @returns current length of the string
3263  */
3264 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3265 {
3266         char *aptr, *eptr;
3267         int n = 0, m = 0;
3268
3269         aptr = Buf->buf;
3270         eptr = Buf->buf + Buf->BufUsed;
3271         while ((aptr < eptr) && (*aptr != '\0')) {
3272                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3273                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3274                         while ((*aptr++ != '\0') && (m-- > 0));
3275                         n ++;
3276                 }
3277                 else {
3278                         n++;
3279                         aptr++;
3280                 }
3281                 if (n > maxlen) {
3282                         *aptr = '\0';
3283                         Buf->BufUsed = aptr - Buf->buf;
3284                         return Buf->BufUsed;
3285                 }                       
3286         }
3287         return Buf->BufUsed;
3288
3289 }
3290
3291
3292
3293
3294
3295 /*******************************************************************************
3296  *                               wrapping ZLib                                 *
3297  *******************************************************************************/
3298
3299 #ifdef HAVE_ZLIB
3300 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3301 #define OS_CODE 0x03    /*< unix */
3302
3303 /**
3304  * @ingroup StrBuf_DeEnCoder
3305  * @brief uses the same calling syntax as compress2(), but it
3306  *   creates a stream compatible with HTTP "Content-encoding: gzip"
3307  * @param dest compressed buffer
3308  * @param destLen length of the compresed data 
3309  * @param source source to encode
3310  * @param sourceLen length of source to encode 
3311  * @param level compression level
3312  */
3313 int ZEXPORT compress_gzip(Bytef * dest,
3314                           size_t * destLen,
3315                           const Bytef * source,
3316                           uLong sourceLen,     
3317                           int level)
3318 {
3319         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3320
3321         /* write gzip header */
3322         snprintf((char *) dest, *destLen, 
3323                  "%c%c%c%c%c%c%c%c%c%c",
3324                  gz_magic[0], gz_magic[1], Z_DEFLATED,
3325                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3326                  OS_CODE);
3327
3328         /* normal deflate */
3329         z_stream stream;
3330         int err;
3331         stream.next_in = (Bytef *) source;
3332         stream.avail_in = (uInt) sourceLen;
3333         stream.next_out = dest + 10L;   // after header
3334         stream.avail_out = (uInt) * destLen;
3335         if ((uLong) stream.avail_out != *destLen)
3336                 return Z_BUF_ERROR;
3337
3338         stream.zalloc = (alloc_func) 0;
3339         stream.zfree = (free_func) 0;
3340         stream.opaque = (voidpf) 0;
3341
3342         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3343                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3344         if (err != Z_OK)
3345                 return err;
3346
3347         err = deflate(&stream, Z_FINISH);
3348         if (err != Z_STREAM_END) {
3349                 deflateEnd(&stream);
3350                 return err == Z_OK ? Z_BUF_ERROR : err;
3351         }
3352         *destLen = stream.total_out + 10L;
3353
3354         /* write CRC and Length */
3355         uLong crc = crc32(0L, source, sourceLen);
3356         int n;
3357         for (n = 0; n < 4; ++n, ++*destLen) {
3358                 dest[*destLen] = (int) (crc & 0xff);
3359                 crc >>= 8;
3360         }
3361         uLong len = stream.total_in;
3362         for (n = 0; n < 4; ++n, ++*destLen) {
3363                 dest[*destLen] = (int) (len & 0xff);
3364                 len >>= 8;
3365         }
3366         err = deflateEnd(&stream);
3367         return err;
3368 }
3369 #endif
3370
3371
3372 /**
3373  * @ingroup StrBuf_DeEnCoder
3374  * @brief compress the buffer with gzip
3375  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3376  * @param Buf buffer whose content is to be gzipped
3377  */
3378 int CompressBuffer(StrBuf *Buf)
3379 {
3380 #ifdef HAVE_ZLIB
3381         char *compressed_data = NULL;
3382         size_t compressed_len, bufsize;
3383         int i = 0;
3384
3385         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
3386         compressed_data = malloc(compressed_len);
3387         
3388         if (compressed_data == NULL)
3389                 return -1;
3390         /* Flush some space after the used payload so valgrind shuts up... */
3391         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3392                 Buf->buf[Buf->BufUsed + i++] = '\0';
3393         if (compress_gzip((Bytef *) compressed_data,
3394                           &compressed_len,
3395                           (Bytef *) Buf->buf,
3396                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3397                 if (!Buf->ConstBuf)
3398                         free(Buf->buf);
3399                 Buf->buf = compressed_data;
3400                 Buf->BufUsed = compressed_len;
3401                 Buf->BufSize = bufsize;
3402                 /* Flush some space after the used payload so valgrind shuts up... */
3403                 i = 0;
3404                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3405                         Buf->buf[Buf->BufUsed + i++] = '\0';
3406                 return 1;
3407         } else {
3408                 free(compressed_data);
3409         }
3410 #endif  /* HAVE_ZLIB */
3411         return 0;
3412 }
3413
3414
3415
3416 /*******************************************************************************
3417  *           File I/O; Prefer buffered read since its faster!                  *
3418  *******************************************************************************/
3419
3420 /**
3421  * @ingroup StrBuf_IO
3422  * @brief Read a line from socket
3423  * flushes and closes the FD on error
3424  * @param buf the buffer to get the input to
3425  * @param fd pointer to the filedescriptor to read
3426  * @param append Append to an existing string or replace?
3427  * @param Error strerror() on error 
3428  * @returns numbers of chars read
3429  */
3430 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
3431 {
3432         int len, rlen, slen;
3433
3434         if (!append)
3435                 FlushStrBuf(buf);
3436
3437         slen = len = buf->BufUsed;
3438         while (1) {
3439                 rlen = read(*fd, &buf->buf[len], 1);
3440                 if (rlen < 1) {
3441                         *Error = strerror(errno);
3442                         
3443                         close(*fd);
3444                         *fd = -1;
3445                         
3446                         return -1;
3447                 }
3448                 if (buf->buf[len] == '\n')
3449                         break;
3450                 if (buf->buf[len] != '\r')
3451                         len ++;
3452                 if (len + 2 >= buf->BufSize) {
3453                         buf->BufUsed = len;
3454                         buf->buf[len+1] = '\0';
3455                         IncreaseBuf(buf, 1, -1);
3456                 }
3457         }
3458         buf->BufUsed = len;
3459         buf->buf[len] = '\0';
3460         return len - slen;
3461 }
3462
3463 /**
3464  * @ingroup StrBuf_BufferedIO
3465  * @brief Read a line from socket
3466  * flushes and closes the FD on error
3467  * @param Line the line to read from the fd / I/O Buffer
3468  * @param buf the buffer to get the input to
3469  * @param fd pointer to the filedescriptor to read
3470  * @param timeout number of successless selects until we bail out
3471  * @param selectresolution how long to wait on each select
3472  * @param Error strerror() on error 
3473  * @returns numbers of chars read
3474  */
3475 int StrBufTCP_read_buffered_line(StrBuf *Line, 
3476                                  StrBuf *buf, 
3477                                  int *fd, 
3478                                  int timeout, 
3479                                  int selectresolution, 
3480                                  const char **Error)
3481 {
3482         int len, rlen;
3483         int nSuccessLess = 0;
3484         fd_set rfds;
3485         char *pch = NULL;
3486         int fdflags;
3487         int IsNonBlock;
3488         struct timeval tv;
3489
3490         if (buf->BufUsed > 0) {
3491                 pch = strchr(buf->buf, '\n');
3492                 if (pch != NULL) {
3493                         rlen = 0;
3494                         len = pch - buf->buf;
3495                         if (len > 0 && (*(pch - 1) == '\r') )
3496                                 rlen ++;
3497                         StrBufSub(Line, buf, 0, len - rlen);
3498                         StrBufCutLeft(buf, len + 1);
3499                         return len - rlen;
3500                 }
3501         }
3502         
3503         if (buf->BufSize - buf->BufUsed < 10)
3504                 IncreaseBuf(buf, 1, -1);
3505
3506         fdflags = fcntl(*fd, F_GETFL);
3507         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3508
3509         while ((nSuccessLess < timeout) && (pch == NULL)) {
3510                 if (IsNonBlock){
3511                         tv.tv_sec = selectresolution;
3512                         tv.tv_usec = 0;
3513                         
3514                         FD_ZERO(&rfds);
3515                         FD_SET(*fd, &rfds);
3516                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
3517                                 *Error = strerror(errno);
3518                                 close (*fd);
3519                                 *fd = -1;
3520                                 return -1;
3521                         }
3522                 }
3523                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
3524                         nSuccessLess ++;
3525                         continue;
3526                 }
3527                 rlen = read(*fd, 
3528                             &buf->buf[buf->BufUsed], 
3529                             buf->BufSize - buf->BufUsed - 1);
3530                 if (rlen < 1) {
3531                         *Error = strerror(errno);
3532                         close(*fd);
3533                         *fd = -1;
3534                         return -1;
3535                 }
3536                 else if (rlen > 0) {
3537                         nSuccessLess = 0;
3538                         buf->BufUsed += rlen;
3539                         buf->buf[buf->BufUsed] = '\0';
3540                         if (buf->BufUsed + 10 > buf->BufSize) {
3541                                 IncreaseBuf(buf, 1, -1);
3542                         }
3543                         pch = strchr(buf->buf, '\n');
3544                         continue;
3545                 }
3546                 
3547         }
3548         if (pch != NULL) {
3549                 rlen = 0;
3550                 len = pch - buf->buf;
3551                 if (len > 0 && (*(pch - 1) == '\r') )
3552                         rlen ++;
3553                 StrBufSub(Line, buf, 0, len - rlen);
3554                 StrBufCutLeft(buf, len + 1);
3555                 return len - rlen;
3556         }
3557         return -1;
3558
3559 }
3560
3561 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
3562 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
3563 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
3564 /**
3565  * @ingroup StrBuf_BufferedIO
3566  * @brief Read a line from socket
3567  * flushes and closes the FD on error
3568  * @param Line where to append our Line read from the fd / I/O Buffer; 
3569  * @param IOBuf the buffer to get the input to; lifetime pair to FD
3570  * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
3571  * @param fd pointer to the filedescriptor to read
3572  * @param timeout number of successless selects until we bail out
3573  * @param selectresolution how long to wait on each select
3574  * @param Error strerror() on error 
3575  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
3576  */
3577 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
3578                                       StrBuf *IOBuf, 
3579                                       const char **Pos,
3580                                       int *fd, 
3581                                       int timeout, 
3582                                       int selectresolution, 
3583                                       const char **Error)
3584 {
3585         const char *pche = NULL;
3586         const char *pos = NULL;
3587         const char *pLF;
3588         int len, rlen, retlen;
3589         int nSuccessLess = 0;
3590         fd_set rfds;
3591         const char *pch = NULL;
3592         int fdflags;
3593         int IsNonBlock;
3594         struct timeval tv;
3595         
3596         retlen = 0;
3597         if ((Line == NULL) ||
3598             (Pos == NULL) ||
3599             (IOBuf == NULL) ||
3600             (*fd == -1))
3601         {
3602                 if (Pos != NULL)
3603                         *Pos = NULL;
3604                 *Error = ErrRBLF_PreConditionFailed;
3605                 return -1;
3606         }
3607
3608         pos = *Pos;
3609         if ((IOBuf->BufUsed > 0) && 
3610             (pos != NULL) && 
3611             (pos < IOBuf->buf + IOBuf->BufUsed)) 
3612         {
3613                 char *pcht;
3614
3615                 pche = IOBuf->buf + IOBuf->BufUsed;
3616                 pch = pos;
3617                 pcht = Line->buf;
3618
3619                 while ((pch < pche) && (*pch != '\n'))
3620                 {
3621                         if (Line->BufUsed + 10 > Line->BufSize)
3622                         {
3623                                 long apos;
3624                                 apos = pcht - Line->buf;
3625                                 *pcht = '\0';
3626                                 IncreaseBuf(Line, 1, -1);
3627                                 pcht = Line->buf + apos;
3628                         }
3629                         *pcht++ = *pch++;
3630                         Line->BufUsed++;
3631                         retlen++;
3632                 }
3633
3634                 len = pch - pos;
3635                 if (len > 0 && (*(pch - 1) == '\r') )
3636                 {
3637                         retlen--;
3638                         len --;
3639                         pcht --;
3640                         Line->BufUsed --;
3641                 }
3642                 *pcht = '\0';
3643
3644                 if ((pch >= pche) || (*pch == '\0'))
3645                 {
3646                         FlushStrBuf(IOBuf);
3647                         *Pos = NULL;
3648                         pch = NULL;
3649                         pos = 0;
3650                 }
3651
3652                 if ((pch != NULL) && 
3653                     (pch <= pche)) 
3654                 {
3655                         if (pch + 1 >= pche) {
3656                                 *Pos = NULL;
3657                                 FlushStrBuf(IOBuf);
3658                         }
3659                         else
3660                                 *Pos = pch + 1;
3661                         
3662                         return retlen;
3663                 }
3664                 else 
3665                         FlushStrBuf(IOBuf);
3666         }
3667
3668         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
3669         
3670         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
3671                 IncreaseBuf(IOBuf, 1, -1);
3672
3673         fdflags = fcntl(*fd, F_GETFL);
3674         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3675
3676         pLF = NULL;
3677         while ((nSuccessLess < timeout) && 
3678                (pLF == NULL) &&
3679                (*fd != -1)) {
3680                 if (IsNonBlock)
3681                 {
3682                         tv.tv_sec = 1;
3683                         tv.tv_usec = 0;
3684                 
3685                         FD_ZERO(&rfds);
3686                         FD_SET(*fd, &rfds);
3687                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
3688                                 *Error = strerror(errno);
3689                                 close (*fd);
3690                                 *fd = -1;
3691                                 if (*Error == NULL)
3692                                         *Error = ErrRBLF_SelectFailed;
3693                                 return -1;
3694                         }
3695                         if (! FD_ISSET(*fd, &rfds) != 0) {
3696                                 nSuccessLess ++;
3697                                 continue;
3698                         }
3699                 }
3700                 rlen = read(*fd, 
3701                             &IOBuf->buf[IOBuf->BufUsed], 
3702                             IOBuf->BufSize - IOBuf->BufUsed - 1);
3703                 if (rlen < 1) {
3704                         *Error = strerror(errno);
3705                         close(*fd);
3706                         *fd = -1;
3707                         return -1;
3708                 }
3709                 else if (rlen > 0) {
3710                         nSuccessLess = 0;
3711                         pLF = IOBuf->buf + IOBuf->BufUsed;
3712                         IOBuf->BufUsed += rlen;
3713                         IOBuf->buf[IOBuf->BufUsed] = '\0';
3714                         
3715                         pche = IOBuf->buf + IOBuf->BufUsed;
3716                         
3717                         while ((pLF < pche) && (*pLF != '\n'))
3718                                 pLF ++;
3719                         if ((pLF >= pche) || (*pLF == '\0'))
3720                                 pLF = NULL;
3721
3722                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
3723                         {
3724                                 long apos = 0;
3725
3726                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
3727                                 IncreaseBuf(IOBuf, 1, -1);      
3728                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
3729                         }
3730
3731                         continue;
3732                 }
3733         }
3734         *Pos = NULL;
3735         if (pLF != NULL) {
3736                 pos = IOBuf->buf;
3737                 len = pLF - pos;
3738                 if (len > 0 && (*(pLF - 1) == '\r') )
3739                         len --;
3740                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
3741                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
3742                 {
3743                         FlushStrBuf(IOBuf);
3744                 }
3745                 else 
3746                         *Pos = pLF + 1;
3747                 return retlen + len;
3748         }
3749         *Error = ErrRBLF_NotEnoughSentFromServer;
3750         return -1;
3751
3752 }
3753
3754 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
3755 /**
3756  * @ingroup StrBuf_IO
3757  * @brief Input binary data from socket
3758  * flushes and closes the FD on error
3759  * @param Buf the buffer to get the input to
3760  * @param fd pointer to the filedescriptor to read
3761  * @param append Append to an existing string or replace?
3762  * @param nBytes the maximal number of bytes to read
3763  * @param Error strerror() on error 
3764  * @returns numbers of chars read
3765  */
3766 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
3767 {
3768         int fdflags;
3769         int len, rlen, slen;
3770         int nSuccessLess;
3771         int nRead = 0;
3772         char *ptr;
3773         int IsNonBlock;
3774         struct timeval tv;
3775         fd_set rfds;
3776
3777         if ((Buf == NULL) || (*fd == -1))
3778         {
3779                 *Error = ErrRBLF_BLOBPreConditionFailed;
3780                 return -1;
3781         }
3782         if (!append)
3783                 FlushStrBuf(Buf);
3784         if (Buf->BufUsed + nBytes >= Buf->BufSize)
3785                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
3786
3787         ptr = Buf->buf + Buf->BufUsed;
3788
3789         slen = len = Buf->BufUsed;
3790
3791         fdflags = fcntl(*fd, F_GETFL);
3792         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3793         nSuccessLess = 0;
3794         while ((nRead < nBytes) && 
3795                (*fd != -1)) 
3796         {
3797                 if (IsNonBlock)
3798                 {
3799                         tv.tv_sec = 1;
3800                         tv.tv_usec = 0;
3801                 
3802                         FD_ZERO(&rfds);
3803                         FD_SET(*fd, &rfds);
3804                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
3805                                 *Error = strerror(errno);
3806                                 close (*fd);
3807                                 *fd = -1;
3808                                 if (*Error == NULL)
3809                                         *Error = ErrRBLF_SelectFailed;
3810                                 return -1;
3811                         }
3812                         if (! FD_ISSET(*fd, &rfds) != 0) {
3813                                 nSuccessLess ++;
3814                                 continue;
3815                         }
3816                 }
3817
3818                 if ((rlen = read(*fd, 
3819                                  ptr,
3820                                  nBytes - nRead)) == -1) {
3821                         close(*fd);
3822                         *fd = -1;
3823                         *Error = strerror(errno);
3824                         return rlen;
3825                 }
3826                 nRead += rlen;
3827                 ptr += rlen;
3828                 Buf->BufUsed += rlen;
3829         }
3830         Buf->buf[Buf->BufUsed] = '\0';
3831         return nRead;
3832 }
3833
3834 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
3835 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
3836 /**
3837  * @ingroup StrBuf_BufferedIO
3838  * @brief Input binary data from socket
3839  * flushes and closes the FD on error
3840  * @param Blob put binary thing here
3841  * @param IOBuf the buffer to get the input to
3842  * @param Pos offset inside of IOBuf
3843  * @param fd pointer to the filedescriptor to read
3844  * @param append Append to an existing string or replace?
3845  * @param nBytes the maximal number of bytes to read
3846  * @param check whether we should search for '000\n' terminators in case of timeouts
3847  * @param Error strerror() on error 
3848  * @returns numbers of chars read
3849  */
3850 int StrBufReadBLOBBuffered(StrBuf *Blob, 
3851                            StrBuf *IOBuf, 
3852                            const char **Pos,
3853                            int *fd, 
3854                            int append, 
3855                            long nBytes, 
3856                            int check, 
3857                            const char **Error)
3858 {
3859         const char *pche;
3860         const char *pos;
3861         int fdflags;
3862         int len = 0;
3863         int rlen, slen;
3864         int nRead = 0;
3865         int nAlreadyRead = 0;
3866         int IsNonBlock;
3867         char *ptr;
3868         fd_set rfds;
3869         const char *pch;
3870         struct timeval tv;
3871         int nSuccessLess = 0;
3872         int MaxTries;
3873
3874         if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
3875         {
3876                 if (*Pos != NULL)
3877                         *Pos = NULL;
3878                 *Error = ErrRBB_BLOBFPreConditionFailed;
3879                 return -1;
3880         }
3881
3882         if (!append)
3883                 FlushStrBuf(Blob);
3884         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
3885                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
3886         
3887         pos = *Pos;
3888
3889         if (pos != NULL)
3890                 len = pos - IOBuf->buf;
3891         rlen = IOBuf->BufUsed - len;
3892
3893
3894         if ((IOBuf->BufUsed > 0) && 
3895             (pos != NULL) && 
3896             (pos < IOBuf->buf + IOBuf->BufUsed)) 
3897         {
3898                 pche = IOBuf->buf + IOBuf->BufUsed;
3899                 pch = pos;
3900
3901                 if (rlen < nBytes) {
3902                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
3903                         Blob->BufUsed += rlen;
3904                         Blob->buf[Blob->BufUsed] = '\0';
3905                         nAlreadyRead = nRead = rlen;
3906                         *Pos = NULL; 
3907                 }
3908                 if (rlen >= nBytes) {
3909                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
3910                         Blob->BufUsed += nBytes;
3911                         Blob->buf[Blob->BufUsed] = '\0';
3912                         if (rlen == nBytes) {
3913                                 *Pos = NULL; 
3914                                 FlushStrBuf(IOBuf);
3915                         }
3916                         else 
3917                                 *Pos += nBytes;
3918                         return nBytes;
3919                 }
3920         }
3921
3922         FlushStrBuf(IOBuf);
3923         *Pos = NULL;
3924         if (IOBuf->BufSize < nBytes - nRead)
3925                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
3926         ptr = IOBuf->buf;
3927
3928         slen = len = Blob->BufUsed;
3929
3930         fdflags = fcntl(*fd, F_GETFL);
3931         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3932         if (IsNonBlock)
3933                 MaxTries =   1000;
3934         else
3935                 MaxTries = 100000;
3936
3937         nBytes -= nRead;
3938         nRead = 0;
3939         while ((nSuccessLess < MaxTries) && 
3940                (nRead < nBytes) &&
3941                (*fd != -1)) {
3942                 if (IsNonBlock)
3943                 {
3944                         tv.tv_sec = 1;
3945                         tv.tv_usec = 0;
3946                 
3947                         FD_ZERO(&rfds);
3948                         FD_SET(*fd, &rfds);
3949                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
3950                                 *Error = strerror(errno);
3951                                 close (*fd);
3952                                 *fd = -1;
3953                                 if (*Error == NULL)
3954                                         *Error = ErrRBLF_SelectFailed;
3955                                 return -1;
3956                         }
3957                         if (! FD_ISSET(*fd, &rfds) != 0) {
3958                                 nSuccessLess ++;
3959                                 continue;
3960                         }
3961                 }
3962                 rlen = read(*fd, 
3963                             ptr,
3964                             IOBuf->BufSize - (ptr - IOBuf->buf));
3965                 if (rlen == -1) {
3966                         close(*fd);
3967                         *fd = -1;
3968                         *Error = strerror(errno);
3969                         return rlen;
3970                 }
3971                 else if (rlen == 0){
3972                         if ((check == NNN_TERM) && 
3973                             (nRead > 5) &&
3974                             (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
3975                         {
3976                                 StrBufPlain(Blob, HKEY("\n000\n"));
3977                                 StrBufCutRight(Blob, 5);
3978                                 return Blob->BufUsed;
3979                         }
3980                         else if (!IsNonBlock) 
3981                                 nSuccessLess ++;
3982                         else if (nSuccessLess > MaxTries) {
3983                                 FlushStrBuf(IOBuf);
3984                                 *Error = ErrRBB_too_many_selects;
3985                                 return -1;
3986                         }
3987                 }
3988                 else if (rlen > 0) {
3989                         nSuccessLess = 0;
3990                         nRead += rlen;
3991                         ptr += rlen;
3992                         IOBuf->BufUsed += rlen;
3993                 }
3994         }
3995         if (nSuccessLess >= MaxTries) {
3996                 FlushStrBuf(IOBuf);
3997                 *Error = ErrRBB_too_many_selects;
3998                 return -1;
3999         }
4000
4001         if (nRead > nBytes) {
4002                 *Pos = IOBuf->buf + nBytes;
4003         }
4004         Blob->buf[Blob->BufUsed] = '\0';
4005         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4006         if (*Pos == NULL) {
4007                 FlushStrBuf(IOBuf);
4008         }
4009         return nRead + nAlreadyRead;
4010 }
4011
4012 /**
4013  * @ingroup StrBuf_IO
4014  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4015  * @param LineBuf your line will be copied here.
4016  * @param Buf BLOB with lines of text...
4017  * @param Ptr moved arround to keep the next-line across several iterations
4018  *        has to be &NULL on start; will be &NotNULL on end of buffer
4019  * @returns size of copied buffer
4020  */
4021 int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
4022 {
4023         const char *aptr, *ptr, *eptr;
4024         char *optr, *xptr;
4025
4026         if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4027                 *Ptr = StrBufNOTNULL;
4028                 return 0;
4029         }
4030
4031         FlushStrBuf(LineBuf);
4032         if (*Ptr==NULL)
4033                 ptr = aptr = Buf->buf;
4034         else
4035                 ptr = aptr = *Ptr;
4036
4037         optr = LineBuf->buf;
4038         eptr = Buf->buf + Buf->BufUsed;
4039         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4040
4041         while ((ptr <= eptr) && 
4042                (*ptr != '\n') &&
4043                (*ptr != '\r') )
4044         {
4045                 *optr = *ptr;
4046                 optr++; ptr++;
4047                 if (optr == xptr) {
4048                         LineBuf->BufUsed = optr - LineBuf->buf;
4049                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4050                         optr = LineBuf->buf + LineBuf->BufUsed;
4051                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4052                 }
4053         }
4054
4055         if ((ptr >= eptr) && (optr > LineBuf->buf))
4056                 optr --;
4057         LineBuf->BufUsed = optr - LineBuf->buf;
4058         *optr = '\0';       
4059         if ((ptr <= eptr) && (*ptr == '\r'))
4060                 ptr ++;
4061         if ((ptr <= eptr) && (*ptr == '\n'))
4062                 ptr ++;
4063         
4064         if (ptr < eptr) {
4065                 *Ptr = ptr;
4066         }
4067         else {
4068                 *Ptr = StrBufNOTNULL;
4069         }
4070
4071         return Buf->BufUsed - (ptr - Buf->buf);
4072 }
4073
4074
4075 /**
4076  * @ingroup StrBuf_IO
4077  * @brief removes double slashes from pathnames
4078  * @param Dir directory string to filter
4079  * @param RemoveTrailingSlash allows / disallows trailing slashes
4080  */
4081 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4082 {
4083         char *a, *b;
4084
4085         a = b = Dir->buf;
4086
4087         while (!IsEmptyStr(a)) {
4088                 if (*a == '/') {
4089                         while (*a == '/')
4090                                 a++;
4091                         *b = '/';
4092                         b++;
4093                 }
4094                 else {
4095                         *b = *a;
4096                         b++; a++;
4097                 }
4098         }
4099         if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
4100                 *b = '/';
4101                 b++;
4102         }
4103         *b = '\0';
4104         Dir->BufUsed = b - Dir->buf;
4105 }
4106
4107