2 * Copyright (c) 1987-2013 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/select.h>
29 #include <sys/types.h>
30 #define SHOW_ME_VAPPEND_PRINTF
33 #include "libcitadel.h"
49 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
50 const Bytef * source, uLong sourceLen, int level);
52 int BaseStrBufSize = 64;
55 const char *StrBufNOTNULL = ((char*) NULL) - 1;
57 const char HexList[256][3] = {
58 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
59 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
60 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
61 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
62 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
63 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
64 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
65 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
66 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
67 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
68 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
69 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
70 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
71 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
72 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
73 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
76 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
77 * StrBuf is a versatile class, aiding the handling of dynamic strings
78 * * reduce de/reallocations
79 * * reduce the need to remeasure it
80 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
81 * * allow asyncroneous IO for line and Blob based operations
82 * * reduce the use of memove in those
83 * * Quick filling in several operations with append functions
87 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
92 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
94 * use these operators to interfere with code demanding char*;
95 * if you need to own the content, smash me. Avoid, since we loose the length information.
99 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
101 * operations to get your Strings into a StrBuf, manipulating them, or appending
104 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
106 * Quick tokenizer; demands of the user to pull its tokens in sequence
110 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
112 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
116 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
118 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
119 * External Cursor to maintain the current read position inside of the buffer
120 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
124 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
130 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
132 * these functions translate the content of a buffer into another representation;
133 * some are combined Fillers and encoders
137 * Private Structure for the Stringbuffer
140 char *buf; /**< the pointer to the dynamic buffer */
141 long BufSize; /**< how many spcae do we optain */
142 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
143 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
145 long nIncreases; /**< for profiling; cound how many times we needed more */
146 char bt [SIZ]; /**< Stacktrace of last increase */
147 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
152 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
153 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
156 #ifdef HAVE_BACKTRACE
157 static void StrBufBacktrace(StrBuf *Buf, int which)
161 void *stack_frames[50];
166 pstart = pch = Buf->bt;
168 pstart = pch = Buf->bt_lastinc;
169 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
170 strings = backtrace_symbols(stack_frames, size);
171 for (i = 0; i < size; i++) {
173 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
175 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
184 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
186 if (hFreeDbglog == -1){
187 pid_t pid = getpid();
189 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
190 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
192 if ((*FreeMe)->nIncreases > 0)
196 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
198 (*FreeMe)->nIncreases,
202 (*FreeMe)->bt_lastinc);
203 n = write(hFreeDbglog, buf, n);
209 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
213 n = write(hFreeDbglog, buf, n);
217 void dbg_IncreaseBuf(StrBuf *IncMe)
220 #ifdef HAVE_BACKTRACE
221 StrBufBacktrace(Buf, 1);
225 void dbg_Init(StrBuf *Buf)
229 Buf->bt_lastinc[0] = '\0';
230 #ifdef HAVE_BACKTRACE
231 StrBufBacktrace(Buf, 0);
237 #define dbg_FreeStrBuf(a, b)
238 #define dbg_IncreaseBuf(a)
245 * @brief swaps the contents of two StrBufs
246 * this is to be used to have cheap switched between a work-buffer and a target buffer
248 * @param B second one
250 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
254 memcpy(&C, A, sizeof(*A));
255 memcpy(A, B, sizeof(*B));
256 memcpy(B, &C, sizeof(C));
261 * @ingroup StrBuf_Cast
262 * @brief Cast operator to Plain String
263 * @note if the buffer is altered by StrBuf operations, this pointer may become
264 * invalid. So don't lean on it after altering the buffer!
265 * Since this operation is considered cheap, rather call it often than risking
266 * your pointer to become invalid!
267 * @param Str the string we want to get the c-string representation for
268 * @returns the Pointer to the Content. Don't mess with it!
270 inline const char *ChrPtr(const StrBuf *Str)
278 * @ingroup StrBuf_Cast
279 * @brief since we know strlen()'s result, provide it here.
280 * @param Str the string to return the length to
281 * @returns contentlength of the buffer
283 inline int StrLength(const StrBuf *Str)
285 return (Str != NULL) ? Str->BufUsed : 0;
289 * @ingroup StrBuf_DeConstructors
290 * @brief local utility function to resize the buffer
291 * @param Buf the buffer whichs storage we should increase
292 * @param KeepOriginal should we copy the original buffer or just start over with a new one
293 * @param DestSize what should fit in after?
295 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
298 size_t NewSize = Buf->BufSize * 2;
304 while ((NewSize <= DestSize) && (NewSize != 0))
310 NewBuf= (char*) malloc(NewSize);
314 if (KeepOriginal && (Buf->BufUsed > 0))
316 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
325 Buf->BufSize = NewSize;
327 dbg_IncreaseBuf(Buf);
333 * @ingroup StrBuf_DeConstructors
334 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
335 * @param Buf Buffer to shrink (has to be empty)
336 * @param ThreshHold if the buffer is bigger then this, its readjusted
337 * @param NewSize if we Shrink it, how big are we going to be afterwards?
339 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
342 (Buf->BufUsed == 0) &&
343 (Buf->BufSize < ThreshHold)) {
345 Buf->buf = (char*) malloc(NewSize);
347 Buf->BufSize = NewSize;
352 * @ingroup StrBuf_DeConstructors
353 * @brief shrink long term buffers to their real size so they don't waste memory
354 * @param Buf buffer to shrink
355 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
356 * @returns physical size of the buffer
358 long StrBufShrinkToFit(StrBuf *Buf, int Force)
363 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
367 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
371 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
372 Buf->BufSize = Buf->BufUsed + 1;
380 * @ingroup StrBuf_DeConstructors
381 * @brief Allocate a new buffer with default buffer size
382 * @returns the new stringbuffer
384 StrBuf* NewStrBuf(void)
388 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
392 NewBuf->buf = (char*) malloc(BaseStrBufSize);
393 if (NewBuf->buf == NULL)
398 NewBuf->buf[0] = '\0';
399 NewBuf->BufSize = BaseStrBufSize;
401 NewBuf->ConstBuf = 0;
409 * @ingroup StrBuf_DeConstructors
410 * @brief Copy Constructor; returns a duplicate of CopyMe
411 * @param CopyMe Buffer to faxmilate
412 * @returns the new stringbuffer
414 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
421 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
425 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
426 if (NewBuf->buf == NULL)
432 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
433 NewBuf->BufUsed = CopyMe->BufUsed;
434 NewBuf->BufSize = CopyMe->BufSize;
435 NewBuf->ConstBuf = 0;
443 * @ingroup StrBuf_DeConstructors
444 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
445 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
446 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
447 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
448 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
449 * @returns the new stringbuffer
451 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
455 if (CreateRelpaceMe == NULL)
460 if (*CreateRelpaceMe != NULL)
461 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
463 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
467 if (CopyFlushMe == NULL)
469 if (*CreateRelpaceMe != NULL)
470 FlushStrBuf(*CreateRelpaceMe);
472 *CreateRelpaceMe = NewStrBuf();
477 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
478 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
479 * be a big IO-Buffer...
481 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
483 if (*CreateRelpaceMe == NULL)
485 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
490 NewBuf = *CreateRelpaceMe;
493 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
497 if (*CreateRelpaceMe == NULL)
499 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
503 NewBuf = *CreateRelpaceMe;
504 SwapBuffers (NewBuf, CopyFlushMe);
507 FlushStrBuf(CopyFlushMe);
512 * @ingroup StrBuf_DeConstructors
513 * @brief create a new Buffer using an existing c-string
514 * this function should also be used if you want to pre-suggest
515 * the buffer size to allocate in conjunction with ptr == NULL
516 * @param ptr the c-string to copy; may be NULL to create a blank instance
517 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
518 * @returns the new stringbuffer
520 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
523 size_t Siz = BaseStrBufSize;
526 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
531 CopySize = strlen((ptr != NULL)?ptr:"");
535 while ((Siz <= CopySize) && (Siz != 0))
544 NewBuf->buf = (char*) malloc(Siz);
545 if (NewBuf->buf == NULL)
550 NewBuf->BufSize = Siz;
552 memcpy(NewBuf->buf, ptr, CopySize);
553 NewBuf->buf[CopySize] = '\0';
554 NewBuf->BufUsed = CopySize;
557 NewBuf->buf[0] = '\0';
560 NewBuf->ConstBuf = 0;
568 * @ingroup StrBuf_DeConstructors
569 * @brief Set an existing buffer from a c-string
570 * @param Buf buffer to load
571 * @param ptr c-string to put into
572 * @param nChars set to -1 if we should work 0-terminated
573 * @returns the new length of the string
575 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
590 CopySize = strlen(ptr);
594 while ((Siz <= CopySize) && (Siz != 0))
602 if (Siz != Buf->BufSize)
603 IncreaseBuf(Buf, 0, Siz);
604 memcpy(Buf->buf, ptr, CopySize);
605 Buf->buf[CopySize] = '\0';
606 Buf->BufUsed = CopySize;
613 * @ingroup StrBuf_DeConstructors
614 * @brief use strbuf as wrapper for a string constant for easy handling
615 * @param StringConstant a string to wrap
616 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
618 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
622 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
625 NewBuf->buf = (char*) StringConstant;
626 NewBuf->BufSize = SizeOfStrConstant;
627 NewBuf->BufUsed = SizeOfStrConstant;
628 NewBuf->ConstBuf = 1;
637 * @ingroup StrBuf_DeConstructors
638 * @brief flush the content of a Buf; keep its struct
639 * @param buf Buffer to flush
641 int FlushStrBuf(StrBuf *buf)
643 if ((buf == NULL) || (buf->buf == NULL))
653 * @ingroup StrBuf_DeConstructors
654 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
655 * @param buf Buffer to wipe
657 int FLUSHStrBuf(StrBuf *buf)
663 if (buf->BufUsed > 0) {
664 memset(buf->buf, 0, buf->BufUsed);
671 int hFreeDbglog = -1;
674 * @ingroup StrBuf_DeConstructors
675 * @brief Release a Buffer
676 * Its a double pointer, so it can NULL your pointer
677 * so fancy SIG11 appear instead of random results
678 * @param FreeMe Pointer Pointer to the buffer to free
680 void FreeStrBuf (StrBuf **FreeMe)
685 dbg_FreeStrBuf(FreeMe, 'F');
687 if (!(*FreeMe)->ConstBuf)
688 free((*FreeMe)->buf);
694 * @ingroup StrBuf_DeConstructors
695 * @brief flatten a Buffer to the Char * we return
696 * Its a double pointer, so it can NULL your pointer
697 * so fancy SIG11 appear instead of random results
698 * The Callee then owns the buffer and is responsible for freeing it.
699 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
700 * @returns the pointer of the buffer; Callee owns the memory thereafter.
702 char *SmashStrBuf (StrBuf **SmashMe)
706 if ((SmashMe == NULL) || (*SmashMe == NULL))
709 dbg_FreeStrBuf(SmashMe, 'S');
711 Ret = (*SmashMe)->buf;
718 * @ingroup StrBuf_DeConstructors
719 * @brief Release the buffer
720 * If you want put your StrBuf into a Hash, use this as Destructor.
721 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
723 void HFreeStrBuf (void *VFreeMe)
725 StrBuf *FreeMe = (StrBuf*)VFreeMe;
729 dbg_FreeStrBuf(SmashMe, 'H');
731 if (!FreeMe->ConstBuf)
737 /*******************************************************************************
738 * Simple string transformations *
739 *******************************************************************************/
743 * @brief Wrapper around atol
745 long StrTol(const StrBuf *Buf)
750 return atol(Buf->buf);
757 * @brief Wrapper around atoi
759 int StrToi(const StrBuf *Buf)
763 if (Buf->BufUsed > 0)
764 return atoi(Buf->buf);
771 * @brief Checks to see if the string is a pure number
772 * @param Buf The buffer to inspect
773 * @returns 1 if its a pure number, 0, if not.
775 int StrBufIsNumber(const StrBuf *Buf) {
777 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
780 strtoll(Buf->buf, &pEnd, 10);
781 if (pEnd == Buf->buf)
783 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
785 if (Buf->buf == pEnd)
791 * @ingroup StrBuf_Filler
792 * @brief modifies a Single char of the Buf
793 * You can point to it via char* or a zero-based integer
794 * @param Buf The buffer to manipulate
795 * @param ptr char* to zero; use NULL if unused
796 * @param nThChar zero based pointer into the string; use -1 if unused
797 * @param PeekValue The Character to place into the position
799 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
804 nThChar = ptr - Buf->buf;
805 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
807 Buf->buf[nThChar] = PeekValue;
812 * @ingroup StrBuf_Filler
813 * @brief modifies a range of chars of the Buf
814 * You can point to it via char* or a zero-based integer
815 * @param Buf The buffer to manipulate
816 * @param ptr char* to zero; use NULL if unused
817 * @param nThChar zero based pointer into the string; use -1 if unused
818 * @param nChars how many chars are to be flushed?
819 * @param PookValue The Character to place into that area
821 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
826 nThChar = ptr - Buf->buf;
827 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
829 if (nThChar + nChars > Buf->BufUsed)
830 nChars = Buf->BufUsed - nThChar;
832 memset(Buf->buf + nThChar, PookValue, nChars);
833 /* just to be shure... */
834 Buf->buf[Buf->BufUsed] = 0;
839 * @ingroup StrBuf_Filler
840 * @brief Append a StringBuffer to the buffer
841 * @param Buf Buffer to modify
842 * @param AppendBuf Buffer to copy at the end of our buffer
843 * @param Offset Should we start copying from an offset?
845 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
847 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
848 (Buf == NULL) || (Buf->buf == NULL))
851 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
854 AppendBuf->BufUsed + Buf->BufUsed);
856 memcpy(Buf->buf + Buf->BufUsed,
857 AppendBuf->buf + Offset,
858 AppendBuf->BufUsed - Offset);
859 Buf->BufUsed += AppendBuf->BufUsed - Offset;
860 Buf->buf[Buf->BufUsed] = '\0';
865 * @ingroup StrBuf_Filler
866 * @brief Append a C-String to the buffer
867 * @param Buf Buffer to modify
868 * @param AppendBuf Buffer to copy at the end of our buffer
869 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
870 * @param Offset Should we start copying from an offset?
872 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
875 long BufSizeRequired;
877 if ((AppendBuf == NULL) || (Buf == NULL))
881 aps = strlen(AppendBuf + Offset);
883 aps = AppendSize - Offset;
885 BufSizeRequired = Buf->BufUsed + aps + 1;
886 if (Buf->BufSize <= BufSizeRequired)
887 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
889 memcpy(Buf->buf + Buf->BufUsed,
893 Buf->buf[Buf->BufUsed] = '\0';
897 * @ingroup StrBuf_Filler
898 * @brief sprintf like function appending the formated string to the buffer
899 * vsnprintf version to wrap into own calls
900 * @param Buf Buffer to extend by format and Params
901 * @param format printf alike format to add
902 * @param ap va_list containing the items for format
904 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
912 if ((Buf == NULL) || (format == NULL))
915 BufSize = Buf->BufSize;
916 nWritten = Buf->BufSize + 1;
917 Offset = Buf->BufUsed;
918 newused = Offset + nWritten;
920 while (newused >= BufSize) {
922 nWritten = vsnprintf(Buf->buf + Offset,
923 Buf->BufSize - Offset,
926 newused = Offset + nWritten;
927 if (newused >= Buf->BufSize) {
928 if (IncreaseBuf(Buf, 1, newused) == -1)
929 return; /* TODO: error handling? */
930 newused = Buf->BufSize + 1;
933 Buf->BufUsed = Offset + nWritten;
934 BufSize = Buf->BufSize;
941 * @ingroup StrBuf_Filler
942 * @brief sprintf like function appending the formated string to the buffer
943 * @param Buf Buffer to extend by format and Params
944 * @param format printf alike format to add
946 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
954 if ((Buf == NULL) || (format == NULL))
957 BufSize = Buf->BufSize;
958 nWritten = Buf->BufSize + 1;
959 Offset = Buf->BufUsed;
960 newused = Offset + nWritten;
962 while (newused >= BufSize) {
963 va_start(arg_ptr, format);
964 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
965 Buf->BufSize - Buf->BufUsed,
968 newused = Buf->BufUsed + nWritten;
969 if (newused >= Buf->BufSize) {
970 if (IncreaseBuf(Buf, 1, newused) == -1)
971 return; /* TODO: error handling? */
972 newused = Buf->BufSize + 1;
975 Buf->BufUsed += nWritten;
976 BufSize = Buf->BufSize;
983 * @ingroup StrBuf_Filler
984 * @brief sprintf like function putting the formated string into the buffer
985 * @param Buf Buffer to extend by format and Parameters
986 * @param format printf alike format to add
988 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
993 if ((Buf == NULL) || (format == NULL))
996 nWritten = Buf->BufSize + 1;
997 while (nWritten >= Buf->BufSize) {
998 va_start(arg_ptr, format);
999 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1001 if (nWritten >= Buf->BufSize) {
1002 if (IncreaseBuf(Buf, 0, 0) == -1)
1003 return; /* TODO: error handling? */
1004 nWritten = Buf->BufSize + 1;
1007 Buf->BufUsed = nWritten ;
1012 * @ingroup StrBuf_Filler
1013 * @brief Callback for cURL to append the webserver reply to a buffer
1014 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1015 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1016 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1017 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1019 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1028 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1029 return size * nmemb;
1035 * @brief extracts a substring from Source into dest
1036 * @param dest buffer to place substring into
1037 * @param Source string to copy substring from
1038 * @param Offset chars to skip from start
1039 * @param nChars number of chars to copy
1040 * @returns the number of chars copied; may be different from nChars due to the size of Source
1042 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1044 size_t NCharsRemain;
1045 if (Offset > Source->BufUsed)
1051 if (Offset + nChars < Source->BufUsed)
1053 if ((nChars >= dest->BufSize) &&
1054 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1056 memcpy(dest->buf, Source->buf + Offset, nChars);
1057 dest->BufUsed = nChars;
1058 dest->buf[dest->BufUsed] = '\0';
1061 NCharsRemain = Source->BufUsed - Offset;
1062 if ((NCharsRemain >= dest->BufSize) &&
1063 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1065 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1066 dest->BufUsed = NCharsRemain;
1067 dest->buf[dest->BufUsed] = '\0';
1068 return NCharsRemain;
1073 * @brief Cut nChars from the start of the string
1074 * @param Buf Buffer to modify
1075 * @param nChars how many chars should be skipped?
1077 void StrBufCutLeft(StrBuf *Buf, int nChars)
1079 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1080 if (nChars >= Buf->BufUsed) {
1084 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1085 Buf->BufUsed -= nChars;
1086 Buf->buf[Buf->BufUsed] = '\0';
1091 * @brief Cut the trailing n Chars from the string
1092 * @param Buf Buffer to modify
1093 * @param nChars how many chars should be trunkated?
1095 void StrBufCutRight(StrBuf *Buf, int nChars)
1097 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1100 if (nChars >= Buf->BufUsed) {
1104 Buf->BufUsed -= nChars;
1105 Buf->buf[Buf->BufUsed] = '\0';
1110 * @brief Cut the string after n Chars
1111 * @param Buf Buffer to modify
1112 * @param AfternChars after how many chars should we trunkate the string?
1113 * @param At if non-null and points inside of our string, cut it there.
1115 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1117 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1119 AfternChars = At - Buf->buf;
1122 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1124 Buf->BufUsed = AfternChars;
1125 Buf->buf[Buf->BufUsed] = '\0';
1131 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1132 * @param Buf the string to modify
1134 void StrBufTrim(StrBuf *Buf)
1137 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1139 while ((Buf->BufUsed > 0) &&
1140 isspace(Buf->buf[Buf->BufUsed - 1]))
1144 Buf->buf[Buf->BufUsed] = '\0';
1146 if (Buf->BufUsed == 0) return;
1148 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1151 if (delta > 0) StrBufCutLeft(Buf, delta);
1155 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1156 * @param Buf the string to modify
1158 void StrBufSpaceToBlank(StrBuf *Buf)
1162 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1165 pche = pch + Buf->BufUsed;
1174 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1179 if ((Buf == NULL) || (Buf->buf == NULL)) {
1183 pRight = strchr(Buf->buf, rightboundary);
1184 if (pRight != NULL) {
1185 StrBufCutAt(Buf, 0, pRight);
1188 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1189 if (pLeft != NULL) {
1190 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1196 * @ingroup StrBuf_Filler
1197 * @brief uppercase the contents of a buffer
1198 * @param Buf the buffer to translate
1200 void StrBufUpCase(StrBuf *Buf)
1204 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1207 pche = pch + Buf->BufUsed;
1208 while (pch < pche) {
1209 *pch = toupper(*pch);
1216 * @ingroup StrBuf_Filler
1217 * @brief lowercase the contents of a buffer
1218 * @param Buf the buffer to translate
1220 void StrBufLowerCase(StrBuf *Buf)
1224 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1227 pche = pch + Buf->BufUsed;
1228 while (pch < pche) {
1229 *pch = tolower(*pch);
1235 /*******************************************************************************
1236 * a tokenizer that kills, maims, and destroys *
1237 *******************************************************************************/
1240 * @ingroup StrBuf_Tokenizer
1241 * @brief Replace a token at a given place with a given length by another token with given length
1242 * @param Buf String where to work on
1243 * @param where where inside of the Buf is the search-token
1244 * @param HowLong How long is the token to be replaced
1245 * @param Repl Token to insert at 'where'
1246 * @param ReplLen Length of repl
1247 * @returns -1 if fail else length of resulting Buf
1249 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1250 const char *Repl, long ReplLen)
1253 if ((Buf == NULL) ||
1254 (where > Buf->BufUsed) ||
1255 (where + HowLong > Buf->BufUsed))
1258 if (where + ReplLen - HowLong > Buf->BufSize)
1259 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1262 memmove(Buf->buf + where + ReplLen,
1263 Buf->buf + where + HowLong,
1264 Buf->BufUsed - where - HowLong);
1266 memcpy(Buf->buf + where,
1269 Buf->BufUsed += ReplLen - HowLong;
1271 return Buf->BufUsed;
1275 * @ingroup StrBuf_Tokenizer
1276 * @brief Counts the numbmer of tokens in a buffer
1277 * @param source String to count tokens in
1278 * @param tok Tokenizer char to count
1279 * @returns numbers of tokenizer chars found
1281 int StrBufNum_tokens(const StrBuf *source, char tok)
1285 if ((source == NULL) || (source->BufUsed == 0))
1287 if ((source->BufUsed == 1) && (*source->buf == tok))
1291 pche = pch + source->BufUsed;
1302 * @ingroup StrBuf_Tokenizer
1303 * @brief a string tokenizer
1304 * @param Source StringBuffer to read into
1305 * @param parmnum n'th Parameter to remove
1306 * @param separator tokenizer character
1307 * @returns -1 if not found, else length of token.
1309 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1312 char *d, *s, *end; /* dest, source */
1315 /* Find desired @parameter */
1316 end = Source->buf + Source->BufUsed;
1318 while ((d <= end) &&
1321 /* End of string, bail! */
1326 if (*d == separator) {
1331 if ((d == NULL) || (d >= end))
1332 return 0; /* @Parameter not found */
1334 /* Find next @parameter */
1336 while ((s <= end) &&
1337 (*s && *s != separator))
1341 if (*s == separator)
1345 /* Hack and slash */
1350 memmove(d, s, Source->BufUsed - (s - Source->buf));
1351 Source->BufUsed += ReducedBy;
1352 Source->buf[Source->BufUsed] = '\0';
1354 else if (d == Source->buf) {
1356 Source->BufUsed = 0;
1360 Source->BufUsed += ReducedBy;
1371 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1373 const StrBuf Temp = {
1386 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1390 * @ingroup StrBuf_Tokenizer
1391 * @brief a string tokenizer
1392 * @param dest Destination StringBuffer
1393 * @param Source StringBuffer to read into
1394 * @param parmnum n'th Parameter to extract
1395 * @param separator tokenizer character
1396 * @returns -1 if not found, else length of token.
1398 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1400 const char *s, *e; //* source * /
1401 int len = 0; //* running total length of extracted string * /
1402 int current_token = 0; //* token currently being processed * /
1405 dest->buf[0] = '\0';
1411 if ((Source == NULL) || (Source->BufUsed ==0)) {
1415 e = s + Source->BufUsed;
1418 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1420 while ((s < e) && !IsEmptyStr(s)) {
1421 if (*s == separator) {
1424 if (len >= dest->BufSize) {
1425 dest->BufUsed = len;
1426 if (IncreaseBuf(dest, 1, -1) < 0) {
1431 if ( (current_token == parmnum) &&
1432 (*s != separator)) {
1433 dest->buf[len] = *s;
1436 else if (current_token > parmnum) {
1442 dest->buf[len] = '\0';
1443 dest->BufUsed = len;
1445 if (current_token < parmnum) {
1446 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1449 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1458 * @ingroup StrBuf_Tokenizer
1459 * @brief a string tokenizer to fetch an integer
1460 * @param Source String containing tokens
1461 * @param parmnum n'th Parameter to extract
1462 * @param separator tokenizer character
1463 * @returns 0 if not found, else integer representation of the token
1465 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1475 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1482 * @ingroup StrBuf_Tokenizer
1483 * @brief a string tokenizer to fetch a long integer
1484 * @param Source String containing tokens
1485 * @param parmnum n'th Parameter to extract
1486 * @param separator tokenizer character
1487 * @returns 0 if not found, else long integer representation of the token
1489 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1499 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1507 * @ingroup StrBuf_Tokenizer
1508 * @brief a string tokenizer to fetch an unsigned long
1509 * @param Source String containing tokens
1510 * @param parmnum n'th Parameter to extract
1511 * @param separator tokenizer character
1512 * @returns 0 if not found, else unsigned long representation of the token
1514 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1525 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1529 return (unsigned long) atol(pnum);
1538 * @ingroup StrBuf_NextTokenizer
1539 * @brief a string tokenizer; Bounds checker
1540 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1541 * @param Source our tokenbuffer
1542 * @param pStart the token iterator pointer to inspect
1543 * @returns whether the revolving pointer is inside of the search range
1545 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1547 if ((Source == NULL) ||
1548 (*pStart == StrBufNOTNULL) ||
1549 (Source->BufUsed == 0))
1553 if (*pStart == NULL)
1557 else if (*pStart > Source->buf + Source->BufUsed)
1561 else if (*pStart <= Source->buf)
1570 * @ingroup StrBuf_NextTokenizer
1571 * @brief a string tokenizer
1572 * @param dest Destination StringBuffer
1573 * @param Source StringBuffer to read into
1574 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1575 * @param separator tokenizer
1576 * @returns -1 if not found, else length of token.
1578 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1580 const char *s; /* source */
1581 const char *EndBuffer; /* end stop of source buffer */
1582 int current_token = 0; /* token currently being processed */
1583 int len = 0; /* running total length of extracted string */
1585 if ((Source == NULL) ||
1586 (Source->BufUsed == 0) )
1588 *pStart = StrBufNOTNULL;
1594 EndBuffer = Source->buf + Source->BufUsed;
1598 dest->buf[0] = '\0';
1603 *pStart = EndBuffer + 1;
1607 if (*pStart == NULL)
1609 *pStart = Source->buf; /* we're starting to examine this buffer. */
1611 else if ((*pStart < Source->buf) ||
1612 (*pStart > EndBuffer ) )
1614 return -1; /* no more tokens to find. */
1618 /* start to find the next token */
1619 while ((s <= EndBuffer) &&
1620 (current_token == 0) )
1622 if (*s == separator)
1624 /* we found the next token */
1628 if (len >= dest->BufSize)
1630 /* our Dest-buffer isn't big enough, increase it. */
1631 dest->BufUsed = len;
1633 if (IncreaseBuf(dest, 1, -1) < 0) {
1634 /* WHUT? no more mem? bail out. */
1641 if ( (current_token == 0 ) && /* are we in our target token? */
1642 (!IsEmptyStr(s) ) &&
1643 (separator != *s) ) /* don't copy the token itself */
1645 dest->buf[len] = *s; /* Copy the payload */
1646 ++len; /* remember the bigger size. */
1652 /* did we reach the end? */
1653 if ((s > EndBuffer)) {
1654 EndBuffer = StrBufNOTNULL;
1655 *pStart = EndBuffer;
1658 *pStart = s; /* remember the position for the next run */
1661 /* sanitize our extracted token */
1662 dest->buf[len] = '\0';
1663 dest->BufUsed = len;
1670 * @ingroup StrBuf_NextTokenizer
1671 * @brief a string tokenizer
1672 * @param Source StringBuffer to read from
1673 * @param pStart pointer to the end of the last token. Feed with NULL.
1674 * @param separator tokenizer character
1675 * @param nTokens number of tokens to fastforward over
1676 * @returns -1 if not found, else length of token.
1678 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1680 const char *s, *EndBuffer; //* source * /
1681 int len = 0; //* running total length of extracted string * /
1682 int current_token = 0; //* token currently being processed * /
1684 if ((Source == NULL) ||
1685 (Source->BufUsed ==0)) {
1689 return Source->BufUsed;
1691 if (*pStart == NULL)
1692 *pStart = Source->buf;
1694 EndBuffer = Source->buf + Source->BufUsed;
1696 if ((*pStart < Source->buf) ||
1697 (*pStart > EndBuffer)) {
1705 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1707 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1708 if (*s == separator) {
1711 if (current_token >= nTokens) {
1723 * @ingroup StrBuf_NextTokenizer
1724 * @brief a string tokenizer to fetch an integer
1725 * @param Source StringBuffer to read from
1726 * @param pStart Cursor on the tokenstring
1727 * @param separator tokenizer character
1728 * @returns 0 if not found, else integer representation of the token
1730 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1740 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1747 * @ingroup StrBuf_NextTokenizer
1748 * @brief a string tokenizer to fetch a long integer
1749 * @param Source StringBuffer to read from
1750 * @param pStart Cursor on the tokenstring
1751 * @param separator tokenizer character
1752 * @returns 0 if not found, else long integer representation of the token
1754 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1764 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1772 * @ingroup StrBuf_NextTokenizer
1773 * @brief a string tokenizer to fetch an unsigned long
1774 * @param Source StringBuffer to read from
1775 * @param pStart Cursor on the tokenstring
1776 * @param separator tokenizer character
1777 * @returns 0 if not found, else unsigned long representation of the token
1779 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1790 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1794 return (unsigned long) atol(pnum);
1804 /*******************************************************************************
1805 * Escape Appending *
1806 *******************************************************************************/
1809 * @ingroup StrBuf_DeEnCoder
1810 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1811 * @param OutBuf the output buffer
1812 * @param In Buffer to encode
1813 * @param PlainIn way in from plain old c strings
1815 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1817 const char *pch, *pche;
1821 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1823 if (PlainIn != NULL) {
1824 len = strlen(PlainIn);
1830 pche = pch + In->BufUsed;
1837 pt = OutBuf->buf + OutBuf->BufUsed;
1838 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1840 while (pch < pche) {
1842 IncreaseBuf(OutBuf, 1, -1);
1843 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1844 pt = OutBuf->buf + OutBuf->BufUsed;
1847 if((*pch >= 'a' && *pch <= 'z') ||
1848 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1849 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1850 (*pch == '!') || (*pch == '_') ||
1851 (*pch == ',') || (*pch == '.'))
1858 *(pt + 1) = HexList[(unsigned char)*pch][0];
1859 *(pt + 2) = HexList[(unsigned char)*pch][1];
1861 OutBuf->BufUsed += 3;
1869 * @ingroup StrBuf_DeEnCoder
1870 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1871 * @param OutBuf the output buffer
1872 * @param In Buffer to encode
1873 * @param PlainIn way in from plain old c strings
1875 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1877 const char *pch, *pche;
1881 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1883 if (PlainIn != NULL) {
1884 len = strlen(PlainIn);
1890 pche = pch + In->BufUsed;
1897 pt = OutBuf->buf + OutBuf->BufUsed;
1898 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1900 while (pch < pche) {
1902 IncreaseBuf(OutBuf, 1, -1);
1903 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1904 pt = OutBuf->buf + OutBuf->BufUsed;
1907 if((*pch >= 'a' && *pch <= 'z') ||
1908 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1909 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1910 (*pch == '!') || (*pch == '_') ||
1911 (*pch == ',') || (*pch == '.'))
1918 *(pt + 1) = HexList[(unsigned char)*pch][0];
1919 *(pt + 2) = HexList[(unsigned char)*pch][1];
1921 OutBuf->BufUsed += 3;
1929 * @ingroup StrBuf_DeEnCoder
1930 * @brief append a string with characters having a special meaning in xml encoded to the buffer
1931 * @param OutBuf the output buffer
1932 * @param In Buffer to encode
1933 * @param PlainIn way in from plain old c strings
1934 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1935 * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1937 void StrBufXMLEscAppend(StrBuf *OutBuf,
1939 const char *PlainIn,
1941 int OverrideLowChars)
1943 const char *pch, *pche;
1948 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1950 if (PlainIn != NULL) {
1952 len = strlen((const char*)PlainIn);
1959 pch = (const char*)In->buf;
1960 pche = pch + In->BufUsed;
1967 pt = OutBuf->buf + OutBuf->BufUsed;
1968 /**< we max append 6 chars at once plus the \0 */
1969 pte = OutBuf->buf + OutBuf->BufSize - 6;
1971 while (pch < pche) {
1973 OutBuf->BufUsed = pt - OutBuf->buf;
1974 IncreaseBuf(OutBuf, 1, -1);
1975 pte = OutBuf->buf + OutBuf->BufSize - 6;
1976 /**< we max append 3 chars at once plus the \0 */
1978 pt = OutBuf->buf + OutBuf->BufUsed;
1982 memcpy(pt, HKEY("<"));
1986 else if (*pch == '>') {
1987 memcpy(pt, HKEY(">"));
1991 else if (*pch == '&') {
1992 memcpy(pt, HKEY("&"));
1996 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
2000 else if (*pch < 0x20) {
2001 /* we probably shouldn't be doing this */
2002 if (OverrideLowChars)
2012 *pt = HexList[*(unsigned char*)pch][0];
2014 *pt = HexList[*(unsigned char*)pch][1];
2022 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
2025 while (IsUtf8Sequence > 0){
2036 *pt = HexList[*(unsigned char*)pch][0];
2038 *pt = HexList[*(unsigned char*)pch][1];
2047 OutBuf->BufUsed = pt - OutBuf->buf;
2052 * @ingroup StrBuf_DeEnCoder
2053 * @brief append a string in hex encoding to the buffer
2054 * @param OutBuf the output buffer
2055 * @param In Buffer to encode
2056 * @param PlainIn way in from plain old c strings
2057 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2059 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2061 const unsigned char *pch, *pche;
2065 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2067 if (PlainIn != NULL) {
2069 len = strlen((const char*)PlainIn);
2076 pch = (const unsigned char*)In->buf;
2077 pche = pch + In->BufUsed;
2084 pt = OutBuf->buf + OutBuf->BufUsed;
2085 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2087 while (pch < pche) {
2089 IncreaseBuf(OutBuf, 1, -1);
2090 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2091 pt = OutBuf->buf + OutBuf->BufUsed;
2094 *pt = HexList[*pch][0];
2096 *pt = HexList[*pch][1];
2097 pt ++; pch ++; OutBuf->BufUsed += 2;
2102 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2109 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2111 if (PlainIn != NULL) {
2113 len = strlen(PlainIn);
2126 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2128 if (ExpectLen > OutBuf->BufSize)
2129 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2132 pt = OutBuf->buf + OutBuf->BufUsed;
2134 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2137 OutBuf->BufUsed += len;
2142 * @ingroup StrBuf_DeEnCoder
2143 * @brief append a string in hex encoding to the buffer
2144 * @param OutBuf the output buffer
2145 * @param In Buffer to encode
2146 * @param PlainIn way in from plain old c strings
2148 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2150 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2154 * @ingroup StrBuf_DeEnCoder
2155 * @brief Append a string, escaping characters which have meaning in HTML.
2157 * @param Target target buffer
2158 * @param Source source buffer; set to NULL if you just have a C-String
2159 * @param PlainIn Plain-C string to append; set to NULL if unused
2160 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2161 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2162 * if set to 2, linebreaks are replaced by <br/>
2164 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2166 const char *aptr, *eiptr;
2170 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2173 if (PlainIn != NULL) {
2175 len = strlen(PlainIn);
2180 eiptr = aptr + Source->BufUsed;
2181 len = Source->BufUsed;
2187 bptr = Target->buf + Target->BufUsed;
2188 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2190 while (aptr < eiptr){
2192 IncreaseBuf(Target, 1, -1);
2193 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2194 bptr = Target->buf + Target->BufUsed;
2197 memcpy(bptr, "<", 4);
2199 Target->BufUsed += 4;
2201 else if (*aptr == '>') {
2202 memcpy(bptr, ">", 4);
2204 Target->BufUsed += 4;
2206 else if (*aptr == '&') {
2207 memcpy(bptr, "&", 5);
2209 Target->BufUsed += 5;
2211 else if (*aptr == '"') {
2212 memcpy(bptr, """, 6);
2214 Target->BufUsed += 6;
2216 else if (*aptr == '\'') {
2217 memcpy(bptr, "'", 5);
2219 Target->BufUsed += 5;
2221 else if (*aptr == LB) {
2226 else if (*aptr == RB) {
2231 else if (*aptr == QU) {
2236 else if ((*aptr == 32) && (nbsp == 1)) {
2237 memcpy(bptr, " ", 6);
2239 Target->BufUsed += 6;
2241 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2242 *bptr='\0'; /* nothing */
2244 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2245 memcpy(bptr, "<br/>", 11);
2247 Target->BufUsed += 11;
2251 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2252 *bptr='\0'; /* nothing */
2262 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2264 return Target->BufUsed;
2268 * @ingroup StrBuf_DeEnCoder
2269 * @brief Append a string, escaping characters which have meaning in HTML.
2270 * Converts linebreaks into blanks; escapes single quotes
2271 * @param Target target buffer
2272 * @param Source source buffer; set to NULL if you just have a C-String
2273 * @param PlainIn Plain-C string to append; set to NULL if unused
2275 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2277 const char *aptr, *eiptr;
2281 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2284 if (PlainIn != NULL) {
2286 len = strlen(PlainIn);
2291 eiptr = aptr + Source->BufUsed;
2292 len = Source->BufUsed;
2298 eptr = Target->buf + Target->BufSize - 8;
2299 tptr = Target->buf + Target->BufUsed;
2301 while (aptr < eiptr){
2303 IncreaseBuf(Target, 1, -1);
2304 eptr = Target->buf + Target->BufSize - 8;
2305 tptr = Target->buf + Target->BufUsed;
2308 if (*aptr == '\n') {
2312 else if (*aptr == '\r') {
2316 else if (*aptr == '\'') {
2322 Target->BufUsed += 5;
2335 * @ingroup StrBuf_DeEnCoder
2336 * @brief Append a string, escaping characters which have meaning in ICAL.
2338 * @param Target target buffer
2339 * @param Source source buffer; set to NULL if you just have a C-String
2340 * @param PlainIn Plain-C string to append; set to NULL if unused
2342 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2344 const char *aptr, *eiptr;
2348 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2351 if (PlainIn != NULL) {
2353 len = strlen(PlainIn);
2358 eiptr = aptr + Source->BufUsed;
2359 len = Source->BufUsed;
2365 eptr = Target->buf + Target->BufSize - 8;
2366 tptr = Target->buf + Target->BufUsed;
2368 while (aptr < eiptr){
2369 if(tptr + 3 >= eptr) {
2370 IncreaseBuf(Target, 1, -1);
2371 eptr = Target->buf + Target->BufSize - 8;
2372 tptr = Target->buf + Target->BufUsed;
2375 if (*aptr == '\n') {
2382 else if (*aptr == '\r') {
2389 else if (*aptr == ',') {
2405 * @ingroup StrBuf_DeEnCoder
2406 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2408 * @param Target target buffer
2409 * @param Source source buffer; set to NULL if you just have a C-String
2410 * @param PlainIn Plain-C string to append; set to NULL if unused
2411 * @returns size of result or -1
2413 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2415 const char *aptr, *eiptr;
2420 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2423 if (PlainIn != NULL) {
2425 len = strlen(PlainIn);
2430 eiptr = aptr + Source->BufUsed;
2431 len = Source->BufUsed;
2437 bptr = Target->buf + Target->BufUsed;
2438 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2440 while (aptr < eiptr){
2442 IncreaseBuf(Target, 1, -1);
2443 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2444 bptr = Target->buf + Target->BufUsed;
2448 memcpy(bptr, HKEY("\\n"));
2450 Target->BufUsed += 2;
2453 memcpy(bptr, HKEY("\\r"));
2455 Target->BufUsed += 2;
2462 Target->BufUsed += 2;
2465 if ((*(aptr + 1) == 'u') &&
2466 isxdigit(*(aptr + 2)) &&
2467 isxdigit(*(aptr + 3)) &&
2468 isxdigit(*(aptr + 4)) &&
2469 isxdigit(*(aptr + 5)))
2470 { /* oh, a unicode escaper. let it pass through. */
2471 memcpy(bptr, aptr, 6);
2474 Target->BufUsed += 6;
2482 Target->BufUsed += 2;
2490 Target->BufUsed += 2;
2497 Target->BufUsed += 2;
2504 Target->BufUsed += 2;
2507 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2508 while (IsUtf8Sequence > 0){
2511 if (--IsUtf8Sequence)
2519 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2521 return Target->BufUsed;
2525 * @ingroup StrBuf_DeEnCoder
2526 * @brief Append a string, escaping characters which have meaning in HTML + json.
2528 * @param Target target buffer
2529 * @param Source source buffer; set to NULL if you just have a C-String
2530 * @param PlainIn Plain-C string to append; set to NULL if unused
2531 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2532 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2533 * if set to 2, linebreaks are replaced by <br/>
2535 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2537 const char *aptr, *eiptr;
2540 int IsUtf8Sequence = 0;
2542 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2545 if (PlainIn != NULL) {
2547 len = strlen(PlainIn);
2552 eiptr = aptr + Source->BufUsed;
2553 len = Source->BufUsed;
2559 bptr = Target->buf + Target->BufUsed;
2560 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2562 while (aptr < eiptr){
2564 IncreaseBuf(Target, 1, -1);
2565 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2566 bptr = Target->buf + Target->BufUsed;
2570 memcpy(bptr, HKEY("<"));
2572 Target->BufUsed += 4;
2575 memcpy(bptr, HKEY(">"));
2577 Target->BufUsed += 4;
2580 memcpy(bptr, HKEY("&"));
2582 Target->BufUsed += 5;
2595 switch (nolinebreaks) {
2597 *bptr='\0'; /* nothing */
2600 memcpy(bptr, HKEY("<br/>"));
2602 Target->BufUsed += 11;
2605 memcpy(bptr, HKEY("\\n"));
2607 Target->BufUsed += 2;
2611 switch (nolinebreaks) {
2614 *bptr='\0'; /* nothing */
2617 memcpy(bptr, HKEY("\\r"));
2619 Target->BufUsed += 2;
2629 Target->BufUsed += 2;
2632 if ((*(aptr + 1) == 'u') &&
2633 isxdigit(*(aptr + 2)) &&
2634 isxdigit(*(aptr + 3)) &&
2635 isxdigit(*(aptr + 4)) &&
2636 isxdigit(*(aptr + 5)))
2637 { /* oh, a unicode escaper. let it pass through. */
2638 memcpy(bptr, aptr, 6);
2641 Target->BufUsed += 6;
2649 Target->BufUsed += 2;
2657 Target->BufUsed += 2;
2664 Target->BufUsed += 2;
2671 Target->BufUsed += 2;
2675 memcpy(bptr, HKEY(" "));
2677 Target->BufUsed += 6;
2681 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2682 while (IsUtf8Sequence > 0){
2685 if (--IsUtf8Sequence)
2693 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2695 return Target->BufUsed;
2700 * @ingroup StrBuf_DeEnCoder
2701 * @brief replace all non-Ascii characters by another
2702 * @param Buf buffer to inspect
2703 * @param repl charater to stamp over non ascii chars
2705 void StrBufAsciify(StrBuf *Buf, const char repl)
2709 for (offset = 0; offset < Buf->BufUsed; offset ++)
2710 if (!isascii(Buf->buf[offset]))
2711 Buf->buf[offset] = repl;
2716 * @ingroup StrBuf_DeEnCoder
2717 * @brief unhide special chars hidden to the HTML escaper
2718 * @param target buffer to put the unescaped string in
2719 * @param source buffer to unescape
2721 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2726 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2732 FlushStrBuf(target);
2734 len = source->BufUsed;
2735 for (a = 0; a < len; ++a) {
2736 if (target->BufUsed >= target->BufSize)
2737 IncreaseBuf(target, 1, -1);
2739 if (source->buf[a] == '=') {
2740 hex[0] = source->buf[a + 1];
2741 hex[1] = source->buf[a + 2];
2744 sscanf(hex, "%02x", &b);
2745 target->buf[target->BufUsed] = b;
2746 target->buf[++target->BufUsed] = 0;
2750 target->buf[target->BufUsed] = source->buf[a];
2751 target->buf[++target->BufUsed] = 0;
2758 * @ingroup StrBuf_DeEnCoder
2759 * @brief hide special chars from the HTML escapers and friends
2760 * @param target buffer to put the escaped string in
2761 * @param source buffer to escape
2763 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2768 FlushStrBuf(target);
2770 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2775 len = source->BufUsed;
2776 for (i=0; i<len; ++i) {
2777 if (target->BufUsed + 4 >= target->BufSize)
2778 IncreaseBuf(target, 1, -1);
2779 if ( (isalnum(source->buf[i])) ||
2780 (source->buf[i]=='-') ||
2781 (source->buf[i]=='_') ) {
2782 target->buf[target->BufUsed++] = source->buf[i];
2785 sprintf(&target->buf[target->BufUsed],
2787 (0xFF &source->buf[i]));
2788 target->BufUsed += 3;
2791 target->buf[target->BufUsed + 1] = '\0';
2795 /*******************************************************************************
2796 * Quoted Printable de/encoding *
2797 *******************************************************************************/
2800 * @ingroup StrBuf_DeEnCoder
2801 * @brief decode a buffer from base 64 encoding; destroys original
2802 * @param Buf Buffor to transform
2804 int StrBufDecodeBase64(StrBuf *Buf)
2812 xferbuf = (char*) malloc(Buf->BufSize);
2813 if (xferbuf == NULL)
2817 siz = CtdlDecodeBase64(xferbuf,
2827 * @ingroup StrBuf_DeEnCoder
2828 * @brief decode a buffer from base 64 encoding; expects targetbuffer
2829 * @param BufIn Buffor to transform
2830 * @param BufOut Buffer to put result into
2832 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2834 if ((BufIn == NULL) || (BufOut == NULL))
2837 if (BufOut->BufSize < BufIn->BufUsed)
2838 IncreaseBuf(BufOut, BufIn->BufUsed, 0);
2840 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2843 return BufOut->BufUsed;
2847 * @ingroup StrBuf_DeEnCoder
2848 * @brief decode a buffer from base 64 encoding; destroys original
2849 * @param Buf Buffor to transform
2851 int StrBufDecodeHex(StrBuf *Buf)
2854 char *pch, *pche, *pchi;
2856 if (Buf == NULL) return -1;
2858 pch = pchi = Buf->buf;
2859 pche = pch + Buf->BufUsed;
2861 while (pchi < pche){
2862 ch = decode_hex(pchi);
2869 Buf->BufUsed = pch - Buf->buf;
2870 return Buf->BufUsed;
2874 * @ingroup StrBuf_DeEnCoder
2875 * @brief replace all chars >0x20 && < 0x7F with Mute
2876 * @param Mute char to put over invalid chars
2877 * @param Buf Buffor to transform
2879 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2883 if (Buf == NULL) return -1;
2884 pch = (unsigned char *)Buf->buf;
2885 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2886 if ((*pch < 0x20) || (*pch > 0x7F))
2890 return Buf->BufUsed;
2895 * @ingroup StrBuf_DeEnCoder
2896 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2897 * @param Buf Buffer to translate
2898 * @param StripBlanks Reduce several blanks to one?
2900 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2909 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2910 Buf->buf[Buf->BufUsed - 1] = '\0';
2915 while (a < Buf->BufUsed) {
2916 if (Buf->buf[a] == '+')
2918 else if (Buf->buf[a] == '%') {
2919 /* don't let % chars through, rather truncate the input. */
2920 if (a + 2 > Buf->BufUsed) {
2925 hex[0] = Buf->buf[a + 1];
2926 hex[1] = Buf->buf[a + 2];
2929 sscanf(hex, "%02x", &b);
2930 Buf->buf[a] = (char) b;
2931 len = Buf->BufUsed - a - 2;
2933 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2945 * @ingroup StrBuf_DeEnCoder
2946 * @brief RFC2047-encode a header field if necessary.
2947 * If no non-ASCII characters are found, the string
2948 * will be copied verbatim without encoding.
2950 * @param target Target buffer.
2951 * @param source Source string to be encoded.
2952 * @returns encoded length; -1 if non success.
2954 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2956 const char headerStr[] = "=?UTF-8?Q?";
2957 int need_to_encode = 0;
2961 if ((source == NULL) ||
2965 while ((i < source->BufUsed) &&
2966 (!IsEmptyStr (&source->buf[i])) &&
2967 (need_to_encode == 0)) {
2968 if (((unsigned char) source->buf[i] < 32) ||
2969 ((unsigned char) source->buf[i] > 126)) {
2975 if (!need_to_encode) {
2976 if (*target == NULL) {
2977 *target = NewStrBufPlain(source->buf, source->BufUsed);
2980 FlushStrBuf(*target);
2981 StrBufAppendBuf(*target, source, 0);
2984 return (*target)->BufUsed;
2988 if (*target == NULL)
2989 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2990 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2991 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2992 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2993 (*target)->BufUsed = sizeof(headerStr) - 1;
2994 for (i=0; (i < source->BufUsed); ++i) {
2995 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2996 IncreaseBuf(*target, 1, 0);
2997 ch = (unsigned char) source->buf[i];
3006 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3007 (*target)->BufUsed += 3;
3011 (*target)->buf[(*target)->BufUsed] = '_';
3013 (*target)->buf[(*target)->BufUsed] = ch;
3014 (*target)->BufUsed++;
3018 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3019 IncreaseBuf(*target, 1, 0);
3021 (*target)->buf[(*target)->BufUsed++] = '?';
3022 (*target)->buf[(*target)->BufUsed++] = '=';
3023 (*target)->buf[(*target)->BufUsed] = '\0';
3024 return (*target)->BufUsed;;
3028 * @ingroup StrBuf_DeEnCoder
3029 * @brief Quoted-Printable encode a message; make it < 80 columns width.
3030 * @param source Source string to be encoded.
3031 * @returns buffer with encoded message.
3033 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3037 const char *ptr, *eptr;
3041 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3043 OEptr = OutBuf->buf + OutBuf->BufSize;
3044 ptr = EncodeMe->buf;
3045 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3050 if (Optr + 4 >= OEptr)
3053 Offset = Optr - OutBuf->buf;
3054 OutBuf->BufUsed = Optr - OutBuf->buf;
3055 IncreaseBuf(OutBuf, 1, 0);
3056 Optr = OutBuf->buf + Offset;
3057 OEptr = OutBuf->buf + OutBuf->BufSize;
3061 /* ignore carriage returns */
3064 else if (*ptr == '\n') {
3065 /* hard line break */
3066 memcpy(Optr, HKEY("=0A"));
3071 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3072 ( (*ptr >= 62) && (*ptr <= 126) ))
3083 *Optr = HexList[ch][0];
3085 *Optr = HexList[ch][1];
3092 /* soft line break */
3093 if (isspace(*(Optr - 1))) {
3098 *Optr = HexList[ch][0];
3100 *Optr = HexList[ch][1];
3112 OutBuf->BufUsed = Optr - OutBuf->buf;
3118 static void AddRecipient(StrBuf *Target,
3120 StrBuf *EmailAddress,
3125 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3126 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3128 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3129 StrBufRFC2047encode(&EncBuf, UserName);
3130 StrBufAppendBuf(Target, EncBuf, 0);
3131 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3132 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3134 if (StrLength(EmailAddress) > 0){
3135 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3136 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3137 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3143 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3144 * \param Recp Source list of email recipients
3145 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3146 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3147 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3148 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3150 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3152 StrBuf *EmailAddress,
3156 const char *pch, *pche;
3157 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3159 if ((Recp == NULL) || (StrLength(Recp) == 0))
3163 pche = pch + StrLength(Recp);
3165 if (!CheckEncode(pch, -1, pche))
3166 return NewStrBufDup(Recp);
3168 Target = NewStrBufPlain(NULL, StrLength(Recp));
3170 while ((pch != NULL) && (pch < pche))
3172 while (isspace(*pch)) pch++;
3173 UserEnd = EmailStart = EmailEnd = NULL;
3175 if ((*pch == '"') || (*pch == '\'')) {
3176 UserStart = pch + 1;
3178 UserEnd = strchr(UserStart, *pch);
3179 if (UserEnd == NULL)
3180 break; ///TODO: Userfeedback??
3181 EmailStart = UserEnd + 1;
3182 while (isspace(*EmailStart))
3184 if (UserEnd == UserStart) {
3185 UserStart = UserEnd = NULL;
3188 if (*EmailStart == '<') {
3190 EmailEnd = strchr(EmailStart, '>');
3191 if (EmailEnd == NULL)
3192 EmailEnd = strchr(EmailStart, ',');
3196 EmailEnd = strchr(EmailStart, ',');
3198 if (EmailEnd == NULL)
3205 EmailEnd = strchr(UserStart, ',');
3206 if (EmailEnd == NULL) {
3207 EmailEnd = strchr(pch, '>');
3209 if (EmailEnd != NULL) {
3219 while ((EmailEnd > UserStart) && !gt &&
3220 ((*EmailEnd == ',') ||
3221 (*EmailEnd == '>') ||
3222 (isspace(*EmailEnd))))
3224 if (*EmailEnd == '>')
3229 if (EmailEnd == UserStart)
3233 EmailStart = strchr(UserStart, '<');
3234 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3236 UserEnd = EmailStart;
3238 while ((UserEnd > UserStart) &&
3239 isspace (*(UserEnd - 1)))
3242 if (UserStart >= UserEnd)
3243 UserStart = UserEnd = NULL;
3245 else { /* this is a local recipient... no domain, just a realname */
3246 EmailStart = UserStart;
3247 At = strchr(EmailStart, '@');
3253 EmailStart = UserStart;
3259 if ((UserStart != NULL) && (UserEnd != NULL))
3260 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3261 else if ((UserStart != NULL) && (UserEnd == NULL))
3262 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3264 FlushStrBuf(UserName);
3266 if ((EmailStart != NULL) && (EmailEnd != NULL))
3267 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3268 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3269 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3271 FlushStrBuf(EmailAddress);
3273 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3278 if ((pch != NULL) && (*pch == ','))
3280 if (pch != NULL) while (isspace(*pch))
3289 * @brief replaces all occurances of 'search' by 'replace'
3290 * @param buf Buffer to modify
3291 * @param search character to search
3292 * @param replace character to replace search by
3294 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3299 for (i=0; i<buf->BufUsed; i++)
3300 if (buf->buf[i] == search)
3301 buf->buf[i] = replace;
3307 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3308 * @param buf Buffer to modify
3310 void StrBufToUnixLF(StrBuf *buf)
3312 char *pche, *pchS, *pchT;
3316 pche = buf->buf + buf->BufUsed;
3317 pchS = pchT = buf->buf;
3323 if (*pchS != '\n') {
3332 buf->BufUsed = pchT - buf->buf;
3336 /*******************************************************************************
3337 * Iconv Wrapper; RFC822 de/encoding *
3338 *******************************************************************************/
3341 * @ingroup StrBuf_DeEnCoder
3342 * @brief Wrapper around iconv_open()
3343 * Our version adds aliases for non-standard Microsoft charsets
3344 * such as 'MS950', aliasing them to names like 'CP950'
3346 * @param tocode Target encoding
3347 * @param fromcode Source encoding
3348 * @param pic anonimized pointer to iconv struct
3350 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3353 iconv_t ic = (iconv_t)(-1) ;
3354 ic = iconv_open(tocode, fromcode);
3355 if (ic == (iconv_t)(-1) ) {
3356 char alias_fromcode[64];
3357 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3358 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3359 alias_fromcode[0] = 'C';
3360 alias_fromcode[1] = 'P';
3361 ic = iconv_open(tocode, alias_fromcode);
3364 *(iconv_t *)pic = ic;
3370 * @ingroup StrBuf_DeEnCoder
3371 * @brief find one chunk of a RFC822 encoded string
3372 * @param Buffer where to search
3373 * @param bptr where to start searching
3374 * @returns found position, NULL if none.
3376 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3379 /* Find the next ?Q? */
3380 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3383 end = strchr(bptr + 2, '?');
3388 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3389 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3390 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3391 (*(end + 2) == '?')) {
3392 /* skip on to the end of the cluster, the next ?= */
3393 end = strstr(end + 3, "?=");
3396 /* sort of half valid encoding, try to find an end. */
3397 end = strstr(bptr, "?=");
3404 * @ingroup StrBuf_DeEnCoder
3405 * @brief convert one buffer according to the preselected iconv pointer PIC
3406 * @param ConvertBuf buffer we need to translate
3407 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3408 * @param pic Pointer to the iconv-session Object
3410 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3416 char *ibuf; /**< Buffer of characters to be converted */
3417 char *obuf; /**< Buffer for converted characters */
3418 size_t ibuflen; /**< Length of input buffer */
3419 size_t obuflen; /**< Length of output buffer */
3422 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3425 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3426 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3427 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3429 ic = *(iconv_t*)pic;
3430 ibuf = ConvertBuf->buf;
3431 ibuflen = ConvertBuf->BufUsed;
3433 obuflen = TmpBuf->BufSize;
3435 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3438 if (errno == E2BIG) {
3440 IncreaseBuf(TmpBuf, 0, 0);
3445 else if (errno == EILSEQ){
3446 /* hm, invalid utf8 sequence... what to do now? */
3447 /* An invalid multibyte sequence has been encountered in the input */
3449 else if (errno == EINVAL) {
3450 /* An incomplete multibyte sequence has been encountered in the input. */
3453 FlushStrBuf(TmpBuf);
3456 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3457 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3459 /* little card game: wheres the red lady? */
3460 SwapBuffers(ConvertBuf, TmpBuf);
3461 FlushStrBuf(TmpBuf);
3468 * @ingroup StrBuf_DeEnCoder
3469 * @brief catches one RFC822 encoded segment, and decodes it.
3470 * @param Target buffer to fill with result
3471 * @param DecodeMe buffer with stuff to process
3472 * @param SegmentStart points to our current segment in DecodeMe
3473 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3474 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3475 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3476 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3478 inline static void DecodeSegment(StrBuf *Target,
3479 const StrBuf *DecodeMe,
3480 const char *SegmentStart,
3481 const char *SegmentEnd,
3483 StrBuf *ConvertBuf2,
3484 StrBuf *FoundCharset)
3490 iconv_t ic = (iconv_t)(-1);
3494 /* Now we handle foreign character sets properly encoded
3495 * in RFC2047 format.
3497 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3498 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3499 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3500 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3501 if (FoundCharset != NULL) {
3502 FlushStrBuf(FoundCharset);
3503 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3505 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3506 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3508 *encoding = toupper(*encoding);
3509 if (*encoding == 'B') { /**< base64 */
3510 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3511 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3512 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3514 ConvertBuf->BufUsed);
3516 else if (*encoding == 'Q') { /**< quoted-printable */
3520 while (pos < ConvertBuf->BufUsed)
3522 if (ConvertBuf->buf[pos] == '_')
3523 ConvertBuf->buf[pos] = ' ';
3527 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3528 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3530 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3533 ConvertBuf->BufUsed);
3536 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3539 ctdl_iconv_open("UTF-8", charset, &ic);
3540 if (ic != (iconv_t)(-1) ) {
3542 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3543 StrBufAppendBuf(Target, ConvertBuf2, 0);
3548 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3554 * @ingroup StrBuf_DeEnCoder
3555 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3556 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3557 * @param Target where to put the decoded string to
3558 * @param DecodeMe buffer with encoded string
3559 * @param DefaultCharset if we don't find one, which should we use?
3560 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3561 * put it here for later use where no string might be known.
3563 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3566 StrBuf *ConvertBuf2;
3567 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3568 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3570 StrBuf_RFC822_2_Utf8(Target,
3576 FreeStrBuf(&ConvertBuf);
3577 FreeStrBuf(&ConvertBuf2);
3581 * @ingroup StrBuf_DeEnCoder
3582 * @brief Handle subjects with RFC2047 encoding such as:
3583 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3584 * @param Target where to put the decoded string to
3585 * @param DecodeMe buffer with encoded string
3586 * @param DefaultCharset if we don't find one, which should we use?
3587 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3588 * put it here for later use where no string might be known.
3589 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3590 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3592 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3593 const StrBuf *DecodeMe,
3594 const StrBuf* DefaultCharset,
3595 StrBuf *FoundCharset,
3597 StrBuf *ConvertBuf2)
3599 StrBuf *DecodedInvalidBuf = NULL;
3600 const StrBuf *DecodeMee = DecodeMe;
3601 const char *start, *end, *next, *nextend, *ptr = NULL;
3603 iconv_t ic = (iconv_t)(-1) ;
3608 int illegal_non_rfc2047_encoding = 0;
3611 if (DecodeMe == NULL)
3613 /* Sometimes, badly formed messages contain strings which were simply
3614 * written out directly in some foreign character set instead of
3615 * using RFC2047 encoding. This is illegal but we will attempt to
3616 * handle it anyway by converting from a user-specified default
3617 * charset to UTF-8 if we see any nonprintable characters.
3620 for (i=0; i<DecodeMe->BufUsed; ++i) {
3621 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3622 illegal_non_rfc2047_encoding = 1;
3627 if ((illegal_non_rfc2047_encoding) &&
3628 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3629 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3632 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3633 if (ic != (iconv_t)(-1) ) {
3634 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3635 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3636 DecodeMee = DecodedInvalidBuf;
3642 /* pre evaluate the first pair */
3644 start = strstr(DecodeMee->buf, "=?");
3645 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3647 end = FindNextEnd (DecodeMee, start + 2);
3649 StrBufAppendBuf(Target, DecodeMee, 0);
3650 FreeStrBuf(&DecodedInvalidBuf);
3655 if (start != DecodeMee->buf) {
3658 nFront = start - DecodeMee->buf;
3659 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3662 * Since spammers will go to all sorts of absurd lengths to get their
3663 * messages through, there are LOTS of corrupt headers out there.
3664 * So, prevent a really badly formed RFC2047 header from throwing
3665 * this function into an infinite loop.
3667 while ((start != NULL) &&
3674 DecodeSegment(Target,
3682 next = strstr(end, "=?");
3684 if ((next != NULL) &&
3686 nextend = FindNextEnd(DecodeMee, next);
3687 if (nextend == NULL)
3690 /* did we find two partitions */
3691 if ((next != NULL) &&
3695 while ((ptr < next) &&
3702 * did we find a gab just filled with blanks?
3703 * if not, copy its stuff over.
3707 StrBufAppendBufPlain(Target,
3713 /* our next-pair is our new first pair now. */
3719 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3720 if ((end != NULL) && (end < nextend)) {
3722 while ( (ptr < nextend) &&
3729 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3731 FreeStrBuf(&DecodedInvalidBuf);
3734 /*******************************************************************************
3735 * Manipulating UTF-8 Strings *
3736 *******************************************************************************/
3740 * @brief evaluate the length of an utf8 special character sequence
3741 * @param Char the character to examine
3742 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3744 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3747 unsigned char test = (1<<7);
3749 if ((*CharS & 0xC0) != 0xC0)
3753 ((test & ((unsigned char)*CharS)) != 0))
3758 if ((n > 6) || ((CharE - CharS) < n))
3765 * @brief detect whether this char starts an utf-8 encoded char
3766 * @param Char character to inspect
3767 * @returns yes or no
3769 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3771 /** 11??.???? indicates an UTF8 Sequence. */
3772 return ((Char & 0xC0) == 0xC0);
3777 * @brief measure the number of glyphs in an UTF8 string...
3778 * @param Buf string to measure
3779 * @returns the number of glyphs in Buf
3781 long StrBuf_Utf8StrLen(StrBuf *Buf)
3787 if ((Buf == NULL) || (Buf->BufUsed == 0))
3790 eptr = Buf->buf + Buf->BufUsed;
3791 while ((aptr < eptr) && (*aptr != '\0')) {
3792 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3793 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3794 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3807 * @brief cuts a string after maxlen glyphs
3808 * @param Buf string to cut to maxlen glyphs
3809 * @param maxlen how long may the string become?
3810 * @returns current length of the string
3812 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3818 eptr = Buf->buf + Buf->BufUsed;
3819 while ((aptr < eptr) && (*aptr != '\0')) {
3820 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3821 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3822 while ((*aptr++ != '\0') && (m-- > 0));
3831 Buf->BufUsed = aptr - Buf->buf;
3832 return Buf->BufUsed;
3835 return Buf->BufUsed;
3843 /*******************************************************************************
3845 *******************************************************************************/
3848 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3849 #define OS_CODE 0x03 /*< unix */
3852 * @ingroup StrBuf_DeEnCoder
3853 * @brief uses the same calling syntax as compress2(), but it
3854 * creates a stream compatible with HTTP "Content-encoding: gzip"
3855 * @param dest compressed buffer
3856 * @param destLen length of the compresed data
3857 * @param source source to encode
3858 * @param sourceLen length of source to encode
3859 * @param level compression level
3861 int ZEXPORT compress_gzip(Bytef * dest,
3863 const Bytef * source,
3867 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3869 /* write gzip header */
3870 snprintf((char *) dest, *destLen,
3871 "%c%c%c%c%c%c%c%c%c%c",
3872 gz_magic[0], gz_magic[1], Z_DEFLATED,
3873 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3876 /* normal deflate */
3879 stream.next_in = (Bytef *) source;
3880 stream.avail_in = (uInt) sourceLen;
3881 stream.next_out = dest + 10L; // after header
3882 stream.avail_out = (uInt) * destLen;
3883 if ((uLong) stream.avail_out != *destLen)
3886 stream.zalloc = (alloc_func) 0;
3887 stream.zfree = (free_func) 0;
3888 stream.opaque = (voidpf) 0;
3890 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3891 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3895 err = deflate(&stream, Z_FINISH);
3896 if (err != Z_STREAM_END) {
3897 deflateEnd(&stream);
3898 return err == Z_OK ? Z_BUF_ERROR : err;
3900 *destLen = stream.total_out + 10L;
3902 /* write CRC and Length */
3903 uLong crc = crc32(0L, source, sourceLen);
3905 for (n = 0; n < 4; ++n, ++*destLen) {
3906 dest[*destLen] = (int) (crc & 0xff);
3909 uLong len = stream.total_in;
3910 for (n = 0; n < 4; ++n, ++*destLen) {
3911 dest[*destLen] = (int) (len & 0xff);
3914 err = deflateEnd(&stream);
3921 * @ingroup StrBuf_DeEnCoder
3922 * @brief compress the buffer with gzip
3923 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3924 * @param Buf buffer whose content is to be gzipped
3926 int CompressBuffer(StrBuf *Buf)
3929 char *compressed_data = NULL;
3930 size_t compressed_len, bufsize;
3933 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3934 compressed_data = malloc(compressed_len);
3936 if (compressed_data == NULL)
3938 /* Flush some space after the used payload so valgrind shuts up... */
3939 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3940 Buf->buf[Buf->BufUsed + i++] = '\0';
3941 if (compress_gzip((Bytef *) compressed_data,
3944 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3947 Buf->buf = compressed_data;
3948 Buf->BufUsed = compressed_len;
3949 Buf->BufSize = bufsize;
3950 /* Flush some space after the used payload so valgrind shuts up... */
3952 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3953 Buf->buf[Buf->BufUsed + i++] = '\0';
3956 free(compressed_data);
3958 #endif /* HAVE_ZLIB */
3962 /*******************************************************************************
3963 * File I/O; Callbacks to libevent *
3964 *******************************************************************************/
3966 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3971 if ((FB == NULL) || (FB->Buf == NULL))
3975 * check whether the read pointer is somewhere in a range
3976 * where a cut left is inexpensive
3979 if (FB->ReadWritePointer != NULL)
3983 already_read = FB->ReadWritePointer - FB->Buf->buf;
3984 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3986 if (already_read != 0) {
3989 unread = FB->Buf->BufUsed - already_read;
3991 /* else nothing to compact... */
3993 FB->ReadWritePointer = FB->Buf->buf;
3994 bufremain = FB->Buf->BufSize;
3996 else if ((unread < 64) ||
3997 (bufremain < already_read))
4000 * if its just a tiny bit remaining, or we run out of space...
4003 FB->Buf->BufUsed = unread;
4004 if (unread < already_read)
4005 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4007 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4008 FB->ReadWritePointer = FB->Buf->buf;
4009 bufremain = FB->Buf->BufSize - unread - 1;
4011 else if (bufremain < (FB->Buf->BufSize / 10))
4013 /* get a bigger buffer */
4015 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4017 FB->ReadWritePointer = FB->Buf->buf + unread;
4019 bufremain = FB->Buf->BufSize - unread - 1;
4020 /*TODO: special increase function that won't copy the already read! */
4023 else if (bufremain < 10) {
4024 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4026 FB->ReadWritePointer = FB->Buf->buf;
4028 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4033 FB->ReadWritePointer = FB->Buf->buf;
4034 bufremain = FB->Buf->BufSize - 1;
4037 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4040 FB->Buf->BufUsed += n;
4041 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4046 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4051 if ((FB == NULL) || (FB->Buf == NULL))
4054 if (FB->ReadWritePointer != NULL)
4056 WriteRemain = FB->Buf->BufUsed -
4057 (FB->ReadWritePointer -
4061 FB->ReadWritePointer = FB->Buf->buf;
4062 WriteRemain = FB->Buf->BufUsed;
4065 n = write(fd, FB->ReadWritePointer, WriteRemain);
4067 FB->ReadWritePointer += n;
4069 if (FB->ReadWritePointer ==
4070 FB->Buf->buf + FB->Buf->BufUsed)
4072 FlushStrBuf(FB->Buf);
4073 FB->ReadWritePointer = NULL;
4076 // check whether we've got something to write
4077 // get the maximum chunk plus the pointer we can send
4078 // write whats there
4079 // if not all was sent, remember the send pointer for the next time
4080 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4086 * @ingroup StrBuf_IO
4087 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4088 * @param LineBuf your line will be copied here.
4089 * @param FB BLOB with lines of text...
4090 * @param Ptr moved arround to keep the next-line across several iterations
4091 * has to be &NULL on start; will be &NotNULL on end of buffer
4092 * @returns size of copied buffer
4094 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4096 const char *aptr, *ptr, *eptr;
4099 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4103 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4104 FB->ReadWritePointer = StrBufNOTNULL;
4108 FlushStrBuf(LineBuf);
4109 if (FB->ReadWritePointer == NULL)
4110 ptr = aptr = FB->Buf->buf;
4112 ptr = aptr = FB->ReadWritePointer;
4114 optr = LineBuf->buf;
4115 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4116 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4118 while ((ptr <= eptr) &&
4125 LineBuf->BufUsed = optr - LineBuf->buf;
4126 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4127 optr = LineBuf->buf + LineBuf->BufUsed;
4128 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4133 if (optr > LineBuf->buf)
4135 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4136 LineBuf->BufUsed = optr - LineBuf->buf;
4138 if ((FB->ReadWritePointer != NULL) &&
4139 (FB->ReadWritePointer != FB->Buf->buf))
4141 /* Ok, the client application read all the data
4142 it was interested in so far. Since there is more to read,
4143 we now shrink the buffer, and move the rest over.
4145 StrBufCutLeft(FB->Buf,
4146 FB->ReadWritePointer - FB->Buf->buf);
4147 FB->ReadWritePointer = FB->Buf->buf;
4149 return eMustReadMore;
4152 LineBuf->BufUsed = optr - LineBuf->buf;
4154 if ((ptr <= eptr) && (*ptr == '\r'))
4156 if ((ptr <= eptr) && (*ptr == '\n'))
4160 FB->ReadWritePointer = ptr;
4163 FlushStrBuf(FB->Buf);
4164 FB->ReadWritePointer = NULL;
4167 return eReadSuccess;
4171 * @ingroup StrBuf_CHUNKED_IO
4172 * @brief check whether the chunk-buffer has more data waiting or not.
4173 * @param FB Chunk-Buffer to inspect
4175 eReadState StrBufCheckBuffer(IOBuffer *FB)
4179 if (FB->Buf->BufUsed == 0)
4180 return eReadSuccess;
4181 if (FB->ReadWritePointer == NULL)
4182 return eBufferNotEmpty;
4183 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4184 return eBufferNotEmpty;
4185 return eReadSuccess;
4188 long IOBufferStrLength(IOBuffer *FB)
4190 if ((FB == NULL) || (FB->Buf == NULL))
4192 if (FB->ReadWritePointer == NULL)
4193 return StrLength(FB->Buf);
4195 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4198 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
4200 memset(FDB, 0, sizeof(FDIOBuffer));
4202 FDB->SplicePipe[0] = -1;
4203 FDB->SplicePipe[1] = -1;
4206 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
4208 FDIOBufferFlush(FDB);
4210 FDB->TotalSendSize = TotalSendSize;
4211 if (TotalSendSize > 0)
4212 FDB->ChunkSize = TotalSendSize;
4215 TotalSendSize = SIZ * 10;
4216 FDB->ChunkSize = TotalSendSize;
4222 pipe(FDB->SplicePipe);
4225 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
4230 void FDIOBufferDelete(FDIOBuffer *FDB)
4235 if (FDB->SplicePipe[0] > 0)
4236 close(FDB->SplicePipe[0]);
4237 if (FDB->SplicePipe[1] > 0)
4238 close(FDB->SplicePipe[1]);
4242 FreeStrBuf(&FDB->ChunkBuffer);
4244 if (FDB->OtherFD > 0)
4245 close(FDB->OtherFD);
4246 FDIOBufferFlush(FDB);
4249 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4251 ssize_t sent, pipesize;
4253 if (FDB->TotalSendSize > 0)
4258 if (FDB->PipeSize == 0)
4260 pipesize = splice(FDB->OtherFD,
4261 &FDB->TotalSentAlready,
4264 FDB->ChunkSendRemain,
4269 *Err = strerror(errno);
4272 FDB->PipeSize = pipesize;
4274 sent = splice(FDB->SplicePipe[0],
4279 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4282 *Err = strerror(errno);
4285 FDB->PipeSize -= sent;
4286 FDB->ChunkSendRemain -= sent;
4295 pRead = FDB->ChunkBuffer->buf;
4296 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4298 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4300 FDB->ChunkBuffer->BufUsed += nRead;
4301 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4303 else if (nRead == 0) {}
4307 nRead = write(FDB->IOB->fd,
4308 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4309 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4312 FDB->TotalSentAlready += nRead;
4313 FDB->ChunkSendRemain -= nRead;
4314 return FDB->ChunkSendRemain;
4326 if (FDB->PipeSize == 0)
4328 pipesize = splice(FDB->OtherFD,
4329 &FDB->TotalSentAlready,
4337 *Err = strerror(errno);
4340 FDB->PipeSize = pipesize;
4344 sent = splice(FDB->SplicePipe[0],
4349 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4352 *Err = strerror(errno);
4355 FDB->PipeSize -= sent;
4356 FDB->ChunkSendRemain -= sent;
4365 pRead = FDB->ChunkBuffer->buf;
4366 while ((FDB->ChunkSendRemain == 0) &&
4367 (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
4370 FDB->TotalSentAlready = 0;
4371 nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
4373 FDB->ChunkBuffer->BufUsed += nRead;
4374 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4375 FDB->ChunkSendRemain += nRead;
4377 else if (nRead == 0)
4383 *Err = strerror(errno);
4388 nRead = write(FDB->IOB->fd,
4389 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4390 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4393 FDB->TotalSentAlready += nRead;
4394 FDB->ChunkSendRemain -= nRead;
4395 if (FDB->ChunkSendRemain == 0)
4397 FDB->ChunkBuffer->BufUsed = 0;
4398 FDB->TotalSentAlready = 0;
4400 return FDB->ChunkSendRemain;
4409 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4411 ssize_t sent, pipesize;
4416 if (FDB->PipeSize == 0)
4418 pipesize = splice(FDB->IOB->fd,
4422 FDB->ChunkSendRemain,
4423 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4427 *Err = strerror(errno);
4430 FDB->PipeSize = pipesize;
4433 sent = splice(FDB->SplicePipe[0],
4436 &FDB->TotalSentAlready,
4438 SPLICE_F_MORE | SPLICE_F_MOVE);
4442 *Err = strerror(errno);
4445 FDB->PipeSize -= sent;
4446 FDB->ChunkSendRemain -= sent;
4452 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4457 FDB->ChunkBuffer->BufUsed = sent;
4459 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4460 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4462 *Err = strerror(errno);
4468 FDB->ChunkBuffer->BufUsed = 0;
4469 FDB->TotalSentAlready += sent;
4470 FDB->ChunkSendRemain -= sent;
4471 return FDB->ChunkSendRemain;
4473 else if (sent < 0) {
4474 *Err = strerror(errno);
4481 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4483 ssize_t sent, pipesize;
4488 if (FDB->PipeSize == 0)
4490 pipesize = splice(FDB->IOB->fd,
4491 &FDB->TotalReadAlready,
4494 FDB->ChunkSendRemain,
4495 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4499 *Err = strerror(errno);
4502 FDB->PipeSize = pipesize;
4505 sent = splice(FDB->SplicePipe[0],
4508 &FDB->TotalSentAlready,
4510 SPLICE_F_MORE | SPLICE_F_MOVE);
4514 *Err = strerror(errno);
4517 FDB->PipeSize -= sent;
4518 FDB->ChunkSendRemain -= sent;
4524 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4529 FDB->ChunkBuffer->BufUsed = sent;
4531 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4532 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4534 *Err = strerror(errno);
4540 FDB->ChunkBuffer->BufUsed = 0;
4541 FDB->TotalSentAlready += sent;
4542 FDB->ChunkSendRemain -= sent;
4543 return FDB->ChunkSendRemain;
4545 else if (sent < 0) {
4546 *Err = strerror(errno);
4553 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4559 int nSuccessLess = 0;
4563 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4564 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4566 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4567 (FDB->ChunkSendRemain > 0))
4570 tv.tv_sec = 1; /* selectresolution; */
4574 FD_SET(FDB->OtherFD, &rfds);
4575 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4576 *Error = strerror(errno);
4580 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4585 should_write = FDB->IOB->Buf->BufUsed -
4586 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4587 if (should_write > FDB->ChunkSendRemain)
4588 should_write = FDB->ChunkSendRemain;
4590 rlen = write(FDB->OtherFD,
4591 FDB->IOB->ReadWritePointer,
4594 *Error = strerror(errno);
4598 FDB->TotalSentAlready += rlen;
4599 FDB->IOB->ReadWritePointer += rlen;
4600 FDB->ChunkSendRemain -= rlen;
4602 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4604 FlushStrBuf(FDB->IOB->Buf);
4605 FDB->IOB->ReadWritePointer = NULL;
4608 if (FDB->ChunkSendRemain == 0)
4609 return eReadSuccess;
4611 return eMustReadMore;
4614 /*******************************************************************************
4615 * File I/O; Prefer buffered read since its faster! *
4616 *******************************************************************************/
4619 * @ingroup StrBuf_IO
4620 * @brief Read a line from socket
4621 * flushes and closes the FD on error
4622 * @param buf the buffer to get the input to
4623 * @param fd pointer to the filedescriptor to read
4624 * @param append Append to an existing string or replace?
4625 * @param Error strerror() on error
4626 * @returns numbers of chars read
4628 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4630 int len, rlen, slen;
4632 if ((buf == NULL) || (buf->buf == NULL)) {
4633 *Error = strerror(EINVAL);
4640 slen = len = buf->BufUsed;
4642 rlen = read(*fd, &buf->buf[len], 1);
4644 *Error = strerror(errno);
4651 if (buf->buf[len] == '\n')
4653 if (buf->buf[len] != '\r')
4655 if (len + 2 >= buf->BufSize) {
4657 buf->buf[len+1] = '\0';
4658 IncreaseBuf(buf, 1, -1);
4662 buf->buf[len] = '\0';
4667 * @ingroup StrBuf_BufferedIO
4668 * @brief Read a line from socket
4669 * flushes and closes the FD on error
4670 * @param Line the line to read from the fd / I/O Buffer
4671 * @param buf the buffer to get the input to
4672 * @param fd pointer to the filedescriptor to read
4673 * @param timeout number of successless selects until we bail out
4674 * @param selectresolution how long to wait on each select
4675 * @param Error strerror() on error
4676 * @returns numbers of chars read
4678 int StrBufTCP_read_buffered_line(StrBuf *Line,
4682 int selectresolution,
4686 int nSuccessLess = 0;
4693 if (buf->BufUsed > 0) {
4694 pch = strchr(buf->buf, '\n');
4697 len = pch - buf->buf;
4698 if (len > 0 && (*(pch - 1) == '\r') )
4700 StrBufSub(Line, buf, 0, len - rlen);
4701 StrBufCutLeft(buf, len + 1);
4706 if (buf->BufSize - buf->BufUsed < 10)
4707 IncreaseBuf(buf, 1, -1);
4709 fdflags = fcntl(*fd, F_GETFL);
4710 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4712 while ((nSuccessLess < timeout) && (pch == NULL)) {
4714 tv.tv_sec = selectresolution;
4719 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4720 *Error = strerror(errno);
4726 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4731 &buf->buf[buf->BufUsed],
4732 buf->BufSize - buf->BufUsed - 1);
4734 *Error = strerror(errno);
4739 else if (rlen > 0) {
4741 buf->BufUsed += rlen;
4742 buf->buf[buf->BufUsed] = '\0';
4743 pch = strchr(buf->buf, '\n');
4744 if ((pch == NULL) &&
4745 (buf->BufUsed + 10 > buf->BufSize) &&
4746 (IncreaseBuf(buf, 1, -1) == -1))
4754 len = pch - buf->buf;
4755 if (len > 0 && (*(pch - 1) == '\r') )
4757 StrBufSub(Line, buf, 0, len - rlen);
4758 StrBufCutLeft(buf, len + 1);
4765 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4766 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4767 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4769 * @ingroup StrBuf_BufferedIO
4770 * @brief Read a line from socket
4771 * flushes and closes the FD on error
4772 * @param Line where to append our Line read from the fd / I/O Buffer;
4773 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4774 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4775 * @param fd pointer to the filedescriptor to read
4776 * @param timeout number of successless selects until we bail out
4777 * @param selectresolution how long to wait on each select
4778 * @param Error strerror() on error
4779 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4781 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4786 int selectresolution,
4789 const char *pche = NULL;
4790 const char *pos = NULL;
4792 int len, rlen, retlen;
4793 int nSuccessLess = 0;
4795 const char *pch = NULL;
4801 if ((Line == NULL) ||
4808 *Error = ErrRBLF_PreConditionFailed;
4813 if ((IOBuf->BufUsed > 0) &&
4815 (pos < IOBuf->buf + IOBuf->BufUsed))
4819 pche = IOBuf->buf + IOBuf->BufUsed;
4823 while ((pch < pche) && (*pch != '\n'))
4825 if (Line->BufUsed + 10 > Line->BufSize)
4828 apos = pcht - Line->buf;
4830 IncreaseBuf(Line, 1, -1);
4831 pcht = Line->buf + apos;
4839 if (len > 0 && (*(pch - 1) == '\r') )
4848 if ((pch >= pche) || (*pch == '\0'))
4856 if ((pch != NULL) &&
4859 if (pch + 1 >= pche) {
4872 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4874 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4875 IncreaseBuf(IOBuf, 1, -1);
4877 fdflags = fcntl(*fd, F_GETFL);
4878 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4881 while ((nSuccessLess < timeout) &&
4891 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4892 *Error = strerror(errno);
4896 *Error = ErrRBLF_SelectFailed;
4899 if (! FD_ISSET(*fd, &rfds) != 0) {
4905 &IOBuf->buf[IOBuf->BufUsed],
4906 IOBuf->BufSize - IOBuf->BufUsed - 1);
4908 *Error = strerror(errno);
4913 else if (rlen > 0) {
4915 pLF = IOBuf->buf + IOBuf->BufUsed;
4916 IOBuf->BufUsed += rlen;
4917 IOBuf->buf[IOBuf->BufUsed] = '\0';
4919 pche = IOBuf->buf + IOBuf->BufUsed;
4921 while ((pLF < pche) && (*pLF != '\n'))
4923 if ((pLF >= pche) || (*pLF == '\0'))
4926 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4930 if (pLF != NULL) apos = pLF - IOBuf->buf;
4931 IncreaseBuf(IOBuf, 1, -1);
4932 if (pLF != NULL) pLF = IOBuf->buf + apos;
4946 if (len > 0 && (*(pLF - 1) == '\r') )
4948 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4949 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4955 return retlen + len;
4957 *Error = ErrRBLF_NotEnoughSentFromServer;
4962 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4964 * @ingroup StrBuf_IO
4965 * @brief Input binary data from socket
4966 * flushes and closes the FD on error
4967 * @param Buf the buffer to get the input to
4968 * @param fd pointer to the filedescriptor to read
4969 * @param append Append to an existing string or replace?
4970 * @param nBytes the maximal number of bytes to read
4971 * @param Error strerror() on error
4972 * @returns numbers of chars read
4974 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4985 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4987 *Error = ErrRBLF_BLOBPreConditionFailed;
4992 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4993 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4995 ptr = Buf->buf + Buf->BufUsed;
4997 fdflags = fcntl(*fd, F_GETFL);
4998 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5000 while ((nRead < nBytes) &&
5010 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5011 *Error = strerror(errno);
5015 *Error = ErrRBLF_SelectFailed;
5018 if (! FD_ISSET(*fd, &rfds) != 0) {
5024 if ((rlen = read(*fd,
5026 nBytes - nRead)) == -1) {
5029 *Error = strerror(errno);
5034 Buf->BufUsed += rlen;
5036 Buf->buf[Buf->BufUsed] = '\0';
5040 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
5041 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
5043 * @ingroup StrBuf_BufferedIO
5044 * @brief Input binary data from socket
5045 * flushes and closes the FD on error
5046 * @param Blob put binary thing here
5047 * @param IOBuf the buffer to get the input to
5048 * @param Pos offset inside of IOBuf
5049 * @param fd pointer to the filedescriptor to read
5050 * @param append Append to an existing string or replace?
5051 * @param nBytes the maximal number of bytes to read
5052 * @param check whether we should search for '000\n' terminators in case of timeouts
5053 * @param Error strerror() on error
5054 * @returns numbers of chars read
5056 int StrBufReadBLOBBuffered(StrBuf *Blob,
5069 int nAlreadyRead = 0;
5074 int nSuccessLess = 0;
5077 if ((Blob == NULL) ||
5084 *Error = ErrRBB_BLOBFPreConditionFailed;
5090 if (Blob->BufUsed + nBytes >= Blob->BufSize)
5091 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
5096 rlen = pos - IOBuf->buf;
5097 rlen = IOBuf->BufUsed - rlen;
5100 if ((IOBuf->BufUsed > 0) &&
5102 (pos < IOBuf->buf + IOBuf->BufUsed))
5104 if (rlen < nBytes) {
5105 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
5106 Blob->BufUsed += rlen;
5107 Blob->buf[Blob->BufUsed] = '\0';
5108 nAlreadyRead = nRead = rlen;
5111 if (rlen >= nBytes) {
5112 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
5113 Blob->BufUsed += nBytes;
5114 Blob->buf[Blob->BufUsed] = '\0';
5115 if (rlen == nBytes) {
5127 if (IOBuf->BufSize < nBytes - nRead)
5128 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5131 fdflags = fcntl(*fd, F_GETFL);
5132 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5140 while ((nSuccessLess < MaxTries) &&
5150 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5151 *Error = strerror(errno);
5155 *Error = ErrRBLF_SelectFailed;
5158 if (! FD_ISSET(*fd, &rfds) != 0) {
5165 IOBuf->BufSize - (ptr - IOBuf->buf));
5169 *Error = strerror(errno);
5172 else if (rlen == 0){
5173 if ((check == NNN_TERM) &&
5175 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
5177 StrBufPlain(Blob, HKEY("\n000\n"));
5178 StrBufCutRight(Blob, 5);
5179 return Blob->BufUsed;
5181 else if (!IsNonBlock)
5183 else if (nSuccessLess > MaxTries) {
5185 *Error = ErrRBB_too_many_selects;
5189 else if (rlen > 0) {
5193 IOBuf->BufUsed += rlen;
5196 if (nSuccessLess >= MaxTries) {
5198 *Error = ErrRBB_too_many_selects;
5202 if (nRead > nBytes) {
5203 *Pos = IOBuf->buf + nBytes;
5205 Blob->buf[Blob->BufUsed] = '\0';
5206 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5210 return nRead + nAlreadyRead;
5214 * @ingroup StrBuf_IO
5215 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5216 * @param LineBuf your line will be copied here.
5217 * @param Buf BLOB with lines of text...
5218 * @param Ptr moved arround to keep the next-line across several iterations
5219 * has to be &NULL on start; will be &NotNULL on end of buffer
5220 * @returns size of remaining buffer
5222 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5224 const char *aptr, *ptr, *eptr;
5227 if ((Buf == NULL) ||
5228 (*Ptr == StrBufNOTNULL) ||
5230 (LineBuf->buf == NULL))
5232 *Ptr = StrBufNOTNULL;
5236 FlushStrBuf(LineBuf);
5238 ptr = aptr = Buf->buf;
5242 optr = LineBuf->buf;
5243 eptr = Buf->buf + Buf->BufUsed;
5244 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5246 while ((ptr <= eptr) &&
5253 LineBuf->BufUsed = optr - LineBuf->buf;
5254 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
5255 optr = LineBuf->buf + LineBuf->BufUsed;
5256 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5260 if ((ptr >= eptr) && (optr > LineBuf->buf))
5262 LineBuf->BufUsed = optr - LineBuf->buf;
5264 if ((ptr <= eptr) && (*ptr == '\r'))
5266 if ((ptr <= eptr) && (*ptr == '\n'))
5273 *Ptr = StrBufNOTNULL;
5276 return Buf->BufUsed - (ptr - Buf->buf);
5281 * @ingroup StrBuf_IO
5282 * @brief removes double slashes from pathnames
5283 * @param Dir directory string to filter
5284 * @param RemoveTrailingSlash allows / disallows trailing slashes
5286 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5292 while (!IsEmptyStr(a)) {
5304 if ((RemoveTrailingSlash) &&
5310 Dir->BufUsed = b - Dir->buf;