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