2 * Copyright (c) 1987-2011 by the citadel.org team
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.
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.
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
26 #include <sys/select.h>
28 #include <sys/types.h>
29 #define SHOW_ME_VAPPEND_PRINTF
31 #include "libcitadel.h"
43 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
44 const Bytef * source, uLong sourceLen, int level);
46 int BaseStrBufSize = 64;
48 const char *StrBufNOTNULL = ((char*) NULL) - 1;
50 const char HexList[256][3] = {
51 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
52 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
53 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
54 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
55 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
56 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
57 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
58 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
59 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
60 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
61 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
62 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
63 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
64 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
65 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
66 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
69 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
70 * StrBuf is a versatile class, aiding the handling of dynamic strings
71 * * reduce de/reallocations
72 * * reduce the need to remeasure it
73 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
74 * * allow asyncroneous IO for line and Blob based operations
75 * * reduce the use of memove in those
76 * * Quick filling in several operations with append functions
80 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
85 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
87 * use these operators to interfere with code demanding char*;
88 * if you need to own the content, smash me. Avoid, since we loose the length information.
92 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
94 * operations to get your Strings into a StrBuf, manipulating them, or appending
97 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
99 * Quick tokenizer; demands of the user to pull its tokens in sequence
103 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
105 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
109 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
111 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
112 * External Cursor to maintain the current read position inside of the buffer
113 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
117 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
123 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
125 * these functions translate the content of a buffer into another representation;
126 * some are combined Fillers and encoders
130 * Private Structure for the Stringbuffer
133 char *buf; /**< the pointer to the dynamic buffer */
134 long BufSize; /**< how many spcae do we optain */
135 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
136 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
138 long nIncreases; /**< for profiling; cound how many times we needed more */
139 char bt [SIZ]; /**< Stacktrace of last increase */
140 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
145 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
146 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
149 #ifdef HAVE_BACKTRACE
150 static void StrBufBacktrace(StrBuf *Buf, int which)
154 void *stack_frames[50];
159 pstart = pch = Buf->bt;
161 pstart = pch = Buf->bt_lastinc;
162 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
163 strings = backtrace_symbols(stack_frames, size);
164 for (i = 0; i < size; i++) {
166 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
168 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
177 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
179 if (hFreeDbglog == -1){
180 pid_t pid = getpid();
182 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
183 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
185 if ((*FreeMe)->nIncreases > 0)
189 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
191 (*FreeMe)->nIncreases,
195 (*FreeMe)->bt_lastinc);
196 n = write(hFreeDbglog, buf, n);
202 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
206 n = write(hFreeDbglog, buf, n);
210 void dbg_IncreaseBuf(StrBuf *IncMe)
213 #ifdef HAVE_BACKTRACE
214 StrBufBacktrace(Buf, 1);
218 void dbg_Init(StrBuf *Buf)
222 Buf->bt_lastinc[0] = '\0';
223 #ifdef HAVE_BACKTRACE
224 StrBufBacktrace(Buf, 0);
230 #define dbg_FreeStrBuf(a, b)
231 #define dbg_IncreaseBuf(a)
238 * @brief swaps the contents of two StrBufs
239 * this is to be used to have cheap switched between a work-buffer and a target buffer
241 * @param B second one
243 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
247 memcpy(&C, A, sizeof(*A));
248 memcpy(A, B, sizeof(*B));
249 memcpy(B, &C, sizeof(C));
254 * @ingroup StrBuf_Cast
255 * @brief Cast operator to Plain String
256 * @note if the buffer is altered by StrBuf operations, this pointer may become
257 * invalid. So don't lean on it after altering the buffer!
258 * Since this operation is considered cheap, rather call it often than risking
259 * your pointer to become invalid!
260 * @param Str the string we want to get the c-string representation for
261 * @returns the Pointer to the Content. Don't mess with it!
263 inline const char *ChrPtr(const StrBuf *Str)
271 * @ingroup StrBuf_Cast
272 * @brief since we know strlen()'s result, provide it here.
273 * @param Str the string to return the length to
274 * @returns contentlength of the buffer
276 inline int StrLength(const StrBuf *Str)
278 return (Str != NULL) ? Str->BufUsed : 0;
282 * @ingroup StrBuf_DeConstructors
283 * @brief local utility function to resize the buffer
284 * @param Buf the buffer whichs storage we should increase
285 * @param KeepOriginal should we copy the original buffer or just start over with a new one
286 * @param DestSize what should fit in after?
288 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
291 size_t NewSize = Buf->BufSize * 2;
297 while ((NewSize <= DestSize) && (NewSize != 0))
303 NewBuf= (char*) malloc(NewSize);
307 if (KeepOriginal && (Buf->BufUsed > 0))
309 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
318 Buf->BufSize = NewSize;
320 dbg_IncreaseBuf(Buf);
326 * @ingroup StrBuf_DeConstructors
327 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
328 * @param Buf Buffer to shrink (has to be empty)
329 * @param ThreshHold if the buffer is bigger then this, its readjusted
330 * @param NewSize if we Shrink it, how big are we going to be afterwards?
332 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
335 (Buf->BufUsed == 0) &&
336 (Buf->BufSize < ThreshHold)) {
338 Buf->buf = (char*) malloc(NewSize);
340 Buf->BufSize = NewSize;
345 * @ingroup StrBuf_DeConstructors
346 * @brief shrink long term buffers to their real size so they don't waste memory
347 * @param Buf buffer to shrink
348 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
349 * @returns physical size of the buffer
351 long StrBufShrinkToFit(StrBuf *Buf, int Force)
356 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
358 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
359 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
360 Buf->BufSize = Buf->BufUsed + 1;
368 * @ingroup StrBuf_DeConstructors
369 * @brief Allocate a new buffer with default buffer size
370 * @returns the new stringbuffer
372 StrBuf* NewStrBuf(void)
376 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
377 NewBuf->buf = (char*) malloc(BaseStrBufSize);
378 NewBuf->buf[0] = '\0';
379 NewBuf->BufSize = BaseStrBufSize;
381 NewBuf->ConstBuf = 0;
389 * @ingroup StrBuf_DeConstructors
390 * @brief Copy Constructor; returns a duplicate of CopyMe
391 * @param CopyMe Buffer to faxmilate
392 * @returns the new stringbuffer
394 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
401 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
402 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
403 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
404 NewBuf->BufUsed = CopyMe->BufUsed;
405 NewBuf->BufSize = CopyMe->BufSize;
406 NewBuf->ConstBuf = 0;
414 * @ingroup StrBuf_DeConstructors
415 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
416 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
417 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
418 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
419 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
420 * @returns the new stringbuffer
422 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
426 if (CreateRelpaceMe == NULL)
431 if (*CreateRelpaceMe != NULL)
432 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
434 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
438 if (CopyFlushMe == NULL)
440 if (*CreateRelpaceMe != NULL)
441 FlushStrBuf(*CreateRelpaceMe);
443 *CreateRelpaceMe = NewStrBuf();
448 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
449 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
450 * be a big IO-Buffer...
452 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
454 if (*CreateRelpaceMe == NULL)
456 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
461 NewBuf = *CreateRelpaceMe;
464 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
468 if (*CreateRelpaceMe == NULL)
470 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
474 NewBuf = *CreateRelpaceMe;
475 SwapBuffers (NewBuf, CopyFlushMe);
478 FlushStrBuf(CopyFlushMe);
483 * @ingroup StrBuf_DeConstructors
484 * @brief create a new Buffer using an existing c-string
485 * this function should also be used if you want to pre-suggest
486 * the buffer size to allocate in conjunction with ptr == NULL
487 * @param ptr the c-string to copy; may be NULL to create a blank instance
488 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
489 * @returns the new stringbuffer
491 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
494 size_t Siz = BaseStrBufSize;
497 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
499 CopySize = strlen((ptr != NULL)?ptr:"");
503 while ((Siz <= CopySize) && (Siz != 0))
511 NewBuf->buf = (char*) malloc(Siz);
512 if (NewBuf->buf == NULL)
517 NewBuf->BufSize = Siz;
519 memcpy(NewBuf->buf, ptr, CopySize);
520 NewBuf->buf[CopySize] = '\0';
521 NewBuf->BufUsed = CopySize;
524 NewBuf->buf[0] = '\0';
527 NewBuf->ConstBuf = 0;
535 * @ingroup StrBuf_DeConstructors
536 * @brief Set an existing buffer from a c-string
537 * @param Buf buffer to load
538 * @param ptr c-string to put into
539 * @param nChars set to -1 if we should work 0-terminated
540 * @returns the new length of the string
542 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
557 CopySize = strlen(ptr);
561 while ((Siz <= CopySize) && (Siz != 0))
569 if (Siz != Buf->BufSize)
570 IncreaseBuf(Buf, 0, Siz);
571 memcpy(Buf->buf, ptr, CopySize);
572 Buf->buf[CopySize] = '\0';
573 Buf->BufUsed = CopySize;
580 * @ingroup StrBuf_DeConstructors
581 * @brief use strbuf as wrapper for a string constant for easy handling
582 * @param StringConstant a string to wrap
583 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
585 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
589 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
590 NewBuf->buf = (char*) StringConstant;
591 NewBuf->BufSize = SizeOfStrConstant;
592 NewBuf->BufUsed = SizeOfStrConstant;
593 NewBuf->ConstBuf = 1;
602 * @ingroup StrBuf_DeConstructors
603 * @brief flush the content of a Buf; keep its struct
604 * @param buf Buffer to flush
606 int FlushStrBuf(StrBuf *buf)
618 * @ingroup StrBuf_DeConstructors
619 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
620 * @param buf Buffer to wipe
622 int FLUSHStrBuf(StrBuf *buf)
628 if (buf->BufUsed > 0) {
629 memset(buf->buf, 0, buf->BufUsed);
636 int hFreeDbglog = -1;
639 * @ingroup StrBuf_DeConstructors
640 * @brief Release a Buffer
641 * Its a double pointer, so it can NULL your pointer
642 * so fancy SIG11 appear instead of random results
643 * @param FreeMe Pointer Pointer to the buffer to free
645 void FreeStrBuf (StrBuf **FreeMe)
650 dbg_FreeStrBuf(FreeMe, 'F');
652 if (!(*FreeMe)->ConstBuf)
653 free((*FreeMe)->buf);
659 * @ingroup StrBuf_DeConstructors
660 * @brief flatten a Buffer to the Char * we return
661 * Its a double pointer, so it can NULL your pointer
662 * so fancy SIG11 appear instead of random results
663 * The Callee then owns the buffer and is responsible for freeing it.
664 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
665 * @returns the pointer of the buffer; Callee owns the memory thereafter.
667 char *SmashStrBuf (StrBuf **SmashMe)
671 if ((SmashMe == NULL) || (*SmashMe == NULL))
674 dbg_FreeStrBuf(SmashMe, 'S');
676 Ret = (*SmashMe)->buf;
683 * @ingroup StrBuf_DeConstructors
684 * @brief Release the buffer
685 * If you want put your StrBuf into a Hash, use this as Destructor.
686 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
688 void HFreeStrBuf (void *VFreeMe)
690 StrBuf *FreeMe = (StrBuf*)VFreeMe;
694 dbg_FreeStrBuf(SmashMe, 'H');
696 if (!FreeMe->ConstBuf)
702 /*******************************************************************************
703 * Simple string transformations *
704 *******************************************************************************/
708 * @brief Wrapper around atol
710 long StrTol(const StrBuf *Buf)
715 return atol(Buf->buf);
722 * @brief Wrapper around atoi
724 int StrToi(const StrBuf *Buf)
728 if (Buf->BufUsed > 0)
729 return atoi(Buf->buf);
736 * @brief Checks to see if the string is a pure number
737 * @param Buf The buffer to inspect
738 * @returns 1 if its a pure number, 0, if not.
740 int StrBufIsNumber(const StrBuf *Buf) {
742 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
745 strtoll(Buf->buf, &pEnd, 10);
746 if (pEnd == Buf->buf)
748 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
750 if (Buf->buf == pEnd)
756 * @ingroup StrBuf_Filler
757 * @brief modifies a Single char of the Buf
758 * You can point to it via char* or a zero-based integer
759 * @param Buf The buffer to manipulate
760 * @param ptr char* to zero; use NULL if unused
761 * @param nThChar zero based pointer into the string; use -1 if unused
762 * @param PeekValue The Character to place into the position
764 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
769 nThChar = ptr - Buf->buf;
770 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
772 Buf->buf[nThChar] = PeekValue;
777 * @ingroup StrBuf_Filler
778 * @brief modifies a range of chars of the Buf
779 * You can point to it via char* or a zero-based integer
780 * @param Buf The buffer to manipulate
781 * @param ptr char* to zero; use NULL if unused
782 * @param nThChar zero based pointer into the string; use -1 if unused
783 * @param nChars how many chars are to be flushed?
784 * @param PookValue The Character to place into that area
786 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
791 nThChar = ptr - Buf->buf;
792 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
794 if (nThChar + nChars > Buf->BufUsed)
795 nChars = Buf->BufUsed - nThChar;
797 memset(Buf->buf + nThChar, PookValue, nChars);
798 /* just to be shure... */
799 Buf->buf[Buf->BufUsed] = 0;
804 * @ingroup StrBuf_Filler
805 * @brief Append a StringBuffer to the buffer
806 * @param Buf Buffer to modify
807 * @param AppendBuf Buffer to copy at the end of our buffer
808 * @param Offset Should we start copying from an offset?
810 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
812 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
815 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
818 AppendBuf->BufUsed + Buf->BufUsed);
820 memcpy(Buf->buf + Buf->BufUsed,
821 AppendBuf->buf + Offset,
822 AppendBuf->BufUsed - Offset);
823 Buf->BufUsed += AppendBuf->BufUsed - Offset;
824 Buf->buf[Buf->BufUsed] = '\0';
829 * @ingroup StrBuf_Filler
830 * @brief Append a C-String to the buffer
831 * @param Buf Buffer to modify
832 * @param AppendBuf Buffer to copy at the end of our buffer
833 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
834 * @param Offset Should we start copying from an offset?
836 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
839 long BufSizeRequired;
841 if ((AppendBuf == NULL) || (Buf == NULL))
845 aps = strlen(AppendBuf + Offset);
847 aps = AppendSize - Offset;
849 BufSizeRequired = Buf->BufUsed + aps + 1;
850 if (Buf->BufSize <= BufSizeRequired)
851 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
853 memcpy(Buf->buf + Buf->BufUsed,
857 Buf->buf[Buf->BufUsed] = '\0';
861 * @ingroup StrBuf_Filler
862 * @brief sprintf like function appending the formated string to the buffer
863 * vsnprintf version to wrap into own calls
864 * @param Buf Buffer to extend by format and Params
865 * @param format printf alike format to add
866 * @param ap va_list containing the items for format
868 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
876 if ((Buf == NULL) || (format == NULL))
879 BufSize = Buf->BufSize;
880 nWritten = Buf->BufSize + 1;
881 Offset = Buf->BufUsed;
882 newused = Offset + nWritten;
884 while (newused >= BufSize) {
886 nWritten = vsnprintf(Buf->buf + Offset,
887 Buf->BufSize - Offset,
890 newused = Offset + nWritten;
891 if (newused >= Buf->BufSize) {
892 if (IncreaseBuf(Buf, 1, newused) == -1)
893 return; /* TODO: error handling? */
894 newused = Buf->BufSize + 1;
897 Buf->BufUsed = Offset + nWritten;
898 BufSize = Buf->BufSize;
905 * @ingroup StrBuf_Filler
906 * @brief sprintf like function appending the formated string to the buffer
907 * @param Buf Buffer to extend by format and Params
908 * @param format printf alike format to add
910 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
918 if ((Buf == NULL) || (format == NULL))
921 BufSize = Buf->BufSize;
922 nWritten = Buf->BufSize + 1;
923 Offset = Buf->BufUsed;
924 newused = Offset + nWritten;
926 while (newused >= BufSize) {
927 va_start(arg_ptr, format);
928 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
929 Buf->BufSize - Buf->BufUsed,
932 newused = Buf->BufUsed + nWritten;
933 if (newused >= Buf->BufSize) {
934 if (IncreaseBuf(Buf, 1, newused) == -1)
935 return; /* TODO: error handling? */
936 newused = Buf->BufSize + 1;
939 Buf->BufUsed += nWritten;
940 BufSize = Buf->BufSize;
947 * @ingroup StrBuf_Filler
948 * @brief sprintf like function putting the formated string into the buffer
949 * @param Buf Buffer to extend by format and Parameters
950 * @param format printf alike format to add
952 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
957 if ((Buf == NULL) || (format == NULL))
960 nWritten = Buf->BufSize + 1;
961 while (nWritten >= Buf->BufSize) {
962 va_start(arg_ptr, format);
963 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
965 if (nWritten >= Buf->BufSize) {
966 if (IncreaseBuf(Buf, 0, 0) == -1)
967 return; /* TODO: error handling? */
968 nWritten = Buf->BufSize + 1;
971 Buf->BufUsed = nWritten ;
976 * @ingroup StrBuf_Filler
977 * @brief Callback for cURL to append the webserver reply to a buffer
978 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
979 * @param size pre-defined by the cURL API; see man 3 curl for mre info
980 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
981 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
983 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
992 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
999 * @brief extracts a substring from Source into dest
1000 * @param dest buffer to place substring into
1001 * @param Source string to copy substring from
1002 * @param Offset chars to skip from start
1003 * @param nChars number of chars to copy
1004 * @returns the number of chars copied; may be different from nChars due to the size of Source
1006 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1008 size_t NCharsRemain;
1009 if (Offset > Source->BufUsed)
1015 if (Offset + nChars < Source->BufUsed)
1017 if (nChars >= dest->BufSize)
1018 IncreaseBuf(dest, 0, nChars + 1);
1019 memcpy(dest->buf, Source->buf + Offset, nChars);
1020 dest->BufUsed = nChars;
1021 dest->buf[dest->BufUsed] = '\0';
1024 NCharsRemain = Source->BufUsed - Offset;
1025 if (NCharsRemain >= dest->BufSize)
1026 IncreaseBuf(dest, 0, NCharsRemain + 1);
1027 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1028 dest->BufUsed = NCharsRemain;
1029 dest->buf[dest->BufUsed] = '\0';
1030 return NCharsRemain;
1035 * @brief Cut nChars from the start of the string
1036 * @param Buf Buffer to modify
1037 * @param nChars how many chars should be skipped?
1039 void StrBufCutLeft(StrBuf *Buf, int nChars)
1041 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1042 if (nChars >= Buf->BufUsed) {
1046 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1047 Buf->BufUsed -= nChars;
1048 Buf->buf[Buf->BufUsed] = '\0';
1053 * @brief Cut the trailing n Chars from the string
1054 * @param Buf Buffer to modify
1055 * @param nChars how many chars should be trunkated?
1057 void StrBufCutRight(StrBuf *Buf, int nChars)
1059 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1060 if (nChars >= Buf->BufUsed) {
1064 Buf->BufUsed -= nChars;
1065 Buf->buf[Buf->BufUsed] = '\0';
1070 * @brief Cut the string after n Chars
1071 * @param Buf Buffer to modify
1072 * @param AfternChars after how many chars should we trunkate the string?
1073 * @param At if non-null and points inside of our string, cut it there.
1075 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1077 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1079 AfternChars = At - Buf->buf;
1082 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1084 Buf->BufUsed = AfternChars;
1085 Buf->buf[Buf->BufUsed] = '\0';
1091 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1092 * @param Buf the string to modify
1094 void StrBufTrim(StrBuf *Buf)
1097 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1099 while ((Buf->BufUsed > 0) &&
1100 isspace(Buf->buf[Buf->BufUsed - 1]))
1104 Buf->buf[Buf->BufUsed] = '\0';
1106 if (Buf->BufUsed == 0) return;
1108 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1111 if (delta > 0) StrBufCutLeft(Buf, delta);
1115 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1116 * @param Buf the string to modify
1118 void StrBufSpaceToBlank(StrBuf *Buf)
1122 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1125 pche = pch + Buf->BufUsed;
1134 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1142 pLeft = pBuff = Buf->buf;
1143 while (pBuff != NULL) {
1145 pBuff = strchr(pBuff, leftboundary);
1154 pRight = strchr(pBuff, rightboundary);
1156 StrBufCutAt(Buf, 0, pRight);
1158 StrBufCutLeft(Buf, pLeft - Buf->buf);
1163 * @ingroup StrBuf_Filler
1164 * @brief uppercase the contents of a buffer
1165 * @param Buf the buffer to translate
1167 void StrBufUpCase(StrBuf *Buf)
1171 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1174 pche = pch + Buf->BufUsed;
1175 while (pch < pche) {
1176 *pch = toupper(*pch);
1183 * @ingroup StrBuf_Filler
1184 * @brief lowercase the contents of a buffer
1185 * @param Buf the buffer to translate
1187 void StrBufLowerCase(StrBuf *Buf)
1191 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1194 pche = pch + Buf->BufUsed;
1195 while (pch < pche) {
1196 *pch = tolower(*pch);
1202 /*******************************************************************************
1203 * a tokenizer that kills, maims, and destroys *
1204 *******************************************************************************/
1207 * @ingroup StrBuf_Tokenizer
1208 * @brief Replace a token at a given place with a given length by another token with given length
1209 * @param Buf String where to work on
1210 * @param where where inside of the Buf is the search-token
1211 * @param HowLong How long is the token to be replaced
1212 * @param Repl Token to insert at 'where'
1213 * @param ReplLen Length of repl
1214 * @returns -1 if fail else length of resulting Buf
1216 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1217 const char *Repl, long ReplLen)
1220 if ((Buf == NULL) ||
1221 (where > Buf->BufUsed) ||
1222 (where + HowLong > Buf->BufUsed))
1225 if (where + ReplLen - HowLong > Buf->BufSize)
1226 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1229 memmove(Buf->buf + where + ReplLen,
1230 Buf->buf + where + HowLong,
1231 Buf->BufUsed - where - HowLong);
1233 memcpy(Buf->buf + where,
1236 Buf->BufUsed += ReplLen - HowLong;
1238 return Buf->BufUsed;
1242 * @ingroup StrBuf_Tokenizer
1243 * @brief Counts the numbmer of tokens in a buffer
1244 * @param source String to count tokens in
1245 * @param tok Tokenizer char to count
1246 * @returns numbers of tokenizer chars found
1248 int StrBufNum_tokens(const StrBuf *source, char tok)
1252 if ((source == NULL) || (source->BufUsed == 0))
1254 if ((source->BufUsed == 1) && (*source->buf == tok))
1258 pche = pch + source->BufUsed;
1269 * @ingroup StrBuf_Tokenizer
1270 * @brief a string tokenizer
1271 * @param Source StringBuffer to read into
1272 * @param parmnum n'th Parameter to remove
1273 * @param separator tokenizer character
1274 * @returns -1 if not found, else length of token.
1276 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1279 char *d, *s, *end; /* dest, source */
1282 /* Find desired @parameter */
1283 end = Source->buf + Source->BufUsed;
1285 while ((d <= end) &&
1288 /* End of string, bail! */
1293 if (*d == separator) {
1298 if ((d == NULL) || (d >= end))
1299 return 0; /* @Parameter not found */
1301 /* Find next @parameter */
1303 while ((s <= end) &&
1304 (*s && *s != separator))
1308 if (*s == separator)
1312 /* Hack and slash */
1317 memmove(d, s, Source->BufUsed - (s - Source->buf));
1318 Source->BufUsed += ReducedBy;
1319 Source->buf[Source->BufUsed] = '\0';
1321 else if (d == Source->buf) {
1323 Source->BufUsed = 0;
1327 Source->BufUsed += ReducedBy;
1340 * @ingroup StrBuf_Tokenizer
1341 * @brief a string tokenizer
1342 * @param dest Destination StringBuffer
1343 * @param Source StringBuffer to read into
1344 * @param parmnum n'th Parameter to extract
1345 * @param separator tokenizer character
1346 * @returns -1 if not found, else length of token.
1348 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1350 const char *s, *e; //* source * /
1351 int len = 0; //* running total length of extracted string * /
1352 int current_token = 0; //* token currently being processed * /
1355 dest->buf[0] = '\0';
1361 if ((Source == NULL) || (Source->BufUsed ==0)) {
1365 e = s + Source->BufUsed;
1368 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1370 while ((s < e) && !IsEmptyStr(s)) {
1371 if (*s == separator) {
1374 if (len >= dest->BufSize) {
1375 dest->BufUsed = len;
1376 if (IncreaseBuf(dest, 1, -1) < 0) {
1381 if ( (current_token == parmnum) &&
1382 (*s != separator)) {
1383 dest->buf[len] = *s;
1386 else if (current_token > parmnum) {
1392 dest->buf[len] = '\0';
1393 dest->BufUsed = len;
1395 if (current_token < parmnum) {
1396 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1399 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1408 * @ingroup StrBuf_Tokenizer
1409 * @brief a string tokenizer to fetch an integer
1410 * @param Source String containing tokens
1411 * @param parmnum n'th Parameter to extract
1412 * @param separator tokenizer character
1413 * @returns 0 if not found, else integer representation of the token
1415 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1425 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1432 * @ingroup StrBuf_Tokenizer
1433 * @brief a string tokenizer to fetch a long integer
1434 * @param Source String containing tokens
1435 * @param parmnum n'th Parameter to extract
1436 * @param separator tokenizer character
1437 * @returns 0 if not found, else long integer representation of the token
1439 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1449 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1457 * @ingroup StrBuf_Tokenizer
1458 * @brief a string tokenizer to fetch an unsigned long
1459 * @param Source String containing tokens
1460 * @param parmnum n'th Parameter to extract
1461 * @param separator tokenizer character
1462 * @returns 0 if not found, else unsigned long representation of the token
1464 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1475 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1479 return (unsigned long) atol(pnum);
1488 * @ingroup StrBuf_NextTokenizer
1489 * @brief a string tokenizer; Bounds checker
1490 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1491 * @param Source our tokenbuffer
1492 * @param pStart the token iterator pointer to inspect
1493 * @returns whether the revolving pointer is inside of the search range
1495 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1497 if ((Source == NULL) ||
1498 (*pStart == StrBufNOTNULL) ||
1499 (Source->BufUsed == 0))
1503 if (*pStart == NULL)
1507 else if (*pStart > Source->buf + Source->BufUsed)
1511 else if (*pStart <= Source->buf)
1520 * @ingroup StrBuf_NextTokenizer
1521 * @brief a string tokenizer
1522 * @param dest Destination StringBuffer
1523 * @param Source StringBuffer to read into
1524 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1525 * @param separator tokenizer
1526 * @returns -1 if not found, else length of token.
1528 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1530 const char *s; /* source */
1531 const char *EndBuffer; /* end stop of source buffer */
1532 int current_token = 0; /* token currently being processed */
1533 int len = 0; /* running total length of extracted string */
1535 if ((Source == NULL) ||
1536 (Source->BufUsed == 0) )
1538 *pStart = StrBufNOTNULL;
1544 EndBuffer = Source->buf + Source->BufUsed;
1548 dest->buf[0] = '\0';
1553 *pStart = EndBuffer + 1;
1557 if (*pStart == NULL)
1559 *pStart = Source->buf; /* we're starting to examine this buffer. */
1561 else if ((*pStart < Source->buf) ||
1562 (*pStart > EndBuffer ) )
1564 return -1; /* no more tokens to find. */
1568 /* start to find the next token */
1569 while ((s <= EndBuffer) &&
1570 (current_token == 0) )
1572 if (*s == separator)
1574 /* we found the next token */
1578 if (len >= dest->BufSize)
1580 /* our Dest-buffer isn't big enough, increase it. */
1581 dest->BufUsed = len;
1583 if (IncreaseBuf(dest, 1, -1) < 0) {
1584 /* WHUT? no more mem? bail out. */
1591 if ( (current_token == 0 ) && /* are we in our target token? */
1592 (!IsEmptyStr(s) ) &&
1593 (separator != *s) ) /* don't copy the token itself */
1595 dest->buf[len] = *s; /* Copy the payload */
1596 ++len; /* remember the bigger size. */
1602 /* did we reach the end? */
1603 if ((s > EndBuffer)) {
1604 EndBuffer = StrBufNOTNULL;
1605 *pStart = EndBuffer;
1608 *pStart = s; /* remember the position for the next run */
1611 /* sanitize our extracted token */
1612 dest->buf[len] = '\0';
1613 dest->BufUsed = len;
1620 * @ingroup StrBuf_NextTokenizer
1621 * @brief a string tokenizer
1622 * @param Source StringBuffer to read from
1623 * @param pStart pointer to the end of the last token. Feed with NULL.
1624 * @param separator tokenizer character
1625 * @param nTokens number of tokens to fastforward over
1626 * @returns -1 if not found, else length of token.
1628 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1630 const char *s, *EndBuffer; //* source * /
1631 int len = 0; //* running total length of extracted string * /
1632 int current_token = 0; //* token currently being processed * /
1634 if ((Source == NULL) ||
1635 (Source->BufUsed ==0)) {
1639 return Source->BufUsed;
1641 if (*pStart == NULL)
1642 *pStart = Source->buf;
1644 EndBuffer = Source->buf + Source->BufUsed;
1646 if ((*pStart < Source->buf) ||
1647 (*pStart > EndBuffer)) {
1655 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1657 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1658 if (*s == separator) {
1661 if (current_token >= nTokens) {
1673 * @ingroup StrBuf_NextTokenizer
1674 * @brief a string tokenizer to fetch an integer
1675 * @param Source StringBuffer to read from
1676 * @param pStart Cursor on the tokenstring
1677 * @param separator tokenizer character
1678 * @returns 0 if not found, else integer representation of the token
1680 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1690 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1697 * @ingroup StrBuf_NextTokenizer
1698 * @brief a string tokenizer to fetch a long integer
1699 * @param Source StringBuffer to read from
1700 * @param pStart Cursor on the tokenstring
1701 * @param separator tokenizer character
1702 * @returns 0 if not found, else long integer representation of the token
1704 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1714 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1722 * @ingroup StrBuf_NextTokenizer
1723 * @brief a string tokenizer to fetch an unsigned long
1724 * @param Source StringBuffer to read from
1725 * @param pStart Cursor on the tokenstring
1726 * @param separator tokenizer character
1727 * @returns 0 if not found, else unsigned long representation of the token
1729 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1740 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1744 return (unsigned long) atol(pnum);
1754 /*******************************************************************************
1755 * Escape Appending *
1756 *******************************************************************************/
1759 * @ingroup StrBuf_DeEnCoder
1760 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1761 * @param OutBuf the output buffer
1762 * @param In Buffer to encode
1763 * @param PlainIn way in from plain old c strings
1765 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1767 const char *pch, *pche;
1771 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1773 if (PlainIn != NULL) {
1774 len = strlen(PlainIn);
1780 pche = pch + In->BufUsed;
1787 pt = OutBuf->buf + OutBuf->BufUsed;
1788 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1790 while (pch < pche) {
1792 IncreaseBuf(OutBuf, 1, -1);
1793 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1794 pt = OutBuf->buf + OutBuf->BufUsed;
1797 if((*pch >= 'a' && *pch <= 'z') ||
1798 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1799 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1800 (*pch == '!') || (*pch == '_') ||
1801 (*pch == ',') || (*pch == '.'))
1808 *(pt + 1) = HexList[(unsigned char)*pch][0];
1809 *(pt + 2) = HexList[(unsigned char)*pch][1];
1811 OutBuf->BufUsed += 3;
1819 * @ingroup StrBuf_DeEnCoder
1820 * @brief append a string in hex encoding to the buffer
1821 * @param OutBuf the output buffer
1822 * @param In Buffer to encode
1823 * @param PlainIn way in from plain old c strings
1824 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1826 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1828 const unsigned char *pch, *pche;
1832 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1834 if (PlainIn != NULL) {
1836 len = strlen((const char*)PlainIn);
1843 pch = (const unsigned char*)In->buf;
1844 pche = pch + In->BufUsed;
1851 pt = OutBuf->buf + OutBuf->BufUsed;
1852 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1854 while (pch < pche) {
1856 IncreaseBuf(OutBuf, 1, -1);
1857 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1858 pt = OutBuf->buf + OutBuf->BufUsed;
1861 *pt = HexList[*pch][0];
1863 *pt = HexList[*pch][1];
1864 pt ++; pch ++; OutBuf->BufUsed += 2;
1870 * @ingroup StrBuf_DeEnCoder
1871 * @brief append a string in hex encoding to the buffer
1872 * @param OutBuf the output buffer
1873 * @param In Buffer to encode
1874 * @param PlainIn way in from plain old c strings
1876 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1878 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1882 * @ingroup StrBuf_DeEnCoder
1883 * @brief Append a string, escaping characters which have meaning in HTML.
1885 * @param Target target buffer
1886 * @param Source source buffer; set to NULL if you just have a C-String
1887 * @param PlainIn Plain-C string to append; set to NULL if unused
1888 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1889 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1890 * if set to 2, linebreaks are replaced by <br/>
1892 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1894 const char *aptr, *eiptr;
1898 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1901 if (PlainIn != NULL) {
1903 len = strlen(PlainIn);
1908 eiptr = aptr + Source->BufUsed;
1909 len = Source->BufUsed;
1915 bptr = Target->buf + Target->BufUsed;
1916 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1918 while (aptr < eiptr){
1920 IncreaseBuf(Target, 1, -1);
1921 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1922 bptr = Target->buf + Target->BufUsed;
1925 memcpy(bptr, "<", 4);
1927 Target->BufUsed += 4;
1929 else if (*aptr == '>') {
1930 memcpy(bptr, ">", 4);
1932 Target->BufUsed += 4;
1934 else if (*aptr == '&') {
1935 memcpy(bptr, "&", 5);
1937 Target->BufUsed += 5;
1939 else if (*aptr == '"') {
1940 memcpy(bptr, """, 6);
1942 Target->BufUsed += 6;
1944 else if (*aptr == '\'') {
1945 memcpy(bptr, "'", 5);
1947 Target->BufUsed += 5;
1949 else if (*aptr == LB) {
1954 else if (*aptr == RB) {
1959 else if (*aptr == QU) {
1964 else if ((*aptr == 32) && (nbsp == 1)) {
1965 memcpy(bptr, " ", 6);
1967 Target->BufUsed += 6;
1969 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1970 *bptr='\0'; /* nothing */
1972 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1973 memcpy(bptr, "<br/>", 11);
1975 Target->BufUsed += 11;
1979 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1980 *bptr='\0'; /* nothing */
1990 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
1992 return Target->BufUsed;
1996 * @ingroup StrBuf_DeEnCoder
1997 * @brief Append a string, escaping characters which have meaning in HTML.
1998 * Converts linebreaks into blanks; escapes single quotes
1999 * @param Target target buffer
2000 * @param Source source buffer; set to NULL if you just have a C-String
2001 * @param PlainIn Plain-C string to append; set to NULL if unused
2003 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2005 const char *aptr, *eiptr;
2009 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2012 if (PlainIn != NULL) {
2014 len = strlen(PlainIn);
2019 eiptr = aptr + Source->BufUsed;
2020 len = Source->BufUsed;
2026 eptr = Target->buf + Target->BufSize - 8;
2027 tptr = Target->buf + Target->BufUsed;
2029 while (aptr < eiptr){
2031 IncreaseBuf(Target, 1, -1);
2032 eptr = Target->buf + Target->BufSize - 8;
2033 tptr = Target->buf + Target->BufUsed;
2036 if (*aptr == '\n') {
2040 else if (*aptr == '\r') {
2044 else if (*aptr == '\'') {
2050 Target->BufUsed += 5;
2063 * @ingroup StrBuf_DeEnCoder
2064 * @brief Append a string, escaping characters which have meaning in ICAL.
2066 * @param Target target buffer
2067 * @param Source source buffer; set to NULL if you just have a C-String
2068 * @param PlainIn Plain-C string to append; set to NULL if unused
2070 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2072 const char *aptr, *eiptr;
2076 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2079 if (PlainIn != NULL) {
2081 len = strlen(PlainIn);
2086 eiptr = aptr + Source->BufUsed;
2087 len = Source->BufUsed;
2093 eptr = Target->buf + Target->BufSize - 8;
2094 tptr = Target->buf + Target->BufUsed;
2096 while (aptr < eiptr){
2097 if(tptr + 3 >= eptr) {
2098 IncreaseBuf(Target, 1, -1);
2099 eptr = Target->buf + Target->BufSize - 8;
2100 tptr = Target->buf + Target->BufUsed;
2103 if (*aptr == '\n') {
2110 else if (*aptr == '\r') {
2117 else if (*aptr == ',') {
2133 * @ingroup StrBuf_DeEnCoder
2134 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2136 * @param Target target buffer
2137 * @param Source source buffer; set to NULL if you just have a C-String
2138 * @param PlainIn Plain-C string to append; set to NULL if unused
2139 * @returns size of result or -1
2141 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2143 const char *aptr, *eiptr;
2148 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2151 if (PlainIn != NULL) {
2153 len = strlen(PlainIn);
2158 eiptr = aptr + Source->BufUsed;
2159 len = Source->BufUsed;
2165 bptr = Target->buf + Target->BufUsed;
2166 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2168 while (aptr < eiptr){
2170 IncreaseBuf(Target, 1, -1);
2171 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2172 bptr = Target->buf + Target->BufUsed;
2176 memcpy(bptr, HKEY("\\n"));
2178 Target->BufUsed += 2;
2181 memcpy(bptr, HKEY("\\r"));
2183 Target->BufUsed += 2;
2190 Target->BufUsed += 2;
2193 if ((*(aptr + 1) == 'u') &&
2194 isxdigit(*(aptr + 2)) &&
2195 isxdigit(*(aptr + 3)) &&
2196 isxdigit(*(aptr + 4)) &&
2197 isxdigit(*(aptr + 5)))
2198 { /* oh, a unicode escaper. let it pass through. */
2199 memcpy(bptr, aptr, 6);
2202 Target->BufUsed += 6;
2210 Target->BufUsed += 2;
2218 Target->BufUsed += 2;
2225 Target->BufUsed += 2;
2232 Target->BufUsed += 2;
2235 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2236 while (IsUtf8Sequence > 0){
2239 if (--IsUtf8Sequence)
2247 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2249 return Target->BufUsed;
2253 * @ingroup StrBuf_DeEnCoder
2254 * @brief Append a string, escaping characters which have meaning in HTML + json.
2256 * @param Target target buffer
2257 * @param Source source buffer; set to NULL if you just have a C-String
2258 * @param PlainIn Plain-C string to append; set to NULL if unused
2259 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2260 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2261 * if set to 2, linebreaks are replaced by <br/>
2263 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2265 const char *aptr, *eiptr;
2268 int IsUtf8Sequence = 0;
2270 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2273 if (PlainIn != NULL) {
2275 len = strlen(PlainIn);
2280 eiptr = aptr + Source->BufUsed;
2281 len = Source->BufUsed;
2287 bptr = Target->buf + Target->BufUsed;
2288 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2290 while (aptr < eiptr){
2292 IncreaseBuf(Target, 1, -1);
2293 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2294 bptr = Target->buf + Target->BufUsed;
2298 memcpy(bptr, HKEY("<"));
2300 Target->BufUsed += 4;
2303 memcpy(bptr, HKEY(">"));
2305 Target->BufUsed += 4;
2308 memcpy(bptr, HKEY("&"));
2310 Target->BufUsed += 5;
2323 switch (nolinebreaks) {
2325 *bptr='\0'; /* nothing */
2328 memcpy(bptr, HKEY("<br/>"));
2330 Target->BufUsed += 11;
2333 memcpy(bptr, HKEY("\\n"));
2335 Target->BufUsed += 2;
2339 switch (nolinebreaks) {
2342 *bptr='\0'; /* nothing */
2345 memcpy(bptr, HKEY("\\r"));
2347 Target->BufUsed += 2;
2357 Target->BufUsed += 2;
2360 if ((*(aptr + 1) == 'u') &&
2361 isxdigit(*(aptr + 2)) &&
2362 isxdigit(*(aptr + 3)) &&
2363 isxdigit(*(aptr + 4)) &&
2364 isxdigit(*(aptr + 5)))
2365 { /* oh, a unicode escaper. let it pass through. */
2366 memcpy(bptr, aptr, 6);
2369 Target->BufUsed += 6;
2377 Target->BufUsed += 2;
2385 Target->BufUsed += 2;
2392 Target->BufUsed += 2;
2399 Target->BufUsed += 2;
2403 memcpy(bptr, HKEY(" "));
2405 Target->BufUsed += 6;
2409 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2410 while (IsUtf8Sequence > 0){
2413 if (--IsUtf8Sequence)
2421 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2423 return Target->BufUsed;
2427 * @ingroup StrBuf_DeEnCoder
2428 * @brief unhide special chars hidden to the HTML escaper
2429 * @param target buffer to put the unescaped string in
2430 * @param source buffer to unescape
2432 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2438 FlushStrBuf(target);
2440 if (source == NULL ||target == NULL)
2445 len = source->BufUsed;
2446 for (a = 0; a < len; ++a) {
2447 if (target->BufUsed >= target->BufSize)
2448 IncreaseBuf(target, 1, -1);
2450 if (source->buf[a] == '=') {
2451 hex[0] = source->buf[a + 1];
2452 hex[1] = source->buf[a + 2];
2455 sscanf(hex, "%02x", &b);
2456 target->buf[target->BufUsed] = b;
2457 target->buf[++target->BufUsed] = 0;
2461 target->buf[target->BufUsed] = source->buf[a];
2462 target->buf[++target->BufUsed] = 0;
2469 * @ingroup StrBuf_DeEnCoder
2470 * @brief hide special chars from the HTML escapers and friends
2471 * @param target buffer to put the escaped string in
2472 * @param source buffer to escape
2474 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2479 FlushStrBuf(target);
2481 if (source == NULL ||target == NULL)
2486 len = source->BufUsed;
2487 for (i=0; i<len; ++i) {
2488 if (target->BufUsed + 4 >= target->BufSize)
2489 IncreaseBuf(target, 1, -1);
2490 if ( (isalnum(source->buf[i])) ||
2491 (source->buf[i]=='-') ||
2492 (source->buf[i]=='_') ) {
2493 target->buf[target->BufUsed++] = source->buf[i];
2496 sprintf(&target->buf[target->BufUsed],
2498 (0xFF &source->buf[i]));
2499 target->BufUsed += 3;
2502 target->buf[target->BufUsed + 1] = '\0';
2506 /*******************************************************************************
2507 * Quoted Printable de/encoding *
2508 *******************************************************************************/
2511 * @ingroup StrBuf_DeEnCoder
2512 * @brief decode a buffer from base 64 encoding; destroys original
2513 * @param Buf Buffor to transform
2515 int StrBufDecodeBase64(StrBuf *Buf)
2519 if (Buf == NULL) return -1;
2521 xferbuf = (char*) malloc(Buf->BufSize);
2523 siz = CtdlDecodeBase64(xferbuf,
2533 * @ingroup StrBuf_DeEnCoder
2534 * @brief decode a buffer from base 64 encoding; destroys original
2535 * @param Buf Buffor to transform
2537 int StrBufDecodeHex(StrBuf *Buf)
2540 char *pch, *pche, *pchi;
2542 if (Buf == NULL) return -1;
2544 pch = pchi = Buf->buf;
2545 pche = pch + Buf->BufUsed;
2547 while (pchi < pche){
2548 ch = decode_hex(pchi);
2555 Buf->BufUsed = pch - Buf->buf;
2556 return Buf->BufUsed;
2560 * @ingroup StrBuf_DeEnCoder
2561 * @brief replace all chars >0x20 && < 0x7F with Mute
2562 * @param Mute char to put over invalid chars
2563 * @param Buf Buffor to transform
2565 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2569 if (Buf == NULL) return -1;
2570 pch = (unsigned char *)Buf->buf;
2571 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2572 if ((*pch < 0x20) || (*pch > 0x7F))
2576 return Buf->BufUsed;
2581 * @ingroup StrBuf_DeEnCoder
2582 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2583 * @param Buf Buffer to translate
2584 * @param StripBlanks Reduce several blanks to one?
2586 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2595 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2596 Buf->buf[Buf->BufUsed - 1] = '\0';
2601 while (a < Buf->BufUsed) {
2602 if (Buf->buf[a] == '+')
2604 else if (Buf->buf[a] == '%') {
2605 /* don't let % chars through, rather truncate the input. */
2606 if (a + 2 > Buf->BufUsed) {
2611 hex[0] = Buf->buf[a + 1];
2612 hex[1] = Buf->buf[a + 2];
2615 sscanf(hex, "%02x", &b);
2616 Buf->buf[a] = (char) b;
2617 len = Buf->BufUsed - a - 2;
2619 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2631 * @ingroup StrBuf_DeEnCoder
2632 * @brief RFC2047-encode a header field if necessary.
2633 * If no non-ASCII characters are found, the string
2634 * will be copied verbatim without encoding.
2636 * @param target Target buffer.
2637 * @param source Source string to be encoded.
2638 * @returns encoded length; -1 if non success.
2640 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2642 const char headerStr[] = "=?UTF-8?Q?";
2643 int need_to_encode = 0;
2647 if ((source == NULL) ||
2651 while ((i < source->BufUsed) &&
2652 (!IsEmptyStr (&source->buf[i])) &&
2653 (need_to_encode == 0)) {
2654 if (((unsigned char) source->buf[i] < 32) ||
2655 ((unsigned char) source->buf[i] > 126)) {
2661 if (!need_to_encode) {
2662 if (*target == NULL) {
2663 *target = NewStrBufPlain(source->buf, source->BufUsed);
2666 FlushStrBuf(*target);
2667 StrBufAppendBuf(*target, source, 0);
2669 return (*target)->BufUsed;
2671 if (*target == NULL)
2672 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2673 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2674 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2675 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2676 (*target)->BufUsed = sizeof(headerStr) - 1;
2677 for (i=0; (i < source->BufUsed); ++i) {
2678 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2679 IncreaseBuf(*target, 1, 0);
2680 ch = (unsigned char) source->buf[i];
2690 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2691 (*target)->BufUsed += 3;
2695 (*target)->buf[(*target)->BufUsed] = '_';
2697 (*target)->buf[(*target)->BufUsed] = ch;
2698 (*target)->BufUsed++;
2702 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2703 IncreaseBuf(*target, 1, 0);
2705 (*target)->buf[(*target)->BufUsed++] = '?';
2706 (*target)->buf[(*target)->BufUsed++] = '=';
2707 (*target)->buf[(*target)->BufUsed] = '\0';
2708 return (*target)->BufUsed;;
2713 static void AddRecipient(StrBuf *Target,
2715 StrBuf *EmailAddress,
2720 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2721 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2723 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2724 StrBufRFC2047encode(&EncBuf, UserName);
2725 StrBufAppendBuf(Target, EncBuf, 0);
2726 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2727 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2729 if (StrLength(EmailAddress) > 0){
2730 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2731 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2732 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2738 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2739 * \param Recp Source list of email recipients
2740 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2741 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2742 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2743 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2745 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2747 StrBuf *EmailAddress,
2751 const char *pch, *pche;
2752 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2754 if ((Recp == NULL) || (StrLength(Recp) == 0))
2758 pche = pch + StrLength(Recp);
2760 if (!CheckEncode(pch, -1, pche))
2761 return NewStrBufDup(Recp);
2763 Target = NewStrBufPlain(NULL, StrLength(Recp));
2765 while ((pch != NULL) && (pch < pche))
2767 while (isspace(*pch)) pch++;
2768 UserStart = UserEnd = EmailStart = EmailEnd = NULL;
2770 if ((*pch == '"') || (*pch == '\'')) {
2771 UserStart = pch + 1;
2773 UserEnd = strchr(UserStart, *pch);
2774 if (UserEnd == NULL)
2775 break; ///TODO: Userfeedback??
2776 EmailStart = UserEnd + 1;
2777 while (isspace(*EmailStart))
2779 if (UserEnd == UserStart) {
2780 UserStart = UserEnd = NULL;
2783 if (*EmailStart == '<') {
2785 EmailEnd = strchr(EmailStart, '>');
2786 if (EmailEnd == NULL)
2787 EmailEnd = strchr(EmailStart, ',');
2791 EmailEnd = strchr(EmailStart, ',');
2793 if (EmailEnd == NULL)
2800 EmailEnd = strchr(UserStart, ',');
2801 if (EmailEnd == NULL) {
2802 EmailEnd = strchr(pch, '>');
2804 if (EmailEnd != NULL) {
2814 while ((EmailEnd > UserStart) && !gt &&
2815 ((*EmailEnd == ',') ||
2816 (*EmailEnd == '>') ||
2817 (isspace(*EmailEnd))))
2819 if (*EmailEnd == '>')
2824 if (EmailEnd == UserStart)
2828 EmailStart = strchr(UserStart, '<');
2829 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2831 UserEnd = EmailStart;
2833 while ((UserEnd > UserStart) &&
2834 isspace (*(UserEnd - 1)))
2837 if (UserStart >= UserEnd)
2838 UserStart = UserEnd = NULL;
2839 At = strchr(EmailStart, '@');
2841 else { /* this is a local recipient... no domain, just a realname */
2842 EmailStart = UserStart;
2843 At = strchr(EmailStart, '@');
2849 EmailStart = UserStart;
2855 if ((UserStart != NULL) && (UserEnd != NULL))
2856 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2857 else if ((UserStart != NULL) && (UserEnd == NULL))
2858 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2860 FlushStrBuf(UserName);
2862 if ((EmailStart != NULL) && (EmailEnd != NULL))
2863 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2864 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2865 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2867 FlushStrBuf(EmailAddress);
2869 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2874 if ((pch != NULL) && (*pch == ','))
2876 if (pch != NULL) while (isspace(*pch))
2885 * @brief replaces all occurances of 'search' by 'replace'
2886 * @param buf Buffer to modify
2887 * @param search character to search
2888 * @param replace character to replace search by
2890 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2895 for (i=0; i<buf->BufUsed; i++)
2896 if (buf->buf[i] == search)
2897 buf->buf[i] = replace;
2903 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2904 * @param buf Buffer to modify
2906 void StrBufToUnixLF(StrBuf *buf)
2908 char *pche, *pchS, *pchT;
2912 pche = buf->buf + buf->BufUsed;
2913 pchS = pchT = buf->buf;
2919 if (*pchS != '\n') {
2928 buf->BufUsed = pchT - buf->buf;
2932 /*******************************************************************************
2933 * Iconv Wrapper; RFC822 de/encoding *
2934 *******************************************************************************/
2937 * @ingroup StrBuf_DeEnCoder
2938 * @brief Wrapper around iconv_open()
2939 * Our version adds aliases for non-standard Microsoft charsets
2940 * such as 'MS950', aliasing them to names like 'CP950'
2942 * @param tocode Target encoding
2943 * @param fromcode Source encoding
2944 * @param pic anonimized pointer to iconv struct
2946 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2949 iconv_t ic = (iconv_t)(-1) ;
2950 ic = iconv_open(tocode, fromcode);
2951 if (ic == (iconv_t)(-1) ) {
2952 char alias_fromcode[64];
2953 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2954 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2955 alias_fromcode[0] = 'C';
2956 alias_fromcode[1] = 'P';
2957 ic = iconv_open(tocode, alias_fromcode);
2960 *(iconv_t *)pic = ic;
2966 * @ingroup StrBuf_DeEnCoder
2967 * @brief find one chunk of a RFC822 encoded string
2968 * @param Buffer where to search
2969 * @param bptr where to start searching
2970 * @returns found position, NULL if none.
2972 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
2975 /* Find the next ?Q? */
2976 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
2979 end = strchr(bptr + 2, '?');
2984 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2985 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
2986 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
2987 (*(end + 2) == '?')) {
2988 /* skip on to the end of the cluster, the next ?= */
2989 end = strstr(end + 3, "?=");
2992 /* sort of half valid encoding, try to find an end. */
2993 end = strstr(bptr, "?=");
3000 * @ingroup StrBuf_DeEnCoder
3001 * @brief convert one buffer according to the preselected iconv pointer PIC
3002 * @param ConvertBuf buffer we need to translate
3003 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3004 * @param pic Pointer to the iconv-session Object
3006 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3012 char *ibuf; /**< Buffer of characters to be converted */
3013 char *obuf; /**< Buffer for converted characters */
3014 size_t ibuflen; /**< Length of input buffer */
3015 size_t obuflen; /**< Length of output buffer */
3018 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3019 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3020 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3022 ic = *(iconv_t*)pic;
3023 ibuf = ConvertBuf->buf;
3024 ibuflen = ConvertBuf->BufUsed;
3026 obuflen = TmpBuf->BufSize;
3028 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3031 if (errno == E2BIG) {
3033 IncreaseBuf(TmpBuf, 0, 0);
3038 else if (errno == EILSEQ){
3039 /* hm, invalid utf8 sequence... what to do now? */
3040 /* An invalid multibyte sequence has been encountered in the input */
3042 else if (errno == EINVAL) {
3043 /* An incomplete multibyte sequence has been encountered in the input. */
3046 FlushStrBuf(TmpBuf);
3049 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3050 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3052 /* little card game: wheres the red lady? */
3053 SwapBuffers(ConvertBuf, TmpBuf);
3054 FlushStrBuf(TmpBuf);
3061 * @ingroup StrBuf_DeEnCoder
3062 * @brief catches one RFC822 encoded segment, and decodes it.
3063 * @param Target buffer to fill with result
3064 * @param DecodeMe buffer with stuff to process
3065 * @param SegmentStart points to our current segment in DecodeMe
3066 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3067 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3068 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3069 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3071 inline static void DecodeSegment(StrBuf *Target,
3072 const StrBuf *DecodeMe,
3073 const char *SegmentStart,
3074 const char *SegmentEnd,
3076 StrBuf *ConvertBuf2,
3077 StrBuf *FoundCharset)
3083 iconv_t ic = (iconv_t)(-1);
3087 /* Now we handle foreign character sets properly encoded
3088 * in RFC2047 format.
3090 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3091 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3092 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3093 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3094 if (FoundCharset != NULL) {
3095 FlushStrBuf(FoundCharset);
3096 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3098 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3099 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3101 *encoding = toupper(*encoding);
3102 if (*encoding == 'B') { /**< base64 */
3103 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3104 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3105 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3107 ConvertBuf->BufUsed);
3109 else if (*encoding == 'Q') { /**< quoted-printable */
3113 while (pos < ConvertBuf->BufUsed)
3115 if (ConvertBuf->buf[pos] == '_')
3116 ConvertBuf->buf[pos] = ' ';
3120 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3121 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3123 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3126 ConvertBuf->BufUsed);
3129 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3132 ctdl_iconv_open("UTF-8", charset, &ic);
3133 if (ic != (iconv_t)(-1) ) {
3135 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3136 StrBufAppendBuf(Target, ConvertBuf2, 0);
3141 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3147 * @ingroup StrBuf_DeEnCoder
3148 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3149 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3150 * @param Target where to put the decoded string to
3151 * @param DecodeMe buffer with encoded string
3152 * @param DefaultCharset if we don't find one, which should we use?
3153 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3154 * put it here for later use where no string might be known.
3156 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3159 StrBuf *ConvertBuf2;
3160 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3161 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3163 StrBuf_RFC822_2_Utf8(Target,
3169 FreeStrBuf(&ConvertBuf);
3170 FreeStrBuf(&ConvertBuf2);
3174 * @ingroup StrBuf_DeEnCoder
3175 * @brief Handle subjects with RFC2047 encoding such as:
3176 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3177 * @param Target where to put the decoded string to
3178 * @param DecodeMe buffer with encoded string
3179 * @param DefaultCharset if we don't find one, which should we use?
3180 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3181 * put it here for later use where no string might be known.
3182 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3183 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3185 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3186 const StrBuf *DecodeMe,
3187 const StrBuf* DefaultCharset,
3188 StrBuf *FoundCharset,
3190 StrBuf *ConvertBuf2)
3192 StrBuf *DecodedInvalidBuf = NULL;
3193 const StrBuf *DecodeMee = DecodeMe;
3194 const char *start, *end, *next, *nextend, *ptr = NULL;
3196 iconv_t ic = (iconv_t)(-1) ;
3201 int illegal_non_rfc2047_encoding = 0;
3203 /* Sometimes, badly formed messages contain strings which were simply
3204 * written out directly in some foreign character set instead of
3205 * using RFC2047 encoding. This is illegal but we will attempt to
3206 * handle it anyway by converting from a user-specified default
3207 * charset to UTF-8 if we see any nonprintable characters.
3210 len = StrLength(DecodeMe);
3211 for (i=0; i<DecodeMe->BufUsed; ++i) {
3212 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3213 illegal_non_rfc2047_encoding = 1;
3218 if ((illegal_non_rfc2047_encoding) &&
3219 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3220 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3223 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3224 if (ic != (iconv_t)(-1) ) {
3225 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3226 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3227 DecodeMee = DecodedInvalidBuf;
3233 /* pre evaluate the first pair */
3234 nextend = end = NULL;
3235 len = StrLength(DecodeMee);
3236 start = strstr(DecodeMee->buf, "=?");
3237 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3239 end = FindNextEnd (DecodeMee, start + 2);
3241 StrBufAppendBuf(Target, DecodeMee, 0);
3242 FreeStrBuf(&DecodedInvalidBuf);
3247 if (start != DecodeMee->buf) {
3250 nFront = start - DecodeMee->buf;
3251 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3255 * Since spammers will go to all sorts of absurd lengths to get their
3256 * messages through, there are LOTS of corrupt headers out there.
3257 * So, prevent a really badly formed RFC2047 header from throwing
3258 * this function into an infinite loop.
3260 while ((start != NULL) &&
3267 DecodeSegment(Target,
3275 next = strstr(end, "=?");
3277 if ((next != NULL) &&
3279 nextend = FindNextEnd(DecodeMee, next);
3280 if (nextend == NULL)
3283 /* did we find two partitions */
3284 if ((next != NULL) &&
3288 while ((ptr < next) &&
3295 * did we find a gab just filled with blanks?
3296 * if not, copy its stuff over.
3300 StrBufAppendBufPlain(Target,
3306 /* our next-pair is our new first pair now. */
3312 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3313 if ((end != NULL) && (end < nextend)) {
3315 while ( (ptr < nextend) &&
3322 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3324 FreeStrBuf(&DecodedInvalidBuf);
3327 /*******************************************************************************
3328 * Manipulating UTF-8 Strings *
3329 *******************************************************************************/
3333 * @brief evaluate the length of an utf8 special character sequence
3334 * @param Char the character to examine
3335 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3337 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3340 unsigned char test = (1<<7);
3342 if ((*CharS & 0xC0) != 0xC0)
3346 ((test & ((unsigned char)*CharS)) != 0))
3351 if ((n > 6) || ((CharE - CharS) < n))
3358 * @brief detect whether this char starts an utf-8 encoded char
3359 * @param Char character to inspect
3360 * @returns yes or no
3362 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3364 /** 11??.???? indicates an UTF8 Sequence. */
3365 return ((Char & 0xC0) == 0xC0);
3370 * @brief measure the number of glyphs in an UTF8 string...
3371 * @param Buf string to measure
3372 * @returns the number of glyphs in Buf
3374 long StrBuf_Utf8StrLen(StrBuf *Buf)
3380 if ((Buf == NULL) || (Buf->BufUsed == 0))
3383 eptr = Buf->buf + Buf->BufUsed;
3384 while ((aptr < eptr) && (*aptr != '\0')) {
3385 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3386 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3387 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3400 * @brief cuts a string after maxlen glyphs
3401 * @param Buf string to cut to maxlen glyphs
3402 * @param maxlen how long may the string become?
3403 * @returns current length of the string
3405 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3411 eptr = Buf->buf + Buf->BufUsed;
3412 while ((aptr < eptr) && (*aptr != '\0')) {
3413 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3414 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3415 while ((*aptr++ != '\0') && (m-- > 0));
3424 Buf->BufUsed = aptr - Buf->buf;
3425 return Buf->BufUsed;
3428 return Buf->BufUsed;
3436 /*******************************************************************************
3438 *******************************************************************************/
3441 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3442 #define OS_CODE 0x03 /*< unix */
3445 * @ingroup StrBuf_DeEnCoder
3446 * @brief uses the same calling syntax as compress2(), but it
3447 * creates a stream compatible with HTTP "Content-encoding: gzip"
3448 * @param dest compressed buffer
3449 * @param destLen length of the compresed data
3450 * @param source source to encode
3451 * @param sourceLen length of source to encode
3452 * @param level compression level
3454 int ZEXPORT compress_gzip(Bytef * dest,
3456 const Bytef * source,
3460 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3462 /* write gzip header */
3463 snprintf((char *) dest, *destLen,
3464 "%c%c%c%c%c%c%c%c%c%c",
3465 gz_magic[0], gz_magic[1], Z_DEFLATED,
3466 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3469 /* normal deflate */
3472 stream.next_in = (Bytef *) source;
3473 stream.avail_in = (uInt) sourceLen;
3474 stream.next_out = dest + 10L; // after header
3475 stream.avail_out = (uInt) * destLen;
3476 if ((uLong) stream.avail_out != *destLen)
3479 stream.zalloc = (alloc_func) 0;
3480 stream.zfree = (free_func) 0;
3481 stream.opaque = (voidpf) 0;
3483 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3484 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3488 err = deflate(&stream, Z_FINISH);
3489 if (err != Z_STREAM_END) {
3490 deflateEnd(&stream);
3491 return err == Z_OK ? Z_BUF_ERROR : err;
3493 *destLen = stream.total_out + 10L;
3495 /* write CRC and Length */
3496 uLong crc = crc32(0L, source, sourceLen);
3498 for (n = 0; n < 4; ++n, ++*destLen) {
3499 dest[*destLen] = (int) (crc & 0xff);
3502 uLong len = stream.total_in;
3503 for (n = 0; n < 4; ++n, ++*destLen) {
3504 dest[*destLen] = (int) (len & 0xff);
3507 err = deflateEnd(&stream);
3514 * @ingroup StrBuf_DeEnCoder
3515 * @brief compress the buffer with gzip
3516 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3517 * @param Buf buffer whose content is to be gzipped
3519 int CompressBuffer(StrBuf *Buf)
3522 char *compressed_data = NULL;
3523 size_t compressed_len, bufsize;
3526 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3527 compressed_data = malloc(compressed_len);
3529 if (compressed_data == NULL)
3531 /* Flush some space after the used payload so valgrind shuts up... */
3532 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3533 Buf->buf[Buf->BufUsed + i++] = '\0';
3534 if (compress_gzip((Bytef *) compressed_data,
3537 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3540 Buf->buf = compressed_data;
3541 Buf->BufUsed = compressed_len;
3542 Buf->BufSize = bufsize;
3543 /* Flush some space after the used payload so valgrind shuts up... */
3545 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3546 Buf->buf[Buf->BufUsed + i++] = '\0';
3549 free(compressed_data);
3551 #endif /* HAVE_ZLIB */
3555 /*******************************************************************************
3556 * File I/O; Callbacks to libevent *
3557 *******************************************************************************/
3559 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3564 if ((FB == NULL) || (FB->Buf == NULL))
3568 * check whether the read pointer is somewhere in a range
3569 * where a cut left is inexpensive
3572 if (FB->ReadWritePointer != NULL)
3576 already_read = FB->ReadWritePointer - FB->Buf->buf;
3577 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3579 if (already_read != 0) {
3582 unread = FB->Buf->BufUsed - already_read;
3584 /* else nothing to compact... */
3586 FB->ReadWritePointer = FB->Buf->buf;
3587 bufremain = FB->Buf->BufSize;
3589 else if ((unread < 64) ||
3590 (bufremain < already_read))
3593 * if its just a tiny bit remaining, or we run out of space...
3596 FB->Buf->BufUsed = unread;
3597 if (unread < already_read)
3598 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3600 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3601 FB->ReadWritePointer = FB->Buf->buf;
3602 bufremain = FB->Buf->BufSize - unread - 1;
3604 else if (bufremain < (FB->Buf->BufSize / 10))
3606 /* get a bigger buffer */
3608 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3610 FB->ReadWritePointer = FB->Buf->buf + unread;
3612 bufremain = FB->Buf->BufSize - unread - 1;
3613 /*TODO: special increase function that won't copy the already read! */
3616 else if (bufremain < 10) {
3617 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3619 FB->ReadWritePointer = FB->Buf->buf;
3621 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3626 FB->ReadWritePointer = FB->Buf->buf;
3627 bufremain = FB->Buf->BufSize - 1;
3630 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3633 FB->Buf->BufUsed += n;
3634 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3639 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3644 if ((FB == NULL) || (FB->Buf == NULL))
3647 if (FB->ReadWritePointer != NULL)
3649 WriteRemain = FB->Buf->BufUsed -
3650 (FB->ReadWritePointer -
3654 FB->ReadWritePointer = FB->Buf->buf;
3655 WriteRemain = FB->Buf->BufUsed;
3658 n = write(fd, FB->ReadWritePointer, WriteRemain);
3660 FB->ReadWritePointer += n;
3662 if (FB->ReadWritePointer ==
3663 FB->Buf->buf + FB->Buf->BufUsed)
3665 FlushStrBuf(FB->Buf);
3666 FB->ReadWritePointer = NULL;
3669 // check whether we've got something to write
3670 // get the maximum chunk plus the pointer we can send
3671 // write whats there
3672 // if not all was sent, remember the send pointer for the next time
3673 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3679 * @ingroup StrBuf_IO
3680 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3681 * @param LineBuf your line will be copied here.
3682 * @param FB BLOB with lines of text...
3683 * @param Ptr moved arround to keep the next-line across several iterations
3684 * has to be &NULL on start; will be &NotNULL on end of buffer
3685 * @returns size of copied buffer
3687 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3689 const char *aptr, *ptr, *eptr;
3692 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3693 FB->ReadWritePointer = StrBufNOTNULL;
3697 FlushStrBuf(LineBuf);
3698 if (FB->ReadWritePointer == NULL)
3699 ptr = aptr = FB->Buf->buf;
3701 ptr = aptr = FB->ReadWritePointer;
3703 optr = LineBuf->buf;
3704 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3705 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3707 while ((ptr <= eptr) &&
3714 LineBuf->BufUsed = optr - LineBuf->buf;
3715 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3716 optr = LineBuf->buf + LineBuf->BufUsed;
3717 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3722 if (optr > LineBuf->buf)
3724 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3725 LineBuf->BufUsed = optr - LineBuf->buf;
3727 if ((FB->ReadWritePointer != NULL) &&
3728 (FB->ReadWritePointer != FB->Buf->buf))
3730 /* Ok, the client application read all the data
3731 it was interested in so far. Since there is more to read,
3732 we now shrink the buffer, and move the rest over.
3734 StrBufCutLeft(FB->Buf,
3735 FB->ReadWritePointer - FB->Buf->buf);
3736 FB->ReadWritePointer = FB->Buf->buf;
3738 return eMustReadMore;
3741 LineBuf->BufUsed = optr - LineBuf->buf;
3743 if ((ptr <= eptr) && (*ptr == '\r'))
3745 if ((ptr <= eptr) && (*ptr == '\n'))
3749 FB->ReadWritePointer = ptr;
3752 FlushStrBuf(FB->Buf);
3753 FB->ReadWritePointer = NULL;
3756 return eReadSuccess;
3760 * @ingroup StrBuf_CHUNKED_IO
3761 * @brief check whether the chunk-buffer has more data waiting or not.
3762 * @param FB Chunk-Buffer to inspect
3764 eReadState StrBufCheckBuffer(IOBuffer *FB)
3768 if (FB->Buf->BufUsed == 0)
3769 return eReadSuccess;
3770 if (FB->ReadWritePointer == NULL)
3771 return eBufferNotEmpty;
3772 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3773 return eBufferNotEmpty;
3774 return eReadSuccess;
3777 /*******************************************************************************
3778 * File I/O; Prefer buffered read since its faster! *
3779 *******************************************************************************/
3782 * @ingroup StrBuf_IO
3783 * @brief Read a line from socket
3784 * flushes and closes the FD on error
3785 * @param buf the buffer to get the input to
3786 * @param fd pointer to the filedescriptor to read
3787 * @param append Append to an existing string or replace?
3788 * @param Error strerror() on error
3789 * @returns numbers of chars read
3791 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
3793 int len, rlen, slen;
3798 slen = len = buf->BufUsed;
3800 rlen = read(*fd, &buf->buf[len], 1);
3802 *Error = strerror(errno);
3809 if (buf->buf[len] == '\n')
3811 if (buf->buf[len] != '\r')
3813 if (len + 2 >= buf->BufSize) {
3815 buf->buf[len+1] = '\0';
3816 IncreaseBuf(buf, 1, -1);
3820 buf->buf[len] = '\0';
3825 * @ingroup StrBuf_BufferedIO
3826 * @brief Read a line from socket
3827 * flushes and closes the FD on error
3828 * @param Line the line to read from the fd / I/O Buffer
3829 * @param buf the buffer to get the input to
3830 * @param fd pointer to the filedescriptor to read
3831 * @param timeout number of successless selects until we bail out
3832 * @param selectresolution how long to wait on each select
3833 * @param Error strerror() on error
3834 * @returns numbers of chars read
3836 int StrBufTCP_read_buffered_line(StrBuf *Line,
3840 int selectresolution,
3844 int nSuccessLess = 0;
3851 if (buf->BufUsed > 0) {
3852 pch = strchr(buf->buf, '\n');
3855 len = pch - buf->buf;
3856 if (len > 0 && (*(pch - 1) == '\r') )
3858 StrBufSub(Line, buf, 0, len - rlen);
3859 StrBufCutLeft(buf, len + 1);
3864 if (buf->BufSize - buf->BufUsed < 10)
3865 IncreaseBuf(buf, 1, -1);
3867 fdflags = fcntl(*fd, F_GETFL);
3868 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3870 while ((nSuccessLess < timeout) && (pch == NULL)) {
3872 tv.tv_sec = selectresolution;
3877 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
3878 *Error = strerror(errno);
3884 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
3889 &buf->buf[buf->BufUsed],
3890 buf->BufSize - buf->BufUsed - 1);
3892 *Error = strerror(errno);
3897 else if (rlen > 0) {
3899 buf->BufUsed += rlen;
3900 buf->buf[buf->BufUsed] = '\0';
3901 if (buf->BufUsed + 10 > buf->BufSize) {
3902 IncreaseBuf(buf, 1, -1);
3904 pch = strchr(buf->buf, '\n');
3911 len = pch - buf->buf;
3912 if (len > 0 && (*(pch - 1) == '\r') )
3914 StrBufSub(Line, buf, 0, len - rlen);
3915 StrBufCutLeft(buf, len + 1);
3922 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
3923 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
3924 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
3926 * @ingroup StrBuf_BufferedIO
3927 * @brief Read a line from socket
3928 * flushes and closes the FD on error
3929 * @param Line where to append our Line read from the fd / I/O Buffer;
3930 * @param IOBuf the buffer to get the input to; lifetime pair to FD
3931 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
3932 * @param fd pointer to the filedescriptor to read
3933 * @param timeout number of successless selects until we bail out
3934 * @param selectresolution how long to wait on each select
3935 * @param Error strerror() on error
3936 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
3938 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
3943 int selectresolution,
3946 const char *pche = NULL;
3947 const char *pos = NULL;
3949 int len, rlen, retlen;
3950 int nSuccessLess = 0;
3952 const char *pch = NULL;
3958 if ((Line == NULL) ||
3965 *Error = ErrRBLF_PreConditionFailed;
3970 if ((IOBuf->BufUsed > 0) &&
3972 (pos < IOBuf->buf + IOBuf->BufUsed))
3976 pche = IOBuf->buf + IOBuf->BufUsed;
3980 while ((pch < pche) && (*pch != '\n'))
3982 if (Line->BufUsed + 10 > Line->BufSize)
3985 apos = pcht - Line->buf;
3987 IncreaseBuf(Line, 1, -1);
3988 pcht = Line->buf + apos;
3996 if (len > 0 && (*(pch - 1) == '\r') )
4005 if ((pch >= pche) || (*pch == '\0'))
4013 if ((pch != NULL) &&
4016 if (pch + 1 >= pche) {
4029 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4031 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4032 IncreaseBuf(IOBuf, 1, -1);
4034 fdflags = fcntl(*fd, F_GETFL);
4035 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4038 while ((nSuccessLess < timeout) &&
4048 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4049 *Error = strerror(errno);
4053 *Error = ErrRBLF_SelectFailed;
4056 if (! FD_ISSET(*fd, &rfds) != 0) {
4062 &IOBuf->buf[IOBuf->BufUsed],
4063 IOBuf->BufSize - IOBuf->BufUsed - 1);
4065 *Error = strerror(errno);
4070 else if (rlen > 0) {
4072 pLF = IOBuf->buf + IOBuf->BufUsed;
4073 IOBuf->BufUsed += rlen;
4074 IOBuf->buf[IOBuf->BufUsed] = '\0';
4076 pche = IOBuf->buf + IOBuf->BufUsed;
4078 while ((pLF < pche) && (*pLF != '\n'))
4080 if ((pLF >= pche) || (*pLF == '\0'))
4083 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4087 if (pLF != NULL) apos = pLF - IOBuf->buf;
4088 IncreaseBuf(IOBuf, 1, -1);
4089 if (pLF != NULL) pLF = IOBuf->buf + apos;
4099 if (len > 0 && (*(pLF - 1) == '\r') )
4101 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4102 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4108 return retlen + len;
4110 *Error = ErrRBLF_NotEnoughSentFromServer;
4115 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4117 * @ingroup StrBuf_IO
4118 * @brief Input binary data from socket
4119 * flushes and closes the FD on error
4120 * @param Buf the buffer to get the input to
4121 * @param fd pointer to the filedescriptor to read
4122 * @param append Append to an existing string or replace?
4123 * @param nBytes the maximal number of bytes to read
4124 * @param Error strerror() on error
4125 * @returns numbers of chars read
4127 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4138 if ((Buf == NULL) || (*fd == -1))
4140 *Error = ErrRBLF_BLOBPreConditionFailed;
4145 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4146 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4148 ptr = Buf->buf + Buf->BufUsed;
4150 fdflags = fcntl(*fd, F_GETFL);
4151 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4153 while ((nRead < nBytes) &&
4163 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4164 *Error = strerror(errno);
4168 *Error = ErrRBLF_SelectFailed;
4171 if (! FD_ISSET(*fd, &rfds) != 0) {
4177 if ((rlen = read(*fd,
4179 nBytes - nRead)) == -1) {
4182 *Error = strerror(errno);
4187 Buf->BufUsed += rlen;
4189 Buf->buf[Buf->BufUsed] = '\0';
4193 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4194 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4196 * @ingroup StrBuf_BufferedIO
4197 * @brief Input binary data from socket
4198 * flushes and closes the FD on error
4199 * @param Blob put binary thing here
4200 * @param IOBuf the buffer to get the input to
4201 * @param Pos offset inside of IOBuf
4202 * @param fd pointer to the filedescriptor to read
4203 * @param append Append to an existing string or replace?
4204 * @param nBytes the maximal number of bytes to read
4205 * @param check whether we should search for '000\n' terminators in case of timeouts
4206 * @param Error strerror() on error
4207 * @returns numbers of chars read
4209 int StrBufReadBLOBBuffered(StrBuf *Blob,
4223 int nAlreadyRead = 0;
4228 int nSuccessLess = 0;
4231 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4235 *Error = ErrRBB_BLOBFPreConditionFailed;
4241 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4242 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4247 len = pos - IOBuf->buf;
4248 rlen = IOBuf->BufUsed - len;
4251 if ((IOBuf->BufUsed > 0) &&
4253 (pos < IOBuf->buf + IOBuf->BufUsed))
4255 if (rlen < nBytes) {
4256 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4257 Blob->BufUsed += rlen;
4258 Blob->buf[Blob->BufUsed] = '\0';
4259 nAlreadyRead = nRead = rlen;
4262 if (rlen >= nBytes) {
4263 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4264 Blob->BufUsed += nBytes;
4265 Blob->buf[Blob->BufUsed] = '\0';
4266 if (rlen == nBytes) {
4278 if (IOBuf->BufSize < nBytes - nRead)
4279 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4282 len = Blob->BufUsed;
4284 fdflags = fcntl(*fd, F_GETFL);
4285 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4293 while ((nSuccessLess < MaxTries) &&
4303 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4304 *Error = strerror(errno);
4308 *Error = ErrRBLF_SelectFailed;
4311 if (! FD_ISSET(*fd, &rfds) != 0) {
4318 IOBuf->BufSize - (ptr - IOBuf->buf));
4322 *Error = strerror(errno);
4325 else if (rlen == 0){
4326 if ((check == NNN_TERM) &&
4328 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4330 StrBufPlain(Blob, HKEY("\n000\n"));
4331 StrBufCutRight(Blob, 5);
4332 return Blob->BufUsed;
4334 else if (!IsNonBlock)
4336 else if (nSuccessLess > MaxTries) {
4338 *Error = ErrRBB_too_many_selects;
4342 else if (rlen > 0) {
4346 IOBuf->BufUsed += rlen;
4349 if (nSuccessLess >= MaxTries) {
4351 *Error = ErrRBB_too_many_selects;
4355 if (nRead > nBytes) {
4356 *Pos = IOBuf->buf + nBytes;
4358 Blob->buf[Blob->BufUsed] = '\0';
4359 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4363 return nRead + nAlreadyRead;
4367 * @ingroup StrBuf_IO
4368 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4369 * @param LineBuf your line will be copied here.
4370 * @param Buf BLOB with lines of text...
4371 * @param Ptr moved arround to keep the next-line across several iterations
4372 * has to be &NULL on start; will be &NotNULL on end of buffer
4373 * @returns size of remaining buffer
4375 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4377 const char *aptr, *ptr, *eptr;
4380 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4381 *Ptr = StrBufNOTNULL;
4385 FlushStrBuf(LineBuf);
4387 ptr = aptr = Buf->buf;
4391 optr = LineBuf->buf;
4392 eptr = Buf->buf + Buf->BufUsed;
4393 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4395 while ((ptr <= eptr) &&
4402 LineBuf->BufUsed = optr - LineBuf->buf;
4403 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4404 optr = LineBuf->buf + LineBuf->BufUsed;
4405 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4409 if ((ptr >= eptr) && (optr > LineBuf->buf))
4411 LineBuf->BufUsed = optr - LineBuf->buf;
4413 if ((ptr <= eptr) && (*ptr == '\r'))
4415 if ((ptr <= eptr) && (*ptr == '\n'))
4422 *Ptr = StrBufNOTNULL;
4425 return Buf->BufUsed - (ptr - Buf->buf);
4430 * @ingroup StrBuf_IO
4431 * @brief removes double slashes from pathnames
4432 * @param Dir directory string to filter
4433 * @param RemoveTrailingSlash allows / disallows trailing slashes
4435 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4441 while (!IsEmptyStr(a)) {
4453 if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
4458 Dir->BufUsed = b - Dir->buf;