1 // Copyright (c) 1987-2022 by the citadel.org team
3 // This program is open source software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 3 of the License, or
6 // (at your option) any later version.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <sys/select.h>
27 #include <sys/types.h>
28 #define SHOW_ME_VAPPEND_PRINTF
31 #include "libcitadel.h"
33 #include "b64/cencode.h"
34 #include "b64/cdecode.h"
50 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
51 const Bytef * source, uLong sourceLen, int level);
53 int BaseStrBufSize = 64;
55 int ZLibCompressionRatio = -1; /* defaults to 6 */
57 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
58 #define OS_CODE 0x03 /*< unix */
59 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
62 const char *StrBufNOTNULL = ((char*) NULL) - 1;
64 const char HexList[256][3] = {
65 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
66 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
67 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
68 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
69 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
70 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
71 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
72 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
73 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
74 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
75 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
76 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
77 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
78 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
79 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
80 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
83 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
84 * StrBuf is a versatile class, aiding the handling of dynamic strings
85 * * reduce de/reallocations
86 * * reduce the need to remeasure it
87 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
88 * * allow asyncroneous IO for line and Blob based operations
89 * * reduce the use of memove in those
90 * * Quick filling in several operations with append functions
94 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
99 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
101 * use these operators to interfere with code demanding char*;
102 * if you need to own the content, smash me. Avoid, since we loose the length information.
106 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
108 * operations to get your Strings into a StrBuf, manipulating them, or appending
111 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
113 * Quick tokenizer; demands of the user to pull its tokens in sequence
117 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
119 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
123 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
125 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
126 * External Cursor to maintain the current read position inside of the buffer
127 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
131 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
137 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
139 * these functions translate the content of a buffer into another representation;
140 * some are combined Fillers and encoders
144 * Private Structure for the Stringbuffer
147 char *buf; /**< the pointer to the dynamic buffer */
148 long BufSize; /**< how many spcae do we optain */
149 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
150 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
152 long nIncreases; /**< for profiling; cound how many times we needed more */
153 char bt [SIZ]; /**< Stacktrace of last increase */
154 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
159 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
160 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
163 #ifdef HAVE_BACKTRACE
164 static void StrBufBacktrace(StrBuf *Buf, int which)
168 void *stack_frames[50];
173 pstart = pch = Buf->bt;
175 pstart = pch = Buf->bt_lastinc;
176 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
177 strings = backtrace_symbols(stack_frames, size);
178 for (i = 0; i < size; i++) {
180 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
182 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
191 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
193 if (hFreeDbglog == -1){
194 pid_t pid = getpid();
196 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
197 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
199 if ((*FreeMe)->nIncreases > 0)
203 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
205 (*FreeMe)->nIncreases,
209 (*FreeMe)->bt_lastinc);
210 n = write(hFreeDbglog, buf, n);
216 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
220 n = write(hFreeDbglog, buf, n);
224 void dbg_IncreaseBuf(StrBuf *IncMe)
227 #ifdef HAVE_BACKTRACE
228 StrBufBacktrace(Buf, 1);
232 void dbg_Init(StrBuf *Buf)
236 Buf->bt_lastinc[0] = '\0';
237 #ifdef HAVE_BACKTRACE
238 StrBufBacktrace(Buf, 0);
244 #define dbg_FreeStrBuf(a, b)
245 #define dbg_IncreaseBuf(a)
252 * @brief swaps the contents of two StrBufs
253 * this is to be used to have cheap switched between a work-buffer and a target buffer
255 * @param B second one
257 static inline void iSwapBuffers(StrBuf *A, StrBuf *B)
261 memcpy(&C, A, sizeof(*A));
262 memcpy(A, B, sizeof(*B));
263 memcpy(B, &C, sizeof(C));
267 void SwapBuffers(StrBuf *A, StrBuf *B)
274 * @ingroup StrBuf_Cast
275 * @brief Cast operator to Plain String
276 * @note if the buffer is altered by StrBuf operations, this pointer may become
277 * invalid. So don't lean on it after altering the buffer!
278 * Since this operation is considered cheap, rather call it often than risking
279 * your pointer to become invalid!
280 * @param Str the string we want to get the c-string representation for
281 * @returns the Pointer to the Content. Don't mess with it!
283 inline const char *ChrPtr(const StrBuf *Str)
291 * @ingroup StrBuf_Cast
292 * @brief since we know strlen()'s result, provide it here.
293 * @param Str the string to return the length to
294 * @returns contentlength of the buffer
296 inline int StrLength(const StrBuf *Str)
298 return (Str != NULL) ? Str->BufUsed : 0;
301 // local utility function to resize the buffer
302 // Buf the buffer whichs storage we should increase
303 // KeepOriginal should we copy the original buffer or just start over with a new one
304 // DestSize what should fit in after?
305 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
307 size_t NewSize = Buf->BufSize * 2;
314 while ((NewSize <= DestSize) && (NewSize != 0)) {
323 NewBuf = (char*) malloc(NewSize);
324 if (NewBuf == NULL) {
328 if (KeepOriginal && (Buf->BufUsed > 0)) {
329 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
337 Buf->BufSize = NewSize;
339 dbg_IncreaseBuf(Buf);
345 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
346 // Buf Buffer to shrink (has to be empty)
347 // ThreshHold if the buffer is bigger then this, its readjusted
348 // NewSize if we Shrink it, how big are we going to be afterwards?
349 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
350 if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
352 Buf->buf = (char*) malloc(NewSize);
354 Buf->BufSize = NewSize;
360 * @ingroup StrBuf_DeConstructors
361 * @brief shrink long term buffers to their real size so they don't waste memory
362 * @param Buf buffer to shrink
363 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
364 * @returns physical size of the buffer
366 long StrBufShrinkToFit(StrBuf *Buf, int Force)
371 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
375 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
379 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
380 Buf->BufSize = Buf->BufUsed + 1;
388 * @ingroup StrBuf_DeConstructors
389 * @brief Allocate a new buffer with default buffer size
390 * @returns the new stringbuffer
392 StrBuf* NewStrBuf(void)
396 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
400 NewBuf->buf = (char*) malloc(BaseStrBufSize);
401 if (NewBuf->buf == NULL)
406 NewBuf->buf[0] = '\0';
407 NewBuf->BufSize = BaseStrBufSize;
409 NewBuf->ConstBuf = 0;
417 * @ingroup StrBuf_DeConstructors
418 * @brief Copy Constructor; returns a duplicate of CopyMe
419 * @param CopyMe Buffer to faxmilate
420 * @returns the new stringbuffer
422 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
429 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
433 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
434 if (NewBuf->buf == NULL)
440 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
441 NewBuf->BufUsed = CopyMe->BufUsed;
442 NewBuf->BufSize = CopyMe->BufSize;
443 NewBuf->ConstBuf = 0;
451 * @ingroup StrBuf_DeConstructors
452 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
453 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
454 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
455 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
456 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
457 * @returns the new stringbuffer
459 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
463 if (CreateRelpaceMe == NULL)
468 if (*CreateRelpaceMe != NULL)
469 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
471 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
475 if (CopyFlushMe == NULL)
477 if (*CreateRelpaceMe != NULL)
478 FlushStrBuf(*CreateRelpaceMe);
480 *CreateRelpaceMe = NewStrBuf();
485 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
486 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
487 * be a big IO-Buffer...
489 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
491 if (*CreateRelpaceMe == NULL)
493 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
498 NewBuf = *CreateRelpaceMe;
501 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
505 if (*CreateRelpaceMe == NULL)
507 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
511 NewBuf = *CreateRelpaceMe;
512 iSwapBuffers (NewBuf, CopyFlushMe);
515 FlushStrBuf(CopyFlushMe);
520 * @ingroup StrBuf_DeConstructors
521 * @brief create a new Buffer using an existing c-string
522 * this function should also be used if you want to pre-suggest
523 * the buffer size to allocate in conjunction with ptr == NULL
524 * @param ptr the c-string to copy; may be NULL to create a blank instance
525 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
526 * @returns the new stringbuffer
528 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
531 size_t Siz = BaseStrBufSize;
534 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
539 CopySize = strlen((ptr != NULL)?ptr:"");
543 while ((Siz <= CopySize) && (Siz != 0))
552 NewBuf->buf = (char*) malloc(Siz);
553 if (NewBuf->buf == NULL)
558 NewBuf->BufSize = Siz;
560 memcpy(NewBuf->buf, ptr, CopySize);
561 NewBuf->buf[CopySize] = '\0';
562 NewBuf->BufUsed = CopySize;
565 NewBuf->buf[0] = '\0';
568 NewBuf->ConstBuf = 0;
576 * @ingroup StrBuf_DeConstructors
577 * @brief Set an existing buffer from a c-string
578 * @param Buf buffer to load
579 * @param ptr c-string to put into
580 * @param nChars set to -1 if we should work 0-terminated
581 * @returns the new length of the string
583 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
598 CopySize = strlen(ptr);
602 while ((Siz <= CopySize) && (Siz != 0))
610 if (Siz != Buf->BufSize)
611 IncreaseBuf(Buf, 0, Siz);
612 memcpy(Buf->buf, ptr, CopySize);
613 Buf->buf[CopySize] = '\0';
614 Buf->BufUsed = CopySize;
621 * @ingroup StrBuf_DeConstructors
622 * @brief use strbuf as wrapper for a string constant for easy handling
623 * @param StringConstant a string to wrap
624 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
626 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
630 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
633 NewBuf->buf = (char*) StringConstant;
634 NewBuf->BufSize = SizeOfStrConstant;
635 NewBuf->BufUsed = SizeOfStrConstant;
636 NewBuf->ConstBuf = 1;
645 * @ingroup StrBuf_DeConstructors
646 * @brief flush the content of a Buf; keep its struct
647 * @param buf Buffer to flush
649 int FlushStrBuf(StrBuf *buf)
651 if ((buf == NULL) || (buf->buf == NULL))
661 * @ingroup StrBuf_DeConstructors
662 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
663 * @param buf Buffer to wipe
665 int FLUSHStrBuf(StrBuf *buf)
671 if (buf->BufUsed > 0) {
672 memset(buf->buf, 0, buf->BufUsed);
679 int hFreeDbglog = -1;
682 * @ingroup StrBuf_DeConstructors
683 * @brief Release a Buffer
684 * Its a double pointer, so it can NULL your pointer
685 * so fancy SIG11 appear instead of random results
686 * @param FreeMe Pointer Pointer to the buffer to free
688 void FreeStrBuf (StrBuf **FreeMe)
693 dbg_FreeStrBuf(FreeMe, 'F');
695 if (!(*FreeMe)->ConstBuf)
696 free((*FreeMe)->buf);
702 * @ingroup StrBuf_DeConstructors
703 * @brief flatten a Buffer to the Char * we return
704 * Its a double pointer, so it can NULL your pointer
705 * so fancy SIG11 appear instead of random results
706 * The Callee then owns the buffer and is responsible for freeing it.
707 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
708 * @returns the pointer of the buffer; Callee owns the memory thereafter.
710 char *SmashStrBuf (StrBuf **SmashMe)
714 if ((SmashMe == NULL) || (*SmashMe == NULL))
717 dbg_FreeStrBuf(SmashMe, 'S');
719 Ret = (*SmashMe)->buf;
726 * @ingroup StrBuf_DeConstructors
727 * @brief Release the buffer
728 * If you want put your StrBuf into a Hash, use this as Destructor.
729 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
731 void HFreeStrBuf (void *VFreeMe)
733 StrBuf *FreeMe = (StrBuf*)VFreeMe;
737 dbg_FreeStrBuf(SmashMe, 'H');
739 if (!FreeMe->ConstBuf)
745 /*******************************************************************************
746 * Simple string transformations *
747 *******************************************************************************/
751 * @brief Wrapper around atol
753 long StrTol(const StrBuf *Buf)
758 return atol(Buf->buf);
765 * @brief Wrapper around atoi
767 int StrToi(const StrBuf *Buf)
771 if (Buf->BufUsed > 0)
772 return atoi(Buf->buf);
779 * @brief Checks to see if the string is a pure number
780 * @param Buf The buffer to inspect
781 * @returns 1 if its a pure number, 0, if not.
783 int StrBufIsNumber(const StrBuf *Buf) {
785 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
788 strtoll(Buf->buf, &pEnd, 10);
789 if (pEnd == Buf->buf)
791 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
793 if (Buf->buf == pEnd)
799 * @ingroup StrBuf_Filler
800 * @brief modifies a Single char of the Buf
801 * You can point to it via char* or a zero-based integer
802 * @param Buf The buffer to manipulate
803 * @param ptr char* to zero; use NULL if unused
804 * @param nThChar zero based pointer into the string; use -1 if unused
805 * @param PeekValue The Character to place into the position
807 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
812 nThChar = ptr - Buf->buf;
813 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
815 Buf->buf[nThChar] = PeekValue;
820 * @ingroup StrBuf_Filler
821 * @brief modifies a range of chars of the Buf
822 * You can point to it via char* or a zero-based integer
823 * @param Buf The buffer to manipulate
824 * @param ptr char* to zero; use NULL if unused
825 * @param nThChar zero based pointer into the string; use -1 if unused
826 * @param nChars how many chars are to be flushed?
827 * @param PookValue The Character to place into that area
829 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
834 nThChar = ptr - Buf->buf;
835 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
837 if (nThChar + nChars > Buf->BufUsed)
838 nChars = Buf->BufUsed - nThChar;
840 memset(Buf->buf + nThChar, PookValue, nChars);
841 /* just to be shure... */
842 Buf->buf[Buf->BufUsed] = 0;
847 * @ingroup StrBuf_Filler
848 * @brief Append a StringBuffer to the buffer
849 * @param Buf Buffer to modify
850 * @param AppendBuf Buffer to copy at the end of our buffer
851 * @param Offset Should we start copying from an offset?
853 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
855 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
856 (Buf == NULL) || (Buf->buf == NULL))
859 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
860 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
862 memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
863 Buf->BufUsed += AppendBuf->BufUsed - Offset;
864 Buf->buf[Buf->BufUsed] = '\0';
868 // Append a C-String to the buffer
869 // Buf Buffer to modify
870 // AppendBuf Buffer to copy at the end of our buffer
871 // AppendSize number of bytes to copy; set to -1 if we should count it in advance
872 // Offset Should we start copying from an offset?
873 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
875 long BufSizeRequired;
877 if ((AppendBuf == NULL) || (Buf == NULL))
880 if (AppendSize < 0) {
881 aps = strlen(AppendBuf + Offset);
884 aps = AppendSize - Offset;
887 BufSizeRequired = Buf->BufUsed + aps + 1;
888 if (Buf->BufSize <= BufSizeRequired) {
889 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
892 memcpy(Buf->buf + Buf->BufUsed,
896 Buf->buf[Buf->BufUsed] = '\0';
900 * @ingroup StrBuf_Filler
901 * @brief sprintf like function appending the formated string to the buffer
902 * vsnprintf version to wrap into own calls
903 * @param Buf Buffer to extend by format and Params
904 * @param format printf alike format to add
905 * @param ap va_list containing the items for format
907 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
915 if ((Buf == NULL) || (format == NULL))
918 BufSize = Buf->BufSize;
919 nWritten = Buf->BufSize + 1;
920 Offset = Buf->BufUsed;
921 newused = Offset + nWritten;
923 while (newused >= BufSize) {
925 nWritten = vsnprintf(Buf->buf + Offset,
926 Buf->BufSize - Offset,
929 newused = Offset + nWritten;
930 if (newused >= Buf->BufSize) {
931 if (IncreaseBuf(Buf, 1, newused) == -1)
932 return; /* TODO: error handling? */
933 newused = Buf->BufSize + 1;
936 Buf->BufUsed = Offset + nWritten;
937 BufSize = Buf->BufSize;
944 * @ingroup StrBuf_Filler
945 * @brief sprintf like function appending the formated string to the buffer
946 * @param Buf Buffer to extend by format and Params
947 * @param format printf alike format to add
949 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
957 if ((Buf == NULL) || (format == NULL))
960 BufSize = Buf->BufSize;
961 nWritten = Buf->BufSize + 1;
962 Offset = Buf->BufUsed;
963 newused = Offset + nWritten;
965 while (newused >= BufSize) {
966 va_start(arg_ptr, format);
967 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
968 Buf->BufSize - Buf->BufUsed,
971 newused = Buf->BufUsed + nWritten;
972 if (newused >= Buf->BufSize) {
973 if (IncreaseBuf(Buf, 1, newused) == -1)
974 return; /* TODO: error handling? */
975 newused = Buf->BufSize + 1;
978 Buf->BufUsed += nWritten;
979 BufSize = Buf->BufSize;
986 * @ingroup StrBuf_Filler
987 * @brief sprintf like function putting the formated string into the buffer
988 * @param Buf Buffer to extend by format and Parameters
989 * @param format printf alike format to add
991 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
996 if ((Buf == NULL) || (format == NULL))
999 nWritten = Buf->BufSize + 1;
1000 while (nWritten >= Buf->BufSize) {
1001 va_start(arg_ptr, format);
1002 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1004 if (nWritten >= Buf->BufSize) {
1005 if (IncreaseBuf(Buf, 0, 0) == -1)
1006 return; /* TODO: error handling? */
1007 nWritten = Buf->BufSize + 1;
1010 Buf->BufUsed = nWritten ;
1015 * @ingroup StrBuf_Filler
1016 * @brief Callback for cURL to append the webserver reply to a buffer
1017 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1018 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1019 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1020 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1022 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1031 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1032 return size * nmemb;
1038 * @brief extracts a substring from Source into dest
1039 * @param dest buffer to place substring into
1040 * @param Source string to copy substring from
1041 * @param Offset chars to skip from start
1042 * @param nChars number of chars to copy
1043 * @returns the number of chars copied; may be different from nChars due to the size of Source
1045 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1047 size_t NCharsRemain;
1048 if (Offset > Source->BufUsed)
1054 if (Offset + nChars < Source->BufUsed)
1056 if ((nChars >= dest->BufSize) &&
1057 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1059 memcpy(dest->buf, Source->buf + Offset, nChars);
1060 dest->BufUsed = nChars;
1061 dest->buf[dest->BufUsed] = '\0';
1064 NCharsRemain = Source->BufUsed - Offset;
1065 if ((NCharsRemain >= dest->BufSize) &&
1066 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1068 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1069 dest->BufUsed = NCharsRemain;
1070 dest->buf[dest->BufUsed] = '\0';
1071 return NCharsRemain;
1076 * @brief Cut nChars from the start of the string
1077 * @param Buf Buffer to modify
1078 * @param nChars how many chars should be skipped?
1080 void StrBufCutLeft(StrBuf *Buf, int nChars)
1082 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1083 if (nChars >= Buf->BufUsed) {
1087 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1088 Buf->BufUsed -= nChars;
1089 Buf->buf[Buf->BufUsed] = '\0';
1094 * @brief Cut the trailing n Chars from the string
1095 * @param Buf Buffer to modify
1096 * @param nChars how many chars should be trunkated?
1098 void StrBufCutRight(StrBuf *Buf, int nChars)
1100 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1103 if (nChars >= Buf->BufUsed) {
1107 Buf->BufUsed -= nChars;
1108 Buf->buf[Buf->BufUsed] = '\0';
1113 * @brief Cut the string after n Chars
1114 * @param Buf Buffer to modify
1115 * @param AfternChars after how many chars should we trunkate the string?
1116 * @param At if non-null and points inside of our string, cut it there.
1118 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1120 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1122 AfternChars = At - Buf->buf;
1125 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1127 Buf->BufUsed = AfternChars;
1128 Buf->buf[Buf->BufUsed] = '\0';
1134 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1135 * @param Buf the string to modify
1137 void StrBufTrim(StrBuf *Buf)
1140 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1142 while ((Buf->BufUsed > 0) &&
1143 isspace(Buf->buf[Buf->BufUsed - 1]))
1147 Buf->buf[Buf->BufUsed] = '\0';
1149 if (Buf->BufUsed == 0) return;
1151 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1154 if (delta > 0) StrBufCutLeft(Buf, delta);
1158 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1159 * @param Buf the string to modify
1161 void StrBufSpaceToBlank(StrBuf *Buf)
1165 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1168 pche = pch + Buf->BufUsed;
1177 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1182 if ((Buf == NULL) || (Buf->buf == NULL)) {
1186 pRight = strchr(Buf->buf, rightboundary);
1187 if (pRight != NULL) {
1188 StrBufCutAt(Buf, 0, pRight);
1191 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1192 if (pLeft != NULL) {
1193 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1199 * @ingroup StrBuf_Filler
1200 * @brief uppercase the contents of a buffer
1201 * @param Buf the buffer to translate
1203 void StrBufUpCase(StrBuf *Buf)
1207 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1210 pche = pch + Buf->BufUsed;
1211 while (pch < pche) {
1212 *pch = toupper(*pch);
1219 * @ingroup StrBuf_Filler
1220 * @brief lowercase the contents of a buffer
1221 * @param Buf the buffer to translate
1223 void StrBufLowerCase(StrBuf *Buf)
1227 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1230 pche = pch + Buf->BufUsed;
1231 while (pch < pche) {
1232 *pch = tolower(*pch);
1238 /*******************************************************************************
1239 * a tokenizer that kills, maims, and destroys *
1240 *******************************************************************************/
1243 * @ingroup StrBuf_Tokenizer
1244 * @brief Replace a token at a given place with a given length by another token with given length
1245 * @param Buf String where to work on
1246 * @param where where inside of the Buf is the search-token
1247 * @param HowLong How long is the token to be replaced
1248 * @param Repl Token to insert at 'where'
1249 * @param ReplLen Length of repl
1250 * @returns -1 if fail else length of resulting Buf
1252 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1253 const char *Repl, long ReplLen)
1256 if ((Buf == NULL) ||
1257 (where > Buf->BufUsed) ||
1258 (where + HowLong > Buf->BufUsed))
1261 if (where + ReplLen - HowLong > Buf->BufSize)
1262 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1265 memmove(Buf->buf + where + ReplLen,
1266 Buf->buf + where + HowLong,
1267 Buf->BufUsed - where - HowLong);
1269 memcpy(Buf->buf + where,
1272 Buf->BufUsed += ReplLen - HowLong;
1274 return Buf->BufUsed;
1278 * @ingroup StrBuf_Tokenizer
1279 * @brief Counts the numbmer of tokens in a buffer
1280 * @param source String to count tokens in
1281 * @param tok Tokenizer char to count
1282 * @returns numbers of tokenizer chars found
1284 int StrBufNum_tokens(const StrBuf *source, char tok)
1288 if ((source == NULL) || (source->BufUsed == 0))
1290 if ((source->BufUsed == 1) && (*source->buf == tok))
1294 pche = pch + source->BufUsed;
1305 * @ingroup StrBuf_Tokenizer
1306 * @brief a string tokenizer
1307 * @param Source StringBuffer to read into
1308 * @param parmnum n'th Parameter to remove
1309 * @param separator tokenizer character
1310 * @returns -1 if not found, else length of token.
1312 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1315 char *d, *s, *end; /* dest, source */
1318 /* Find desired @parameter */
1319 end = Source->buf + Source->BufUsed;
1321 while ((d <= end) &&
1324 /* End of string, bail! */
1329 if (*d == separator) {
1334 if ((d == NULL) || (d >= end))
1335 return 0; /* @Parameter not found */
1337 /* Find next @parameter */
1339 while ((s <= end) &&
1340 (*s && *s != separator))
1344 if (*s == separator)
1348 /* Hack and slash */
1353 memmove(d, s, Source->BufUsed - (s - Source->buf));
1354 Source->BufUsed += ReducedBy;
1355 Source->buf[Source->BufUsed] = '\0';
1357 else if (d == Source->buf) {
1359 Source->BufUsed = 0;
1363 Source->BufUsed += ReducedBy;
1374 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1376 const StrBuf Temp = {
1389 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1393 * @ingroup StrBuf_Tokenizer
1394 * @brief a string tokenizer
1395 * @param dest Destination StringBuffer
1396 * @param Source StringBuffer to read into
1397 * @param parmnum n'th Parameter to extract
1398 * @param separator tokenizer character
1399 * @returns -1 if not found, else length of token.
1401 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1403 const char *s, *e; //* source * /
1404 int len = 0; //* running total length of extracted string * /
1405 int current_token = 0; //* token currently being processed * /
1408 dest->buf[0] = '\0';
1414 if ((Source == NULL) || (Source->BufUsed ==0)) {
1418 e = s + Source->BufUsed;
1421 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1423 while ((s < e) && !IsEmptyStr(s)) {
1424 if (*s == separator) {
1427 if (len >= dest->BufSize) {
1428 dest->BufUsed = len;
1429 if (IncreaseBuf(dest, 1, -1) < 0) {
1434 if ( (current_token == parmnum) &&
1435 (*s != separator)) {
1436 dest->buf[len] = *s;
1439 else if (current_token > parmnum) {
1445 dest->buf[len] = '\0';
1446 dest->BufUsed = len;
1448 if (current_token < parmnum) {
1449 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1452 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1461 * @ingroup StrBuf_Tokenizer
1462 * @brief a string tokenizer to fetch an integer
1463 * @param Source String containing tokens
1464 * @param parmnum n'th Parameter to extract
1465 * @param separator tokenizer character
1466 * @returns 0 if not found, else integer representation of the token
1468 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1478 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1485 * @ingroup StrBuf_Tokenizer
1486 * @brief a string tokenizer to fetch a long integer
1487 * @param Source String containing tokens
1488 * @param parmnum n'th Parameter to extract
1489 * @param separator tokenizer character
1490 * @returns 0 if not found, else long integer representation of the token
1492 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1502 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1510 * @ingroup StrBuf_Tokenizer
1511 * @brief a string tokenizer to fetch an unsigned long
1512 * @param Source String containing tokens
1513 * @param parmnum n'th Parameter to extract
1514 * @param separator tokenizer character
1515 * @returns 0 if not found, else unsigned long representation of the token
1517 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1528 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1532 return (unsigned long) atol(pnum);
1541 * @ingroup StrBuf_NextTokenizer
1542 * @brief a string tokenizer; Bounds checker
1543 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1544 * @param Source our tokenbuffer
1545 * @param pStart the token iterator pointer to inspect
1546 * @returns whether the revolving pointer is inside of the search range
1548 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1550 if ((Source == NULL) ||
1551 (*pStart == StrBufNOTNULL) ||
1552 (Source->BufUsed == 0))
1556 if (*pStart == NULL)
1560 else if (*pStart > Source->buf + Source->BufUsed)
1564 else if (*pStart <= Source->buf)
1573 * @ingroup StrBuf_NextTokenizer
1574 * @brief a string tokenizer
1575 * @param dest Destination StringBuffer
1576 * @param Source StringBuffer to read into
1577 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1578 * @param separator tokenizer
1579 * @returns -1 if not found, else length of token.
1581 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1583 const char *s; /* source */
1584 const char *EndBuffer; /* end stop of source buffer */
1585 int current_token = 0; /* token currently being processed */
1586 int len = 0; /* running total length of extracted string */
1588 if ((Source == NULL) ||
1589 (Source->BufUsed == 0) )
1591 *pStart = StrBufNOTNULL;
1597 EndBuffer = Source->buf + Source->BufUsed;
1601 dest->buf[0] = '\0';
1606 *pStart = EndBuffer + 1;
1610 if (*pStart == NULL)
1612 *pStart = Source->buf; /* we're starting to examine this buffer. */
1614 else if ((*pStart < Source->buf) ||
1615 (*pStart > EndBuffer ) )
1617 return -1; /* no more tokens to find. */
1621 /* start to find the next token */
1622 while ((s <= EndBuffer) &&
1623 (current_token == 0) )
1625 if (*s == separator)
1627 /* we found the next token */
1631 if (len >= dest->BufSize)
1633 /* our Dest-buffer isn't big enough, increase it. */
1634 dest->BufUsed = len;
1636 if (IncreaseBuf(dest, 1, -1) < 0) {
1637 /* WHUT? no more mem? bail out. */
1644 if ( (current_token == 0 ) && /* are we in our target token? */
1645 (!IsEmptyStr(s) ) &&
1646 (separator != *s) ) /* don't copy the token itself */
1648 dest->buf[len] = *s; /* Copy the payload */
1649 ++len; /* remember the bigger size. */
1655 /* did we reach the end? */
1656 if ((s > EndBuffer)) {
1657 EndBuffer = StrBufNOTNULL;
1658 *pStart = EndBuffer;
1661 *pStart = s; /* remember the position for the next run */
1664 /* sanitize our extracted token */
1665 dest->buf[len] = '\0';
1666 dest->BufUsed = len;
1673 * @ingroup StrBuf_NextTokenizer
1674 * @brief a string tokenizer
1675 * @param Source StringBuffer to read from
1676 * @param pStart pointer to the end of the last token. Feed with NULL.
1677 * @param separator tokenizer character
1678 * @param nTokens number of tokens to fastforward over
1679 * @returns -1 if not found, else length of token.
1681 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1683 const char *s, *EndBuffer; //* source * /
1684 int len = 0; //* running total length of extracted string * /
1685 int current_token = 0; //* token currently being processed * /
1687 if ((Source == NULL) ||
1688 (Source->BufUsed ==0)) {
1692 return Source->BufUsed;
1694 if (*pStart == NULL)
1695 *pStart = Source->buf;
1697 EndBuffer = Source->buf + Source->BufUsed;
1699 if ((*pStart < Source->buf) ||
1700 (*pStart > EndBuffer)) {
1708 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1710 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1711 if (*s == separator) {
1714 if (current_token >= nTokens) {
1726 * @ingroup StrBuf_NextTokenizer
1727 * @brief a string tokenizer to fetch an integer
1728 * @param Source StringBuffer to read from
1729 * @param pStart Cursor on the tokenstring
1730 * @param separator tokenizer character
1731 * @returns 0 if not found, else integer representation of the token
1733 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1743 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1750 * @ingroup StrBuf_NextTokenizer
1751 * @brief a string tokenizer to fetch a long integer
1752 * @param Source StringBuffer to read from
1753 * @param pStart Cursor on the tokenstring
1754 * @param separator tokenizer character
1755 * @returns 0 if not found, else long integer representation of the token
1757 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1767 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1775 * @ingroup StrBuf_NextTokenizer
1776 * @brief a string tokenizer to fetch an unsigned long
1777 * @param Source StringBuffer to read from
1778 * @param pStart Cursor on the tokenstring
1779 * @param separator tokenizer character
1780 * @returns 0 if not found, else unsigned long representation of the token
1782 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1793 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1797 return (unsigned long) atol(pnum);
1807 /*******************************************************************************
1808 * Escape Appending *
1809 *******************************************************************************/
1812 * @ingroup StrBuf_DeEnCoder
1813 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1814 * @param OutBuf the output buffer
1815 * @param In Buffer to encode
1816 * @param PlainIn way in from plain old c strings
1818 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1820 const char *pch, *pche;
1824 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1826 if (PlainIn != NULL) {
1827 len = strlen(PlainIn);
1833 pche = pch + In->BufUsed;
1840 pt = OutBuf->buf + OutBuf->BufUsed;
1841 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1843 while (pch < pche) {
1845 IncreaseBuf(OutBuf, 1, -1);
1846 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1847 pt = OutBuf->buf + OutBuf->BufUsed;
1850 if((*pch >= 'a' && *pch <= 'z') ||
1851 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1852 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1853 (*pch == '!') || (*pch == '_') ||
1854 (*pch == ',') || (*pch == '.'))
1861 *(pt + 1) = HexList[(unsigned char)*pch][0];
1862 *(pt + 2) = HexList[(unsigned char)*pch][1];
1864 OutBuf->BufUsed += 3;
1872 * @ingroup StrBuf_DeEnCoder
1873 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1874 * @param OutBuf the output buffer
1875 * @param In Buffer to encode
1876 * @param PlainIn way in from plain old c strings
1878 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1880 const char *pch, *pche;
1884 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1886 if (PlainIn != NULL) {
1887 len = strlen(PlainIn);
1893 pche = pch + In->BufUsed;
1900 pt = OutBuf->buf + OutBuf->BufUsed;
1901 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1903 while (pch < pche) {
1905 IncreaseBuf(OutBuf, 1, -1);
1906 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1907 pt = OutBuf->buf + OutBuf->BufUsed;
1910 if((*pch >= 'a' && *pch <= 'z') ||
1911 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1912 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1913 (*pch == '!') || (*pch == '_') ||
1914 (*pch == ',') || (*pch == '.'))
1921 *(pt + 1) = HexList[(unsigned char)*pch][0];
1922 *(pt + 2) = HexList[(unsigned char)*pch][1];
1924 OutBuf->BufUsed += 3;
1932 * @ingroup StrBuf_DeEnCoder
1933 * @brief append a string with characters having a special meaning in xml encoded to the buffer
1934 * @param OutBuf the output buffer
1935 * @param In Buffer to encode
1936 * @param PlainIn way in from plain old c strings
1937 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1938 * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1940 void StrBufXMLEscAppend(StrBuf *OutBuf,
1942 const char *PlainIn,
1944 int OverrideLowChars)
1946 const char *pch, *pche;
1951 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1953 if (PlainIn != NULL) {
1955 len = strlen((const char*)PlainIn);
1962 pch = (const char*)In->buf;
1963 pche = pch + In->BufUsed;
1970 pt = OutBuf->buf + OutBuf->BufUsed;
1971 /**< we max append 6 chars at once plus the \0 */
1972 pte = OutBuf->buf + OutBuf->BufSize - 6;
1974 while (pch < pche) {
1976 OutBuf->BufUsed = pt - OutBuf->buf;
1977 IncreaseBuf(OutBuf, 1, -1);
1978 pte = OutBuf->buf + OutBuf->BufSize - 6;
1979 /**< we max append 3 chars at once plus the \0 */
1981 pt = OutBuf->buf + OutBuf->BufUsed;
1985 memcpy(pt, HKEY("<"));
1989 else if (*pch == '>') {
1990 memcpy(pt, HKEY(">"));
1994 else if (*pch == '&') {
1995 memcpy(pt, HKEY("&"));
1999 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
2004 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
2007 while ((IsUtf8Sequence > 0) &&
2020 *pt = HexList[*(unsigned char*)pch][0];
2022 *pt = HexList[*(unsigned char*)pch][1];
2031 OutBuf->BufUsed = pt - OutBuf->buf;
2036 * @ingroup StrBuf_DeEnCoder
2037 * @brief append a string in hex encoding to the buffer
2038 * @param OutBuf the output buffer
2039 * @param In Buffer to encode
2040 * @param PlainIn way in from plain old c strings
2041 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2043 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2045 const unsigned char *pch, *pche;
2049 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2051 if (PlainIn != NULL) {
2053 len = strlen((const char*)PlainIn);
2060 pch = (const unsigned char*)In->buf;
2061 pche = pch + In->BufUsed;
2068 pt = OutBuf->buf + OutBuf->BufUsed;
2069 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2071 while (pch < pche) {
2073 IncreaseBuf(OutBuf, 1, -1);
2074 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2075 pt = OutBuf->buf + OutBuf->BufUsed;
2078 *pt = HexList[*pch][0];
2080 *pt = HexList[*pch][1];
2081 pt ++; pch ++; OutBuf->BufUsed += 2;
2086 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2093 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2095 if (PlainIn != NULL) {
2097 len = strlen(PlainIn);
2110 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2112 if (ExpectLen > OutBuf->BufSize)
2113 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2116 pt = OutBuf->buf + OutBuf->BufUsed;
2118 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2121 OutBuf->BufUsed += len;
2126 * @ingroup StrBuf_DeEnCoder
2127 * @brief append a string in hex encoding to the buffer
2128 * @param OutBuf the output buffer
2129 * @param In Buffer to encode
2130 * @param PlainIn way in from plain old c strings
2132 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2134 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2138 * @ingroup StrBuf_DeEnCoder
2139 * @brief Append a string, escaping characters which have meaning in HTML.
2141 * @param Target target buffer
2142 * @param Source source buffer; set to NULL if you just have a C-String
2143 * @param PlainIn Plain-C string to append; set to NULL if unused
2144 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2145 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2146 * if set to 2, linebreaks are replaced by <br/>
2148 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2150 const char *aptr, *eiptr;
2154 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2157 if (PlainIn != NULL) {
2159 len = strlen(PlainIn);
2164 eiptr = aptr + Source->BufUsed;
2165 len = Source->BufUsed;
2171 bptr = Target->buf + Target->BufUsed;
2172 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2174 while (aptr < eiptr){
2176 IncreaseBuf(Target, 1, -1);
2177 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2178 bptr = Target->buf + Target->BufUsed;
2181 memcpy(bptr, "<", 4);
2183 Target->BufUsed += 4;
2185 else if (*aptr == '>') {
2186 memcpy(bptr, ">", 4);
2188 Target->BufUsed += 4;
2190 else if (*aptr == '&') {
2191 memcpy(bptr, "&", 5);
2193 Target->BufUsed += 5;
2195 else if (*aptr == '"') {
2196 memcpy(bptr, """, 6);
2198 Target->BufUsed += 6;
2200 else if (*aptr == '\'') {
2201 memcpy(bptr, "'", 5);
2203 Target->BufUsed += 5;
2205 else if (*aptr == LB) {
2210 else if (*aptr == RB) {
2215 else if (*aptr == QU) {
2220 else if ((*aptr == 32) && (nbsp == 1)) {
2221 memcpy(bptr, " ", 6);
2223 Target->BufUsed += 6;
2225 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2226 *bptr='\0'; /* nothing */
2228 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2229 memcpy(bptr, "<br/>", 11);
2231 Target->BufUsed += 11;
2235 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2236 *bptr='\0'; /* nothing */
2246 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2248 return Target->BufUsed;
2252 * @ingroup StrBuf_DeEnCoder
2253 * @brief Append a string, escaping characters which have meaning in HTML.
2254 * Converts linebreaks into blanks; escapes single quotes
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
2259 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2261 const char *aptr, *eiptr;
2265 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2268 if (PlainIn != NULL) {
2270 len = strlen(PlainIn);
2275 eiptr = aptr + Source->BufUsed;
2276 len = Source->BufUsed;
2282 eptr = Target->buf + Target->BufSize - 8;
2283 tptr = Target->buf + Target->BufUsed;
2285 while (aptr < eiptr){
2287 IncreaseBuf(Target, 1, -1);
2288 eptr = Target->buf + Target->BufSize - 8;
2289 tptr = Target->buf + Target->BufUsed;
2292 if (*aptr == '\n') {
2296 else if (*aptr == '\r') {
2300 else if (*aptr == '\'') {
2306 Target->BufUsed += 5;
2319 * @ingroup StrBuf_DeEnCoder
2320 * @brief Append a string, escaping characters which have meaning in ICAL.
2322 * @param Target target buffer
2323 * @param Source source buffer; set to NULL if you just have a C-String
2324 * @param PlainIn Plain-C string to append; set to NULL if unused
2326 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2328 const char *aptr, *eiptr;
2332 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2335 if (PlainIn != NULL) {
2337 len = strlen(PlainIn);
2342 eiptr = aptr + Source->BufUsed;
2343 len = Source->BufUsed;
2349 eptr = Target->buf + Target->BufSize - 8;
2350 tptr = Target->buf + Target->BufUsed;
2352 while (aptr < eiptr){
2353 if(tptr + 3 >= eptr) {
2354 IncreaseBuf(Target, 1, -1);
2355 eptr = Target->buf + Target->BufSize - 8;
2356 tptr = Target->buf + Target->BufUsed;
2359 if (*aptr == '\n') {
2366 else if (*aptr == '\r') {
2373 else if (*aptr == ',') {
2389 * @ingroup StrBuf_DeEnCoder
2390 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2392 * @param Target target buffer
2393 * @param Source source buffer; set to NULL if you just have a C-String
2394 * @param PlainIn Plain-C string to append; set to NULL if unused
2395 * @returns size of result or -1
2397 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2399 const char *aptr, *eiptr;
2404 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2407 if (PlainIn != NULL) {
2409 len = strlen(PlainIn);
2414 eiptr = aptr + Source->BufUsed;
2415 len = Source->BufUsed;
2421 bptr = Target->buf + Target->BufUsed;
2422 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2424 while (aptr < eiptr){
2426 IncreaseBuf(Target, 1, -1);
2427 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2428 bptr = Target->buf + Target->BufUsed;
2432 memcpy(bptr, HKEY("\\n"));
2434 Target->BufUsed += 2;
2437 memcpy(bptr, HKEY("\\r"));
2439 Target->BufUsed += 2;
2446 Target->BufUsed += 2;
2449 if ((*(aptr + 1) == 'u') &&
2450 isxdigit(*(aptr + 2)) &&
2451 isxdigit(*(aptr + 3)) &&
2452 isxdigit(*(aptr + 4)) &&
2453 isxdigit(*(aptr + 5)))
2454 { /* oh, a unicode escaper. let it pass through. */
2455 memcpy(bptr, aptr, 6);
2458 Target->BufUsed += 6;
2466 Target->BufUsed += 2;
2474 Target->BufUsed += 2;
2481 Target->BufUsed += 2;
2488 Target->BufUsed += 2;
2491 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2492 while (IsUtf8Sequence > 0){
2495 if (--IsUtf8Sequence)
2503 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2505 return Target->BufUsed;
2509 * @ingroup StrBuf_DeEnCoder
2510 * @brief Append a string, escaping characters which have meaning in HTML + json.
2512 * @param Target target buffer
2513 * @param Source source buffer; set to NULL if you just have a C-String
2514 * @param PlainIn Plain-C string to append; set to NULL if unused
2515 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2516 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2517 * if set to 2, linebreaks are replaced by <br/>
2519 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2521 const char *aptr, *eiptr;
2524 int IsUtf8Sequence = 0;
2526 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2529 if (PlainIn != NULL) {
2531 len = strlen(PlainIn);
2536 eiptr = aptr + Source->BufUsed;
2537 len = Source->BufUsed;
2543 bptr = Target->buf + Target->BufUsed;
2544 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2546 while (aptr < eiptr){
2548 IncreaseBuf(Target, 1, -1);
2549 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2550 bptr = Target->buf + Target->BufUsed;
2554 memcpy(bptr, HKEY("<"));
2556 Target->BufUsed += 4;
2559 memcpy(bptr, HKEY(">"));
2561 Target->BufUsed += 4;
2564 memcpy(bptr, HKEY("&"));
2566 Target->BufUsed += 5;
2579 switch (nolinebreaks) {
2581 *bptr='\0'; /* nothing */
2584 memcpy(bptr, HKEY("<br/>"));
2586 Target->BufUsed += 11;
2589 memcpy(bptr, HKEY("\\n"));
2591 Target->BufUsed += 2;
2595 switch (nolinebreaks) {
2598 *bptr='\0'; /* nothing */
2601 memcpy(bptr, HKEY("\\r"));
2603 Target->BufUsed += 2;
2613 Target->BufUsed += 2;
2616 if ((*(aptr + 1) == 'u') &&
2617 isxdigit(*(aptr + 2)) &&
2618 isxdigit(*(aptr + 3)) &&
2619 isxdigit(*(aptr + 4)) &&
2620 isxdigit(*(aptr + 5)))
2621 { /* oh, a unicode escaper. let it pass through. */
2622 memcpy(bptr, aptr, 6);
2625 Target->BufUsed += 6;
2633 Target->BufUsed += 2;
2641 Target->BufUsed += 2;
2648 Target->BufUsed += 2;
2655 Target->BufUsed += 2;
2659 memcpy(bptr, HKEY(" "));
2661 Target->BufUsed += 6;
2665 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2666 while (IsUtf8Sequence > 0){
2669 if (--IsUtf8Sequence)
2677 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2679 return Target->BufUsed;
2684 * @ingroup StrBuf_DeEnCoder
2685 * @brief replace all non-Ascii characters by another
2686 * @param Buf buffer to inspect
2687 * @param repl charater to stamp over non ascii chars
2689 void StrBufAsciify(StrBuf *Buf, const char repl)
2693 for (offset = 0; offset < Buf->BufUsed; offset ++)
2694 if (!isascii(Buf->buf[offset]))
2695 Buf->buf[offset] = repl;
2700 * @ingroup StrBuf_DeEnCoder
2701 * @brief unhide special chars hidden to the HTML escaper
2702 * @param target buffer to put the unescaped string in
2703 * @param source buffer to unescape
2705 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2710 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2716 FlushStrBuf(target);
2718 len = source->BufUsed;
2719 for (a = 0; a < len; ++a) {
2720 if (target->BufUsed >= target->BufSize)
2721 IncreaseBuf(target, 1, -1);
2723 if (source->buf[a] == '=') {
2724 hex[0] = source->buf[a + 1];
2725 hex[1] = source->buf[a + 2];
2728 sscanf(hex, "%02x", &b);
2729 target->buf[target->BufUsed] = b;
2730 target->buf[++target->BufUsed] = 0;
2734 target->buf[target->BufUsed] = source->buf[a];
2735 target->buf[++target->BufUsed] = 0;
2742 * @ingroup StrBuf_DeEnCoder
2743 * @brief hide special chars from the HTML escapers and friends
2744 * @param target buffer to put the escaped string in
2745 * @param source buffer to escape
2747 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2752 FlushStrBuf(target);
2754 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2759 len = source->BufUsed;
2760 for (i=0; i<len; ++i) {
2761 if (target->BufUsed + 4 >= target->BufSize)
2762 IncreaseBuf(target, 1, -1);
2763 if ( (isalnum(source->buf[i])) ||
2764 (source->buf[i]=='-') ||
2765 (source->buf[i]=='_') ) {
2766 target->buf[target->BufUsed++] = source->buf[i];
2769 sprintf(&target->buf[target->BufUsed],
2771 (0xFF &source->buf[i]));
2772 target->BufUsed += 3;
2775 target->buf[target->BufUsed + 1] = '\0';
2779 /*******************************************************************************
2780 * Quoted Printable de/encoding *
2781 *******************************************************************************/
2784 * @ingroup StrBuf_DeEnCoder
2785 * @brief decode a buffer from base 64 encoding; destroys original
2786 * @param Buf Buffor to transform
2788 int StrBufDecodeBase64(StrBuf *Buf)
2796 xferbuf = (char*) malloc(Buf->BufSize);
2797 if (xferbuf == NULL)
2801 siz = CtdlDecodeBase64(xferbuf,
2808 Buf->buf[Buf->BufUsed] = '\0';
2813 * @ingroup StrBuf_DeEnCoder
2814 * @brief decode a buffer from base 64 encoding; expects targetbuffer
2815 * @param BufIn Buffor to transform
2816 * @param BufOut Buffer to put result into
2818 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2820 if ((BufIn == NULL) || (BufOut == NULL))
2823 if (BufOut->BufSize < BufIn->BufUsed)
2824 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2826 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2829 return BufOut->BufUsed;
2832 typedef struct __z_enc_stream {
2837 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err)
2839 base64_decodestate *state;;
2846 state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2847 base64_init_decodestate(state);
2848 return (vStreamT*) state;
2853 z_enc_stream *stream;
2856 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2857 memset(stream, 0, sizeof(z_enc_stream));
2858 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2859 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2861 err = inflateInit(&stream->zstream);
2864 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2868 return (vStreamT*) stream;
2873 z_enc_stream *stream;
2876 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2877 memset(stream, 0, sizeof(z_enc_stream));
2878 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2879 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2880 /* write gzip header */
2881 stream->OutBuf.BufUsed = snprintf
2882 (stream->OutBuf.buf,
2883 stream->OutBuf.BufSize,
2884 "%c%c%c%c%c%c%c%c%c%c",
2885 gz_magic[0], gz_magic[1], Z_DEFLATED,
2886 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2889 err = deflateInit2(&stream->zstream,
2890 ZLibCompressionRatio,
2894 Z_DEFAULT_STRATEGY);
2896 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2900 return (vStreamT*) stream;
2910 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err)
2916 if ((vStream == NULL) || (*vStream==NULL)) {
2917 *Err = strerror(EINVAL);
2929 z_enc_stream *stream = (z_enc_stream *)*vStream;
2930 (void)inflateEnd(&stream->zstream);
2931 free(stream->OutBuf.buf);
2936 z_enc_stream *stream = (z_enc_stream *)*vStream;
2937 err = deflateEnd(&stream->zstream);
2942 free(stream->OutBuf.buf);
2953 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err) {
2969 z_enc_stream *stream = (z_enc_stream *)vStream;
2970 int org_outbuf_len = stream->OutBuf.BufUsed;
2972 unsigned int chunkavail;
2974 if (In->ReadWritePointer != NULL)
2976 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2977 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
2978 (In->ReadWritePointer - In->Buf->buf);
2982 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2983 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2986 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2987 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2989 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
2991 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
2993 if (Target && (LastChunk || (stream->OutBuf.BufUsed != org_outbuf_len))) {
2994 iSwapBuffers(Target->Buf, &stream->OutBuf);
2997 if (stream->zstream.avail_in == 0) {
2998 FlushStrBuf(In->Buf);
2999 In->ReadWritePointer = NULL;
3002 if (stream->zstream.avail_in < 64) {
3003 memmove(In->Buf->buf,
3004 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3005 stream->zstream.avail_in);
3007 In->Buf->BufUsed = stream->zstream.avail_in;
3008 In->Buf->buf[In->Buf->BufUsed] = '\0';
3011 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
3014 rc = (LastChunk && (err != Z_FINISH));
3015 if (!rc && (err != Z_OK)) {
3022 z_enc_stream *stream = (z_enc_stream *)vStream;
3023 int org_outbuf_len = stream->zstream.total_out;
3026 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3027 if (In->ReadWritePointer != NULL) {
3028 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3029 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - (In->ReadWritePointer - In->Buf->buf);
3032 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3033 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3037 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3038 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3040 err = inflate(&stream->zstream, Z_NO_FLUSH);
3042 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
3045 err = Z_DATA_ERROR; /* and fall through */
3050 (void)inflateEnd(&stream->zstream);
3054 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3056 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
3058 if (stream->zstream.avail_in == 0) {
3059 FlushStrBuf(In->Buf);
3060 In->ReadWritePointer = NULL;
3063 if (stream->zstream.avail_in < 64) {
3064 memmove(In->Buf->buf,
3065 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3066 stream->zstream.avail_in);
3068 In->Buf->BufUsed = stream->zstream.avail_in;
3069 In->Buf->buf[In->Buf->BufUsed] = '\0';
3073 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
3087 * @ingroup StrBuf_DeEnCoder
3088 * @brief decode a buffer from base 64 encoding; destroys original
3089 * @param Buf Buffor to transform
3091 int StrBufDecodeHex(StrBuf *Buf) {
3093 char *pch, *pche, *pchi;
3095 if (Buf == NULL) return -1;
3097 pch = pchi = Buf->buf;
3098 pche = pch + Buf->BufUsed;
3100 while (pchi < pche){
3101 ch = decode_hex(pchi);
3108 Buf->BufUsed = pch - Buf->buf;
3109 return Buf->BufUsed;
3113 * @ingroup StrBuf_DeEnCoder
3114 * @brief replace all chars >0x20 && < 0x7F with Mute
3115 * @param Mute char to put over invalid chars
3116 * @param Buf Buffor to transform
3118 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3122 if (Buf == NULL) return -1;
3123 pch = (unsigned char *)Buf->buf;
3124 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3125 if ((*pch < 0x20) || (*pch > 0x7F))
3129 return Buf->BufUsed;
3134 * @ingroup StrBuf_DeEnCoder
3135 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3136 * @param Buf Buffer to translate
3137 * @param StripBlanks Reduce several blanks to one?
3139 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3148 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3149 Buf->buf[Buf->BufUsed - 1] = '\0';
3154 while (a < Buf->BufUsed) {
3155 if (Buf->buf[a] == '+')
3157 else if (Buf->buf[a] == '%') {
3158 /* don't let % chars through, rather truncate the input. */
3159 if (a + 2 > Buf->BufUsed) {
3164 hex[0] = Buf->buf[a + 1];
3165 hex[1] = Buf->buf[a + 2];
3168 sscanf(hex, "%02x", &b);
3169 Buf->buf[a] = (char) b;
3170 len = Buf->BufUsed - a - 2;
3172 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3184 * @ingroup StrBuf_DeEnCoder
3185 * @brief RFC2047-encode a header field if necessary.
3186 * If no non-ASCII characters are found, the string
3187 * will be copied verbatim without encoding.
3189 * @param target Target buffer.
3190 * @param source Source string to be encoded.
3191 * @returns encoded length; -1 if non success.
3193 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3195 const char headerStr[] = "=?UTF-8?Q?";
3196 int need_to_encode = 0;
3200 if ((source == NULL) ||
3204 while ((i < source->BufUsed) &&
3205 (!IsEmptyStr (&source->buf[i])) &&
3206 (need_to_encode == 0)) {
3207 if (((unsigned char) source->buf[i] < 32) ||
3208 ((unsigned char) source->buf[i] > 126)) {
3214 if (!need_to_encode) {
3215 if (*target == NULL) {
3216 *target = NewStrBufPlain(source->buf, source->BufUsed);
3219 FlushStrBuf(*target);
3220 StrBufAppendBuf(*target, source, 0);
3223 return (*target)->BufUsed;
3227 if (*target == NULL)
3228 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3229 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3230 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3231 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3232 (*target)->BufUsed = sizeof(headerStr) - 1;
3233 for (i=0; (i < source->BufUsed); ++i) {
3234 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3235 IncreaseBuf(*target, 1, 0);
3236 ch = (unsigned char) source->buf[i];
3245 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3246 (*target)->BufUsed += 3;
3250 (*target)->buf[(*target)->BufUsed] = '_';
3252 (*target)->buf[(*target)->BufUsed] = ch;
3253 (*target)->BufUsed++;
3257 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3258 IncreaseBuf(*target, 1, 0);
3260 (*target)->buf[(*target)->BufUsed++] = '?';
3261 (*target)->buf[(*target)->BufUsed++] = '=';
3262 (*target)->buf[(*target)->BufUsed] = '\0';
3263 return (*target)->BufUsed;;
3267 * @ingroup StrBuf_DeEnCoder
3268 * @brief Quoted-Printable encode a message; make it < 80 columns width.
3269 * @param source Source string to be encoded.
3270 * @returns buffer with encoded message.
3272 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3276 const char *ptr, *eptr;
3280 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3282 OEptr = OutBuf->buf + OutBuf->BufSize;
3283 ptr = EncodeMe->buf;
3284 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3289 if (Optr + 4 >= OEptr)
3292 Offset = Optr - OutBuf->buf;
3293 OutBuf->BufUsed = Optr - OutBuf->buf;
3294 IncreaseBuf(OutBuf, 1, 0);
3295 Optr = OutBuf->buf + Offset;
3296 OEptr = OutBuf->buf + OutBuf->BufSize;
3300 /* ignore carriage returns */
3303 else if (*ptr == '\n') {
3304 /* hard line break */
3305 memcpy(Optr, HKEY("=0A"));
3310 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3311 ( (*ptr >= 62) && (*ptr <= 126) ))
3322 *Optr = HexList[ch][0];
3324 *Optr = HexList[ch][1];
3331 /* soft line break */
3332 if (isspace(*(Optr - 1))) {
3337 *Optr = HexList[ch][0];
3339 *Optr = HexList[ch][1];
3351 OutBuf->BufUsed = Optr - OutBuf->buf;
3357 static void AddRecipient(StrBuf *Target,
3359 StrBuf *EmailAddress,
3364 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3365 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3367 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3368 StrBufRFC2047encode(&EncBuf, UserName);
3369 StrBufAppendBuf(Target, EncBuf, 0);
3370 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3371 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3373 if (StrLength(EmailAddress) > 0){
3374 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3375 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3376 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3382 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3383 * \param Recp Source list of email recipients
3384 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3385 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3386 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3387 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3389 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3391 StrBuf *EmailAddress,
3395 const char *pch, *pche;
3396 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3398 if ((Recp == NULL) || (StrLength(Recp) == 0))
3402 pche = pch + StrLength(Recp);
3404 if (!CheckEncode(pch, -1, pche))
3405 return NewStrBufDup(Recp);
3407 Target = NewStrBufPlain(NULL, StrLength(Recp));
3409 while ((pch != NULL) && (pch < pche))
3411 while (isspace(*pch)) pch++;
3412 UserEnd = EmailStart = EmailEnd = NULL;
3414 if ((*pch == '"') || (*pch == '\'')) {
3415 UserStart = pch + 1;
3417 UserEnd = strchr(UserStart, *pch);
3418 if (UserEnd == NULL)
3419 break; ///TODO: Userfeedback??
3420 EmailStart = UserEnd + 1;
3421 while (isspace(*EmailStart))
3423 if (UserEnd == UserStart) {
3424 UserStart = UserEnd = NULL;
3427 if (*EmailStart == '<') {
3429 EmailEnd = strchr(EmailStart, '>');
3430 if (EmailEnd == NULL)
3431 EmailEnd = strchr(EmailStart, ',');
3435 EmailEnd = strchr(EmailStart, ',');
3437 if (EmailEnd == NULL)
3444 EmailEnd = strchr(UserStart, ',');
3445 if (EmailEnd == NULL) {
3446 EmailEnd = strchr(pch, '>');
3448 if (EmailEnd != NULL) {
3458 while ((EmailEnd > UserStart) && !gt &&
3459 ((*EmailEnd == ',') ||
3460 (*EmailEnd == '>') ||
3461 (isspace(*EmailEnd))))
3463 if (*EmailEnd == '>')
3468 if (EmailEnd == UserStart)
3472 EmailStart = strchr(UserStart, '<');
3473 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3475 UserEnd = EmailStart;
3477 while ((UserEnd > UserStart) &&
3478 isspace (*(UserEnd - 1)))
3481 if (UserStart >= UserEnd)
3482 UserStart = UserEnd = NULL;
3484 else { /* this is a local recipient... no domain, just a realname */
3485 EmailStart = UserStart;
3486 At = strchr(EmailStart, '@');
3492 EmailStart = UserStart;
3498 if ((UserStart != NULL) && (UserEnd != NULL))
3499 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3500 else if ((UserStart != NULL) && (UserEnd == NULL))
3501 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3503 FlushStrBuf(UserName);
3505 if ((EmailStart != NULL) && (EmailEnd != NULL))
3506 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3507 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3508 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3510 FlushStrBuf(EmailAddress);
3512 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3517 if ((pch != NULL) && (*pch == ','))
3519 if (pch != NULL) while (isspace(*pch))
3528 * @brief replaces all occurances of 'search' by 'replace'
3529 * @param buf Buffer to modify
3530 * @param search character to search
3531 * @param replace character to replace search by
3533 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3538 for (i=0; i<buf->BufUsed; i++)
3539 if (buf->buf[i] == search)
3540 buf->buf[i] = replace;
3546 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3547 * @param buf Buffer to modify
3549 void StrBufToUnixLF(StrBuf *buf)
3551 char *pche, *pchS, *pchT;
3555 pche = buf->buf + buf->BufUsed;
3556 pchS = pchT = buf->buf;
3562 if (*pchS != '\n') {
3571 buf->BufUsed = pchT - buf->buf;
3575 /*******************************************************************************
3576 * Iconv Wrapper; RFC822 de/encoding *
3577 *******************************************************************************/
3580 * @ingroup StrBuf_DeEnCoder
3581 * @brief Wrapper around iconv_open()
3582 * Our version adds aliases for non-standard Microsoft charsets
3583 * such as 'MS950', aliasing them to names like 'CP950'
3585 * @param tocode Target encoding
3586 * @param fromcode Source encoding
3587 * @param pic anonimized pointer to iconv struct
3589 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3592 iconv_t ic = (iconv_t)(-1) ;
3593 ic = iconv_open(tocode, fromcode);
3594 if (ic == (iconv_t)(-1) ) {
3595 char alias_fromcode[64];
3596 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3597 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3598 alias_fromcode[0] = 'C';
3599 alias_fromcode[1] = 'P';
3600 ic = iconv_open(tocode, alias_fromcode);
3603 *(iconv_t *)pic = ic;
3609 * @ingroup StrBuf_DeEnCoder
3610 * @brief find one chunk of a RFC822 encoded string
3611 * @param Buffer where to search
3612 * @param bptr where to start searching
3613 * @returns found position, NULL if none.
3615 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3618 /* Find the next ?Q? */
3619 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3622 end = strchr(bptr + 2, '?');
3627 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3628 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3629 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3630 (*(end + 2) == '?')) {
3631 /* skip on to the end of the cluster, the next ?= */
3632 end = strstr(end + 3, "?=");
3635 /* sort of half valid encoding, try to find an end. */
3636 end = strstr(bptr, "?=");
3643 * @ingroup StrBuf_DeEnCoder
3644 * @brief convert one buffer according to the preselected iconv pointer PIC
3645 * @param ConvertBuf buffer we need to translate
3646 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3647 * @param pic Pointer to the iconv-session Object
3649 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3655 char *ibuf; /**< Buffer of characters to be converted */
3656 char *obuf; /**< Buffer for converted characters */
3657 size_t ibuflen; /**< Length of input buffer */
3658 size_t obuflen; /**< Length of output buffer */
3661 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3664 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3665 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3666 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3668 ic = *(iconv_t*)pic;
3669 ibuf = ConvertBuf->buf;
3670 ibuflen = ConvertBuf->BufUsed;
3672 obuflen = TmpBuf->BufSize;
3674 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3677 if (errno == E2BIG) {
3679 IncreaseBuf(TmpBuf, 0, 0);
3684 else if (errno == EILSEQ){
3685 /* hm, invalid utf8 sequence... what to do now? */
3686 /* An invalid multibyte sequence has been encountered in the input */
3688 else if (errno == EINVAL) {
3689 /* An incomplete multibyte sequence has been encountered in the input. */
3692 FlushStrBuf(TmpBuf);
3695 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3696 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3698 /* little card game: wheres the red lady? */
3699 iSwapBuffers(ConvertBuf, TmpBuf);
3700 FlushStrBuf(TmpBuf);
3707 * @ingroup StrBuf_DeEnCoder
3708 * @brief catches one RFC822 encoded segment, and decodes it.
3709 * @param Target buffer to fill with result
3710 * @param DecodeMe buffer with stuff to process
3711 * @param SegmentStart points to our current segment in DecodeMe
3712 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3713 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3714 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3715 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3717 inline static void DecodeSegment(StrBuf *Target,
3718 const StrBuf *DecodeMe,
3719 const char *SegmentStart,
3720 const char *SegmentEnd,
3722 StrBuf *ConvertBuf2,
3723 StrBuf *FoundCharset)
3729 iconv_t ic = (iconv_t)(-1);
3733 /* Now we handle foreign character sets properly encoded
3734 * in RFC2047 format.
3736 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3737 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3738 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3739 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3740 if (FoundCharset != NULL) {
3741 FlushStrBuf(FoundCharset);
3742 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3744 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3745 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3747 *encoding = toupper(*encoding);
3748 if (*encoding == 'B') { /**< base64 */
3749 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3750 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3751 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3753 ConvertBuf->BufUsed);
3755 else if (*encoding == 'Q') { /**< quoted-printable */
3759 while (pos < ConvertBuf->BufUsed)
3761 if (ConvertBuf->buf[pos] == '_')
3762 ConvertBuf->buf[pos] = ' ';
3766 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3767 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3769 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3772 ConvertBuf->BufUsed);
3775 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3778 ctdl_iconv_open("UTF-8", charset, &ic);
3779 if (ic != (iconv_t)(-1) ) {
3781 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3782 StrBufAppendBuf(Target, ConvertBuf2, 0);
3787 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3793 * @ingroup StrBuf_DeEnCoder
3794 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3795 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3796 * @param Target where to put the decoded string to
3797 * @param DecodeMe buffer with encoded string
3798 * @param DefaultCharset if we don't find one, which should we use?
3799 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3800 * put it here for later use where no string might be known.
3802 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3805 StrBuf *ConvertBuf2;
3806 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3807 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3809 StrBuf_RFC822_2_Utf8(Target,
3815 FreeStrBuf(&ConvertBuf);
3816 FreeStrBuf(&ConvertBuf2);
3820 * @ingroup StrBuf_DeEnCoder
3821 * @brief Handle subjects with RFC2047 encoding such as:
3822 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3823 * @param Target where to put the decoded string to
3824 * @param DecodeMe buffer with encoded string
3825 * @param DefaultCharset if we don't find one, which should we use?
3826 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3827 * put it here for later use where no string might be known.
3828 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3829 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3831 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3832 const StrBuf *DecodeMe,
3833 const StrBuf* DefaultCharset,
3834 StrBuf *FoundCharset,
3836 StrBuf *ConvertBuf2)
3838 StrBuf *DecodedInvalidBuf = NULL;
3839 const StrBuf *DecodeMee = DecodeMe;
3840 const char *start, *end, *next, *nextend, *ptr = NULL;
3842 iconv_t ic = (iconv_t)(-1) ;
3847 int illegal_non_rfc2047_encoding = 0;
3850 if (DecodeMe == NULL)
3852 /* Sometimes, badly formed messages contain strings which were simply
3853 * written out directly in some foreign character set instead of
3854 * using RFC2047 encoding. This is illegal but we will attempt to
3855 * handle it anyway by converting from a user-specified default
3856 * charset to UTF-8 if we see any nonprintable characters.
3859 for (i=0; i<DecodeMe->BufUsed; ++i) {
3860 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3861 illegal_non_rfc2047_encoding = 1;
3866 if ((illegal_non_rfc2047_encoding) &&
3867 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3868 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3871 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3872 if (ic != (iconv_t)(-1) ) {
3873 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3874 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3875 DecodeMee = DecodedInvalidBuf;
3881 /* pre evaluate the first pair */
3883 start = strstr(DecodeMee->buf, "=?");
3884 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3886 end = FindNextEnd (DecodeMee, start + 2);
3888 StrBufAppendBuf(Target, DecodeMee, 0);
3889 FreeStrBuf(&DecodedInvalidBuf);
3894 if (start != DecodeMee->buf) {
3897 nFront = start - DecodeMee->buf;
3898 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3901 * Since spammers will go to all sorts of absurd lengths to get their
3902 * messages through, there are LOTS of corrupt headers out there.
3903 * So, prevent a really badly formed RFC2047 header from throwing
3904 * this function into an infinite loop.
3906 while ((start != NULL) &&
3913 DecodeSegment(Target,
3921 next = strstr(end, "=?");
3923 if ((next != NULL) &&
3925 nextend = FindNextEnd(DecodeMee, next);
3926 if (nextend == NULL)
3929 /* did we find two partitions */
3930 if ((next != NULL) &&
3934 while ((ptr < next) &&
3941 * did we find a gab just filled with blanks?
3942 * if not, copy its stuff over.
3946 StrBufAppendBufPlain(Target,
3952 /* our next-pair is our new first pair now. */
3958 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3959 if ((end != NULL) && (end < nextend)) {
3961 while ( (ptr < nextend) &&
3968 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3970 FreeStrBuf(&DecodedInvalidBuf);
3973 /*******************************************************************************
3974 * Manipulating UTF-8 Strings *
3975 *******************************************************************************/
3979 * @brief evaluate the length of an utf8 special character sequence
3980 * @param Char the character to examine
3981 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3983 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3986 unsigned char test = (1<<7);
3988 if ((*CharS & 0xC0) != 0xC0)
3992 ((test & ((unsigned char)*CharS)) != 0))
3997 if ((n > 6) || ((CharE - CharS) < n))
4004 * @brief detect whether this char starts an utf-8 encoded char
4005 * @param Char character to inspect
4006 * @returns yes or no
4008 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4010 /** 11??.???? indicates an UTF8 Sequence. */
4011 return ((Char & 0xC0) == 0xC0);
4016 * @brief measure the number of glyphs in an UTF8 string...
4017 * @param Buf string to measure
4018 * @returns the number of glyphs in Buf
4020 long StrBuf_Utf8StrLen(StrBuf *Buf)
4026 if ((Buf == NULL) || (Buf->BufUsed == 0))
4029 eptr = Buf->buf + Buf->BufUsed;
4030 while ((aptr < eptr) && (*aptr != '\0')) {
4031 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4032 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4033 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4046 * @brief cuts a string after maxlen glyphs
4047 * @param Buf string to cut to maxlen glyphs
4048 * @param maxlen how long may the string become?
4049 * @returns current length of the string
4051 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4057 eptr = Buf->buf + Buf->BufUsed;
4058 while ((aptr < eptr) && (*aptr != '\0')) {
4059 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4060 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4061 while ((*aptr++ != '\0') && (m-- > 0));
4070 Buf->BufUsed = aptr - Buf->buf;
4071 return Buf->BufUsed;
4074 return Buf->BufUsed;
4082 /*******************************************************************************
4084 *******************************************************************************/
4087 * @ingroup StrBuf_DeEnCoder
4088 * @brief uses the same calling syntax as compress2(), but it
4089 * creates a stream compatible with HTTP "Content-encoding: gzip"
4090 * @param dest compressed buffer
4091 * @param destLen length of the compresed data
4092 * @param source source to encode
4093 * @param sourceLen length of source to encode
4094 * @param level compression level
4097 int ZEXPORT compress_gzip(Bytef * dest,
4099 const Bytef * source,
4103 /* write gzip header */
4104 snprintf((char *) dest, *destLen,
4105 "%c%c%c%c%c%c%c%c%c%c",
4106 gz_magic[0], gz_magic[1], Z_DEFLATED,
4107 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4110 /* normal deflate */
4113 stream.next_in = (Bytef *) source;
4114 stream.avail_in = (uInt) sourceLen;
4115 stream.next_out = dest + 10L; // after header
4116 stream.avail_out = (uInt) * destLen;
4117 if ((uLong) stream.avail_out != *destLen)
4120 stream.zalloc = (alloc_func) 0;
4121 stream.zfree = (free_func) 0;
4122 stream.opaque = (voidpf) 0;
4124 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4125 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4129 err = deflate(&stream, Z_FINISH);
4130 if (err != Z_STREAM_END) {
4131 deflateEnd(&stream);
4132 return err == Z_OK ? Z_BUF_ERROR : err;
4134 *destLen = stream.total_out + 10L;
4136 /* write CRC and Length */
4137 uLong crc = crc32(0L, source, sourceLen);
4139 for (n = 0; n < 4; ++n, ++*destLen) {
4140 dest[*destLen] = (int) (crc & 0xff);
4143 uLong len = stream.total_in;
4144 for (n = 0; n < 4; ++n, ++*destLen) {
4145 dest[*destLen] = (int) (len & 0xff);
4148 err = deflateEnd(&stream);
4155 * @ingroup StrBuf_DeEnCoder
4156 * @brief compress the buffer with gzip
4157 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4158 * @param Buf buffer whose content is to be gzipped
4160 int CompressBuffer(StrBuf *Buf)
4163 char *compressed_data = NULL;
4164 size_t compressed_len, bufsize;
4167 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
4168 compressed_data = malloc(compressed_len);
4170 if (compressed_data == NULL)
4172 /* Flush some space after the used payload so valgrind shuts up... */
4173 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4174 Buf->buf[Buf->BufUsed + i++] = '\0';
4175 if (compress_gzip((Bytef *) compressed_data,
4178 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4181 Buf->buf = compressed_data;
4182 Buf->BufUsed = compressed_len;
4183 Buf->BufSize = bufsize;
4184 /* Flush some space after the used payload so valgrind shuts up... */
4186 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4187 Buf->buf[Buf->BufUsed + i++] = '\0';
4190 free(compressed_data);
4192 #endif /* HAVE_ZLIB */
4196 /*******************************************************************************
4197 * File I/O; Callbacks to libevent *
4198 *******************************************************************************/
4200 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4205 if ((FB == NULL) || (FB->Buf == NULL))
4209 * check whether the read pointer is somewhere in a range
4210 * where a cut left is inexpensive
4213 if (FB->ReadWritePointer != NULL)
4217 already_read = FB->ReadWritePointer - FB->Buf->buf;
4218 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4220 if (already_read != 0) {
4223 unread = FB->Buf->BufUsed - already_read;
4225 /* else nothing to compact... */
4227 FB->ReadWritePointer = FB->Buf->buf;
4228 bufremain = FB->Buf->BufSize;
4230 else if ((unread < 64) ||
4231 (bufremain < already_read))
4234 * if its just a tiny bit remaining, or we run out of space...
4237 FB->Buf->BufUsed = unread;
4238 if (unread < already_read)
4239 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4241 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4242 FB->ReadWritePointer = FB->Buf->buf;
4243 bufremain = FB->Buf->BufSize - unread - 1;
4245 else if (bufremain < (FB->Buf->BufSize / 10))
4247 /* get a bigger buffer */
4249 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4251 FB->ReadWritePointer = FB->Buf->buf + unread;
4253 bufremain = FB->Buf->BufSize - unread - 1;
4254 /*TODO: special increase function that won't copy the already read! */
4257 else if (bufremain < 10) {
4258 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4260 FB->ReadWritePointer = FB->Buf->buf;
4262 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4267 FB->ReadWritePointer = FB->Buf->buf;
4268 bufremain = FB->Buf->BufSize - 1;
4271 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4274 FB->Buf->BufUsed += n;
4275 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4280 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4285 if ((FB == NULL) || (FB->Buf == NULL))
4288 if (FB->ReadWritePointer != NULL)
4290 WriteRemain = FB->Buf->BufUsed -
4291 (FB->ReadWritePointer -
4295 FB->ReadWritePointer = FB->Buf->buf;
4296 WriteRemain = FB->Buf->BufUsed;
4299 n = write(fd, FB->ReadWritePointer, WriteRemain);
4301 FB->ReadWritePointer += n;
4303 if (FB->ReadWritePointer ==
4304 FB->Buf->buf + FB->Buf->BufUsed)
4306 FlushStrBuf(FB->Buf);
4307 FB->ReadWritePointer = NULL;
4310 // check whether we've got something to write
4311 // get the maximum chunk plus the pointer we can send
4312 // write whats there
4313 // if not all was sent, remember the send pointer for the next time
4314 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4320 * @ingroup StrBuf_IO
4321 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4322 * @param LineBuf your line will be copied here.
4323 * @param FB BLOB with lines of text...
4324 * @param Ptr moved arround to keep the next-line across several iterations
4325 * has to be &NULL on start; will be &NotNULL on end of buffer
4326 * @returns size of copied buffer
4328 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4330 const char *aptr, *ptr, *eptr;
4333 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4337 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4338 FB->ReadWritePointer = StrBufNOTNULL;
4342 FlushStrBuf(LineBuf);
4343 if (FB->ReadWritePointer == NULL)
4344 ptr = aptr = FB->Buf->buf;
4346 ptr = aptr = FB->ReadWritePointer;
4348 optr = LineBuf->buf;
4349 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4350 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4352 while ((ptr <= eptr) &&
4359 LineBuf->BufUsed = optr - LineBuf->buf;
4360 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4361 optr = LineBuf->buf + LineBuf->BufUsed;
4362 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4367 if (optr > LineBuf->buf)
4369 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4370 LineBuf->BufUsed = optr - LineBuf->buf;
4372 if ((FB->ReadWritePointer != NULL) &&
4373 (FB->ReadWritePointer != FB->Buf->buf))
4375 /* Ok, the client application read all the data
4376 it was interested in so far. Since there is more to read,
4377 we now shrink the buffer, and move the rest over.
4379 StrBufCutLeft(FB->Buf,
4380 FB->ReadWritePointer - FB->Buf->buf);
4381 FB->ReadWritePointer = FB->Buf->buf;
4383 return eMustReadMore;
4386 LineBuf->BufUsed = optr - LineBuf->buf;
4388 if ((ptr <= eptr) && (*ptr == '\r'))
4390 if ((ptr <= eptr) && (*ptr == '\n'))
4394 FB->ReadWritePointer = ptr;
4397 FlushStrBuf(FB->Buf);
4398 FB->ReadWritePointer = NULL;
4401 return eReadSuccess;
4405 * @ingroup StrBuf_CHUNKED_IO
4406 * @brief check whether the chunk-buffer has more data waiting or not.
4407 * @param FB Chunk-Buffer to inspect
4409 eReadState StrBufCheckBuffer(IOBuffer *FB)
4413 if (FB->Buf->BufUsed == 0)
4414 return eReadSuccess;
4415 if (FB->ReadWritePointer == NULL)
4416 return eBufferNotEmpty;
4417 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4418 return eBufferNotEmpty;
4419 return eReadSuccess;
4422 long IOBufferStrLength(IOBuffer *FB)
4424 if ((FB == NULL) || (FB->Buf == NULL))
4426 if (FB->ReadWritePointer == NULL)
4427 return StrLength(FB->Buf);
4429 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4433 /*******************************************************************************
4434 * File I/O; Prefer buffered read since its faster! *
4435 *******************************************************************************/
4438 * @ingroup StrBuf_IO
4439 * @brief Read a line from socket
4440 * flushes and closes the FD on error
4441 * @param buf the buffer to get the input to
4442 * @param fd pointer to the filedescriptor to read
4443 * @param append Append to an existing string or replace?
4444 * @param Error strerror() on error
4445 * @returns numbers of chars read
4447 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4449 int len, rlen, slen;
4451 if ((buf == NULL) || (buf->buf == NULL)) {
4452 *Error = strerror(EINVAL);
4459 slen = len = buf->BufUsed;
4461 rlen = read(*fd, &buf->buf[len], 1);
4463 *Error = strerror(errno);
4470 if (buf->buf[len] == '\n')
4472 if (buf->buf[len] != '\r')
4474 if (len + 2 >= buf->BufSize) {
4476 buf->buf[len+1] = '\0';
4477 IncreaseBuf(buf, 1, -1);
4481 buf->buf[len] = '\0';
4487 * @ingroup StrBuf_BufferedIO
4488 * @brief Read a line from socket
4489 * flushes and closes the FD on error
4490 * @param Line the line to read from the fd / I/O Buffer
4491 * @param buf the buffer to get the input to
4492 * @param fd pointer to the filedescriptor to read
4493 * @param timeout number of successless selects until we bail out
4494 * @param selectresolution how long to wait on each select
4495 * @param Error strerror() on error
4496 * @returns numbers of chars read
4498 int StrBufTCP_read_buffered_line(StrBuf *Line,
4502 int selectresolution,
4506 int nSuccessLess = 0;
4513 if (buf->BufUsed > 0) {
4514 pch = strchr(buf->buf, '\n');
4517 len = pch - buf->buf;
4518 if (len > 0 && (*(pch - 1) == '\r') )
4520 StrBufSub(Line, buf, 0, len - rlen);
4521 StrBufCutLeft(buf, len + 1);
4526 if (buf->BufSize - buf->BufUsed < 10)
4527 IncreaseBuf(buf, 1, -1);
4529 fdflags = fcntl(*fd, F_GETFL);
4530 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4532 while ((nSuccessLess < timeout) && (pch == NULL)) {
4534 tv.tv_sec = selectresolution;
4539 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4540 *Error = strerror(errno);
4546 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4551 &buf->buf[buf->BufUsed],
4552 buf->BufSize - buf->BufUsed - 1);
4554 *Error = strerror(errno);
4559 else if (rlen > 0) {
4561 buf->BufUsed += rlen;
4562 buf->buf[buf->BufUsed] = '\0';
4563 pch = strchr(buf->buf, '\n');
4564 if ((pch == NULL) &&
4565 (buf->BufUsed + 10 > buf->BufSize) &&
4566 (IncreaseBuf(buf, 1, -1) == -1))
4574 len = pch - buf->buf;
4575 if (len > 0 && (*(pch - 1) == '\r') )
4577 StrBufSub(Line, buf, 0, len - rlen);
4578 StrBufCutLeft(buf, len + 1);
4585 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4586 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4587 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4589 * @ingroup StrBuf_BufferedIO
4590 * @brief Read a line from socket
4591 * flushes and closes the FD on error
4592 * @param Line where to append our Line read from the fd / I/O Buffer;
4593 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4594 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4595 * @param fd pointer to the filedescriptor to read
4596 * @param timeout number of successless selects until we bail out
4597 * @param selectresolution how long to wait on each select
4598 * @param Error strerror() on error
4599 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4601 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4606 int selectresolution,
4609 const char *pche = NULL;
4610 const char *pos = NULL;
4612 int len, rlen, retlen;
4613 int nSuccessLess = 0;
4615 const char *pch = NULL;
4621 if ((Line == NULL) ||
4628 *Error = ErrRBLF_PreConditionFailed;
4633 if ((IOBuf->BufUsed > 0) &&
4635 (pos < IOBuf->buf + IOBuf->BufUsed))
4639 pche = IOBuf->buf + IOBuf->BufUsed;
4643 while ((pch < pche) && (*pch != '\n'))
4645 if (Line->BufUsed + 10 > Line->BufSize)
4648 apos = pcht - Line->buf;
4650 IncreaseBuf(Line, 1, -1);
4651 pcht = Line->buf + apos;
4659 if (len > 0 && (*(pch - 1) == '\r') )
4668 if ((pch >= pche) || (*pch == '\0'))
4676 if ((pch != NULL) &&
4679 if (pch + 1 >= pche) {
4692 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4694 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4695 IncreaseBuf(IOBuf, 1, -1);
4697 fdflags = fcntl(*fd, F_GETFL);
4698 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4701 while ((nSuccessLess < timeout) &&
4711 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4712 *Error = strerror(errno);
4716 *Error = ErrRBLF_SelectFailed;
4719 if (! FD_ISSET(*fd, &rfds) != 0) {
4725 &IOBuf->buf[IOBuf->BufUsed],
4726 IOBuf->BufSize - IOBuf->BufUsed - 1);
4728 *Error = strerror(errno);
4733 else if (rlen > 0) {
4735 pLF = IOBuf->buf + IOBuf->BufUsed;
4736 IOBuf->BufUsed += rlen;
4737 IOBuf->buf[IOBuf->BufUsed] = '\0';
4739 pche = IOBuf->buf + IOBuf->BufUsed;
4741 while ((pLF < pche) && (*pLF != '\n'))
4743 if ((pLF >= pche) || (*pLF == '\0'))
4746 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4750 if (pLF != NULL) apos = pLF - IOBuf->buf;
4751 IncreaseBuf(IOBuf, 1, -1);
4752 if (pLF != NULL) pLF = IOBuf->buf + apos;
4766 if (len > 0 && (*(pLF - 1) == '\r') )
4768 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4769 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4775 return retlen + len;
4777 *Error = ErrRBLF_NotEnoughSentFromServer;
4782 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4784 * @ingroup StrBuf_IO
4785 * @brief Input binary data from socket
4786 * flushes and closes the FD on error
4787 * @param Buf the buffer to get the input to
4788 * @param fd pointer to the filedescriptor to read
4789 * @param append Append to an existing string or replace?
4790 * @param nBytes the maximal number of bytes to read
4791 * @param Error strerror() on error
4792 * @returns numbers of chars read
4794 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4805 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4807 *Error = ErrRBLF_BLOBPreConditionFailed;
4812 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4813 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4815 ptr = Buf->buf + Buf->BufUsed;
4817 fdflags = fcntl(*fd, F_GETFL);
4818 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4820 while ((nRead < nBytes) &&
4830 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4831 *Error = strerror(errno);
4835 *Error = ErrRBLF_SelectFailed;
4838 if (! FD_ISSET(*fd, &rfds) != 0) {
4844 if ((rlen = read(*fd,
4846 nBytes - nRead)) == -1) {
4849 *Error = strerror(errno);
4854 Buf->BufUsed += rlen;
4856 Buf->buf[Buf->BufUsed] = '\0';
4860 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4861 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4863 * @ingroup StrBuf_BufferedIO
4864 * @brief Input binary data from socket
4865 * flushes and closes the FD on error
4866 * @param Blob put binary thing here
4867 * @param IOBuf the buffer to get the input to
4868 * @param Pos offset inside of IOBuf
4869 * @param fd pointer to the filedescriptor to read
4870 * @param append Append to an existing string or replace?
4871 * @param nBytes the maximal number of bytes to read
4872 * @param check whether we should search for '000\n' terminators in case of timeouts
4873 * @param Error strerror() on error
4874 * @returns numbers of chars read
4876 int StrBufReadBLOBBuffered(StrBuf *Blob,
4889 int nAlreadyRead = 0;
4894 int nSuccessLess = 0;
4897 if ((Blob == NULL) ||
4904 *Error = ErrRBB_BLOBFPreConditionFailed;
4910 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4911 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4916 rlen = pos - IOBuf->buf;
4918 rlen = IOBuf->BufUsed - rlen;
4921 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed))
4923 if (rlen < nBytes) {
4924 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4925 Blob->BufUsed += rlen;
4926 Blob->buf[Blob->BufUsed] = '\0';
4927 nAlreadyRead = nRead = rlen;
4930 if (rlen >= nBytes) {
4931 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4932 Blob->BufUsed += nBytes;
4933 Blob->buf[Blob->BufUsed] = '\0';
4934 if (rlen == nBytes) {
4946 if (IOBuf->BufSize < nBytes - nRead) {
4947 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4951 fdflags = fcntl(*fd, F_GETFL);
4952 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4960 while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
4967 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4968 *Error = strerror(errno);
4971 if (*Error == NULL) {
4972 *Error = ErrRBLF_SelectFailed;
4976 if (! FD_ISSET(*fd, &rfds) != 0) {
4981 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
4982 // if (rlen == -1) { 2021feb27 ajc changed this, apparently we will always get at least 1 byte unless the connection is broken
4986 *Error = strerror(errno);
4989 else if (rlen == 0){
4990 if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
4991 StrBufPlain(Blob, HKEY("\n000\n"));
4992 StrBufCutRight(Blob, 5);
4993 return Blob->BufUsed;
4995 else if (!IsNonBlock)
4997 else if (nSuccessLess > MaxTries) {
4999 *Error = ErrRBB_too_many_selects;
5003 else if (rlen > 0) {
5007 IOBuf->BufUsed += rlen;
5010 if (nSuccessLess >= MaxTries) {
5012 *Error = ErrRBB_too_many_selects;
5016 if (nRead > nBytes) {
5017 *Pos = IOBuf->buf + nBytes;
5019 Blob->buf[Blob->BufUsed] = '\0';
5020 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5024 return nRead + nAlreadyRead;
5028 * @ingroup StrBuf_IO
5029 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5030 * @param LineBuf your line will be copied here.
5031 * @param Buf BLOB with lines of text...
5032 * @param Ptr moved arround to keep the next-line across several iterations
5033 * has to be &NULL on start; will be &NotNULL on end of buffer
5034 * @returns size of remaining buffer
5036 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5038 const char *aptr, *ptr, *eptr;
5041 if ((Buf == NULL) ||
5042 (*Ptr == StrBufNOTNULL) ||
5044 (LineBuf->buf == NULL))
5046 *Ptr = StrBufNOTNULL;
5050 FlushStrBuf(LineBuf);
5052 ptr = aptr = Buf->buf;
5056 optr = LineBuf->buf;
5057 eptr = Buf->buf + Buf->BufUsed;
5058 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5060 while ((ptr <= eptr) &&
5067 LineBuf->BufUsed = optr - LineBuf->buf;
5068 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
5069 optr = LineBuf->buf + LineBuf->BufUsed;
5070 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5074 if ((ptr >= eptr) && (optr > LineBuf->buf))
5076 LineBuf->BufUsed = optr - LineBuf->buf;
5078 if ((ptr <= eptr) && (*ptr == '\r'))
5080 if ((ptr <= eptr) && (*ptr == '\n'))
5087 *Ptr = StrBufNOTNULL;
5090 return Buf->BufUsed - (ptr - Buf->buf);
5095 * @ingroup StrBuf_IO
5096 * @brief removes double slashes from pathnames
5097 * @param Dir directory string to filter
5098 * @param RemoveTrailingSlash allows / disallows trailing slashes
5100 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5106 while (!IsEmptyStr(a)) {
5118 if ((RemoveTrailingSlash) &&
5124 Dir->BufUsed = b - Dir->buf;
5129 * Decode a quoted-printable encoded StrBuf buffer "in place"
5130 * This is possible because the decoded will always be shorter than the encoded
5131 * so we don't have to worry about the buffer being to small.
5133 void StrBufDecodeQP(StrBuf *Buf)
5135 if (!Buf) { // sanity check #1
5139 int source_len = StrLength(Buf);
5140 if (source_len < 1) { // sanity check #2
5144 int spos = 0; // source position
5145 int tpos = 0; // target position
5147 while (spos < source_len) {
5148 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
5151 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
5154 else if (Buf->buf[spos] == '=') {
5157 sscanf(&Buf->buf[spos], "%02x", &ch);
5158 Buf->buf[tpos++] = ch;
5162 Buf->buf[tpos++] = Buf->buf[spos++];
5167 Buf->BufUsed = tpos;