8 #include <sys/select.h>
10 #include <sys/types.h>
11 #define SHOW_ME_VAPPEND_PRINTF
13 #include "libcitadel.h"
25 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
26 const Bytef * source, uLong sourceLen, int level);
28 int BaseStrBufSize = 64;
30 const char *StrBufNOTNULL = ((char*) NULL) - 1;
33 * Private Structure for the Stringbuffer
36 char *buf; /**< the pointer to the dynamic buffer */
37 long BufSize; /**< how many spcae do we optain */
38 long BufUsed; /**< StNumber of Chars used excluding the trailing \0 */
39 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
43 char bt_lastinc [SIZ];
48 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
49 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
53 static void StrBufBacktrace(StrBuf *Buf, int which)
57 void *stack_frames[50];
62 pstart = pch = Buf->bt;
64 pstart = pch = Buf->bt_lastinc;
65 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
66 strings = backtrace_symbols(stack_frames, size);
67 for (i = 0; i < size; i++) {
69 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
71 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
82 * @brief Cast operator to Plain String
83 * Note: if the buffer is altered by StrBuf operations, this pointer may become
84 * invalid. So don't lean on it after altering the buffer!
85 * Since this operation is considered cheap, rather call it often than risking
86 * your pointer to become invalid!
87 * @param Str the string we want to get the c-string representation for
88 * @returns the Pointer to the Content. Don't mess with it!
90 inline const char *ChrPtr(const StrBuf *Str)
98 * @brief since we know strlen()'s result, provide it here.
99 * @param Str the string to return the length to
100 * @returns contentlength of the buffer
102 inline int StrLength(const StrBuf *Str)
104 return (Str != NULL) ? Str->BufUsed : 0;
108 * @brief local utility function to resize the buffer
109 * @param Buf the buffer whichs storage we should increase
110 * @param KeepOriginal should we copy the original buffer or just start over with a new one
111 * @param DestSize what should fit in after?
113 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
116 size_t NewSize = Buf->BufSize * 2;
122 while (NewSize <= DestSize)
125 NewBuf= (char*) malloc(NewSize);
129 if (KeepOriginal && (Buf->BufUsed > 0))
131 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
140 Buf->BufSize = NewSize;
143 #ifdef HAVE_BACKTRACE
144 StrBufBacktrace(Buf, 1);
151 * @brief shrink an _EMPTY_ buffer if its Buffer superseeds threshhold to NewSize. Buffercontent is thoroughly ignored and flushed.
152 * @param Buf Buffer to shrink (has to be empty)
153 * @param ThreshHold if the buffer is bigger then this, its readjusted
154 * @param NewSize if we Shrink it, how big are we going to be afterwards?
156 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
158 if (Buf->BufUsed > ThreshHold) {
160 Buf->buf = (char*) malloc(NewSize);
162 Buf->BufSize = NewSize;
167 * @brief shrink long term buffers to their real size so they don't waste memory
168 * @param Buf buffer to shrink
169 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
170 * @returns physical size of the buffer
172 long StrBufShrinkToFit(StrBuf *Buf, int Force)
175 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
177 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
178 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
179 Buf->BufSize = Buf->BufUsed + 1;
187 * @brief Allocate a new buffer with default buffer size
188 * @returns the new stringbuffer
190 StrBuf* NewStrBuf(void)
194 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
195 NewBuf->buf = (char*) malloc(BaseStrBufSize);
196 NewBuf->buf[0] = '\0';
197 NewBuf->BufSize = BaseStrBufSize;
199 NewBuf->ConstBuf = 0;
201 NewBuf->nIncreases = 0;
202 NewBuf->bt[0] = '\0';
203 NewBuf->bt_lastinc[0] = '\0';
204 #ifdef HAVE_BACKTRACE
205 StrBufBacktrace(NewBuf, 0);
212 * @brief Copy Constructor; returns a duplicate of CopyMe
213 * @params CopyMe Buffer to faxmilate
214 * @returns the new stringbuffer
216 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
223 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
224 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
225 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
226 NewBuf->BufUsed = CopyMe->BufUsed;
227 NewBuf->BufSize = CopyMe->BufSize;
228 NewBuf->ConstBuf = 0;
230 NewBuf->nIncreases = 0;
231 NewBuf->bt[0] = '\0';
232 NewBuf->bt_lastinc[0] = '\0';
233 #ifdef HAVE_BACKTRACE
234 StrBufBacktrace(NewBuf, 0);
241 * @brief create a new Buffer using an existing c-string
242 * this function should also be used if you want to pre-suggest
243 * the buffer size to allocate in conjunction with ptr == NULL
244 * @param ptr the c-string to copy; may be NULL to create a blank instance
245 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
246 * @returns the new stringbuffer
248 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
251 size_t Siz = BaseStrBufSize;
254 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
256 CopySize = strlen((ptr != NULL)?ptr:"");
260 while (Siz <= CopySize)
263 NewBuf->buf = (char*) malloc(Siz);
264 NewBuf->BufSize = Siz;
266 memcpy(NewBuf->buf, ptr, CopySize);
267 NewBuf->buf[CopySize] = '\0';
268 NewBuf->BufUsed = CopySize;
271 NewBuf->buf[0] = '\0';
274 NewBuf->ConstBuf = 0;
276 NewBuf->nIncreases = 0;
277 NewBuf->bt[0] = '\0';
278 NewBuf->bt_lastinc[0] = '\0';
279 #ifdef HAVE_BACKTRACE
280 StrBufBacktrace(NewBuf, 0);
287 * @brief Set an existing buffer from a c-string
288 * @param ptr c-string to put into
289 * @param nChars set to -1 if we should work 0-terminated
290 * @returns the new length of the string
292 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
294 size_t Siz = Buf->BufSize;
298 CopySize = strlen(ptr);
302 while (Siz <= CopySize)
305 if (Siz != Buf->BufSize)
306 IncreaseBuf(Buf, 0, Siz);
307 memcpy(Buf->buf, ptr, CopySize);
308 Buf->buf[CopySize] = '\0';
309 Buf->BufUsed = CopySize;
316 * @brief use strbuf as wrapper for a string constant for easy handling
317 * @param StringConstant a string to wrap
318 * @param SizeOfConstant should be sizeof(StringConstant)-1
320 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
324 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
325 NewBuf->buf = (char*) StringConstant;
326 NewBuf->BufSize = SizeOfStrConstant;
327 NewBuf->BufUsed = SizeOfStrConstant;
328 NewBuf->ConstBuf = 1;
330 NewBuf->nIncreases = 0;
331 NewBuf->bt[0] = '\0';
332 NewBuf->bt_lastinc[0] = '\0';
339 * @brief flush the content of a Buf; keep its struct
340 * @param buf Buffer to flush
342 int FlushStrBuf(StrBuf *buf)
354 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
355 * @param buf Buffer to wipe
357 int FLUSHStrBuf(StrBuf *buf)
363 if (buf->BufUsed > 0) {
364 memset(buf->buf, 0, buf->BufUsed);
371 int hFreeDbglog = -1;
374 * @brief Release a Buffer
375 * Its a double pointer, so it can NULL your pointer
376 * so fancy SIG11 appear instead of random results
377 * @param FreeMe Pointer Pointer to the buffer to free
379 void FreeStrBuf (StrBuf **FreeMe)
384 if (hFreeDbglog == -1){
385 pid_t pid = getpid();
387 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
388 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
390 if ((*FreeMe)->nIncreases > 0)
394 n = snprintf(buf, SIZ * 3, "+|%ld|%ld|%ld|%s|%s|\n",
395 (*FreeMe)->nIncreases,
399 (*FreeMe)->bt_lastinc);
400 n = write(hFreeDbglog, buf, n);
406 n = snprintf(buf, 128, "_|0|%ld%ld|\n",
409 n = write(hFreeDbglog, buf, n);
412 if (!(*FreeMe)->ConstBuf)
413 free((*FreeMe)->buf);
419 * @brief flatten a Buffer to the Char * we return
420 * Its a double pointer, so it can NULL your pointer
421 * so fancy SIG11 appear instead of random results
422 * The Callee then owns the buffer and is responsible for freeing it.
423 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
424 * @returns the pointer of the buffer; Callee owns the memory thereafter.
426 char *SmashStrBuf (StrBuf **SmashMe)
430 if (*SmashMe == NULL)
433 if (hFreeDbglog == -1){
434 pid_t pid = getpid();
436 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
437 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
439 if ((*SmashMe)->nIncreases > 0)
443 n = snprintf(buf, SIZ * 3, "S+|%ld|%ld|%ld|%s|%s|\n",
444 (*SmashMe)->nIncreases,
448 (*SmashMe)->bt_lastinc);
449 n = write(hFreeDbglog, buf, n);
455 n = snprintf(buf, 128, "S_|0|%ld%ld|\n",
457 (*SmashMe)->BufSize);
458 n = write(hFreeDbglog, buf, n);
461 Ret = (*SmashMe)->buf;
468 * @brief Release the buffer
469 * If you want put your StrBuf into a Hash, use this as Destructor.
470 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
472 void HFreeStrBuf (void *VFreeMe)
474 StrBuf *FreeMe = (StrBuf*)VFreeMe;
478 if (hFreeDbglog == -1){
479 pid_t pid = getpid();
481 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
482 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
484 if (FreeMe->nIncreases > 0)
488 n = snprintf(buf, SIZ * 3, "+|%ld|%ld|%ld|%s|%s|\n",
494 write(hFreeDbglog, buf, n);
500 n = snprintf(buf, 128, "_|%ld|%ld%ld|\n",
506 if (!FreeMe->ConstBuf)
512 * @brief Wrapper around atol
514 long StrTol(const StrBuf *Buf)
519 return atol(Buf->buf);
525 * @brief Wrapper around atoi
527 int StrToi(const StrBuf *Buf)
531 if (Buf->BufUsed > 0)
532 return atoi(Buf->buf);
538 * @brief Checks to see if the string is a pure number
540 int StrBufIsNumber(const StrBuf *Buf) {
545 strtoll(Buf->buf, &pEnd, 10);
546 if (pEnd == NULL && ((Buf->buf)-pEnd) != 0) {
552 * @brief modifies a Single char of the Buf
553 * You can point to it via char* or a zero-based integer
554 * @param ptr char* to zero; use NULL if unused
555 * @param nThChar zero based pointer into the string; use -1 if unused
556 * @param PeekValue The Character to place into the position
558 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
563 nThChar = ptr - Buf->buf;
564 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
566 Buf->buf[nThChar] = PeekValue;
571 * @brief Append a StringBuffer to the buffer
572 * @param Buf Buffer to modify
573 * @param AppendBuf Buffer to copy at the end of our buffer
574 * @param Offset Should we start copying from an offset?
576 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
578 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
581 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
584 AppendBuf->BufUsed + Buf->BufUsed);
586 memcpy(Buf->buf + Buf->BufUsed,
587 AppendBuf->buf + Offset,
588 AppendBuf->BufUsed - Offset);
589 Buf->BufUsed += AppendBuf->BufUsed - Offset;
590 Buf->buf[Buf->BufUsed] = '\0';
595 * @brief Append a C-String to the buffer
596 * @param Buf Buffer to modify
597 * @param AppendBuf Buffer to copy at the end of our buffer
598 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
599 * @param Offset Should we start copying from an offset?
601 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
604 long BufSizeRequired;
606 if ((AppendBuf == NULL) || (Buf == NULL))
610 aps = strlen(AppendBuf + Offset);
612 aps = AppendSize - Offset;
614 BufSizeRequired = Buf->BufUsed + aps + 1;
615 if (Buf->BufSize <= BufSizeRequired)
616 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
618 memcpy(Buf->buf + Buf->BufUsed,
622 Buf->buf[Buf->BufUsed] = '\0';
626 * @brief Callback for cURL to append the webserver reply to a buffer
627 * @params pre-defined by the cURL API; see man 3 curl for mre info
629 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
638 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
644 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
645 * @param outbuf the output buffer
646 * @param oblen the size of outbuf to sanitize
647 * @param strbuf the input buffer
649 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
651 const char *pch, *pche;
654 const char ec[] = " +#&;`'|*?-~<>^()[]{}/$\"\\";
655 int eclen = sizeof(ec) -1;
657 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
659 if (PlainIn != NULL) {
660 len = strlen(PlainIn);
666 pche = pch + In->BufUsed;
673 pt = OutBuf->buf + OutBuf->BufUsed;
674 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
678 IncreaseBuf(OutBuf, 1, -1);
679 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
680 pt = OutBuf->buf + OutBuf->BufUsed;
684 for (b = 0; b < eclen; ++b) {
691 sprintf(pt,"%%%02X", *pch);
693 OutBuf->BufUsed += 3;
705 * @brief Append a string, escaping characters which have meaning in HTML.
707 * @param Target target buffer
708 * @param Source source buffer; set to NULL if you just have a C-String
709 * @param PlainIn Plain-C string to append; set to NULL if unused
710 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
711 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
712 * if set to 2, linebreaks are replaced by <br/>
714 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
716 const char *aptr, *eiptr;
720 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
723 if (PlainIn != NULL) {
725 len = strlen(PlainIn);
730 eiptr = aptr + Source->BufUsed;
731 len = Source->BufUsed;
737 bptr = Target->buf + Target->BufUsed;
738 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
740 while (aptr < eiptr){
742 IncreaseBuf(Target, 1, -1);
743 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
744 bptr = Target->buf + Target->BufUsed;
747 memcpy(bptr, "<", 4);
749 Target->BufUsed += 4;
751 else if (*aptr == '>') {
752 memcpy(bptr, ">", 4);
754 Target->BufUsed += 4;
756 else if (*aptr == '&') {
757 memcpy(bptr, "&", 5);
759 Target->BufUsed += 5;
761 else if (*aptr == '"') {
762 memcpy(bptr, """, 6);
764 Target->BufUsed += 6;
766 else if (*aptr == '\'') {
767 memcpy(bptr, "'", 5);
769 Target->BufUsed += 5;
771 else if (*aptr == LB) {
776 else if (*aptr == RB) {
781 else if (*aptr == QU) {
786 else if ((*aptr == 32) && (nbsp == 1)) {
787 memcpy(bptr, " ", 6);
789 Target->BufUsed += 6;
791 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
792 *bptr='\0'; /* nothing */
794 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
795 memcpy(bptr, "<br/>", 11);
797 Target->BufUsed += 11;
801 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
802 *bptr='\0'; /* nothing */
812 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
814 return Target->BufUsed;
818 * @brief Append a string, escaping characters which have meaning in HTML.
819 * Converts linebreaks into blanks; escapes single quotes
820 * @param Target target buffer
821 * @param Source source buffer; set to NULL if you just have a C-String
822 * @param PlainIn Plain-C string to append; set to NULL if unused
824 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
826 const char *aptr, *eiptr;
830 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
833 if (PlainIn != NULL) {
835 len = strlen(PlainIn);
840 eiptr = aptr + Source->BufUsed;
841 len = Source->BufUsed;
847 eptr = Target->buf + Target->BufSize - 8;
848 tptr = Target->buf + Target->BufUsed;
850 while (aptr < eiptr){
852 IncreaseBuf(Target, 1, -1);
853 eptr = Target->buf + Target->BufSize - 8;
854 tptr = Target->buf + Target->BufUsed;
861 else if (*aptr == '\r') {
865 else if (*aptr == '\'') {
871 Target->BufUsed += 5;
884 * @brief Append a string, escaping characters which have meaning in ICAL.
886 * @param Target target buffer
887 * @param Source source buffer; set to NULL if you just have a C-String
888 * @param PlainIn Plain-C string to append; set to NULL if unused
890 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
892 const char *aptr, *eiptr;
896 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
899 if (PlainIn != NULL) {
901 len = strlen(PlainIn);
906 eiptr = aptr + Source->BufUsed;
907 len = Source->BufUsed;
913 eptr = Target->buf + Target->BufSize - 8;
914 tptr = Target->buf + Target->BufUsed;
916 while (aptr < eiptr){
917 if(tptr + 3 >= eptr) {
918 IncreaseBuf(Target, 1, -1);
919 eptr = Target->buf + Target->BufSize - 8;
920 tptr = Target->buf + Target->BufUsed;
930 else if (*aptr == '\r') {
937 else if (*aptr == ',') {
953 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
955 * @param Target target buffer
956 * @param Source source buffer; set to NULL if you just have a C-String
957 * @param PlainIn Plain-C string to append; set to NULL if unused
958 * @returns size of result or -1
960 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
962 const char *aptr, *eiptr;
966 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
969 if (PlainIn != NULL) {
971 len = strlen(PlainIn);
976 eiptr = aptr + Source->BufUsed;
977 len = Source->BufUsed;
983 bptr = Target->buf + Target->BufUsed;
984 eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in... */
986 while (aptr < eiptr){
988 IncreaseBuf(Target, 1, -1);
989 eptr = Target->buf + Target->BufSize - 3;
990 bptr = Target->buf + Target->BufUsed;
997 Target->BufUsed += 2;
998 } else if (*aptr == '\\') {
1003 Target->BufUsed += 2;
1013 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
1015 return Target->BufUsed;
1019 * @brief Append a string, escaping characters which have meaning in HTML + json.
1021 * @param Target target buffer
1022 * @param Source source buffer; set to NULL if you just have a C-String
1023 * @param PlainIn Plain-C string to append; set to NULL if unused
1024 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1025 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1026 * if set to 2, linebreaks are replaced by <br/>
1028 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1030 const char *aptr, *eiptr;
1033 int IsUtf8Sequence = 0;
1035 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1038 if (PlainIn != NULL) {
1040 len = strlen(PlainIn);
1045 eiptr = aptr + Source->BufUsed;
1046 len = Source->BufUsed;
1052 bptr = Target->buf + Target->BufUsed;
1053 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1055 while (aptr < eiptr){
1057 IncreaseBuf(Target, 1, -1);
1058 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1059 bptr = Target->buf + Target->BufUsed;
1062 memcpy(bptr, "<", 4);
1064 Target->BufUsed += 4;
1066 else if (*aptr == '>') {
1067 memcpy(bptr, ">", 4);
1069 Target->BufUsed += 4;
1071 else if (*aptr == '&') {
1072 memcpy(bptr, "&", 5);
1074 Target->BufUsed += 5;
1076 else if (*aptr == LB) {
1081 else if (*aptr == RB) {
1086 else if ((*aptr == 32) && (nbsp == 1)) {
1087 memcpy(bptr, " ", 6);
1089 Target->BufUsed += 6;
1091 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1092 *bptr='\0'; /* nothing */
1094 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1095 memcpy(bptr, "<br/>", 11);
1097 Target->BufUsed += 11;
1100 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1101 *bptr='\0'; /* nothing */
1104 else if ((*aptr == '"') || (*aptr == QU)) {
1109 Target->BufUsed += 2;
1110 } else if (*aptr == '\\') {
1115 Target->BufUsed += 2;
1118 if (IsUtf8Sequence != 0) {
1127 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
1139 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
1141 return Target->BufUsed;
1146 * @brief extracts a substring from Source into dest
1147 * @param dest buffer to place substring into
1148 * @param Source string to copy substring from
1149 * @param Offset chars to skip from start
1150 * @param nChars number of chars to copy
1151 * @returns the number of chars copied; may be different from nChars due to the size of Source
1153 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1155 size_t NCharsRemain;
1156 if (Offset > Source->BufUsed)
1161 if (Offset + nChars < Source->BufUsed)
1163 if (nChars >= dest->BufSize)
1164 IncreaseBuf(dest, 0, nChars + 1);
1165 memcpy(dest->buf, Source->buf + Offset, nChars);
1166 dest->BufUsed = nChars;
1167 dest->buf[dest->BufUsed] = '\0';
1170 NCharsRemain = Source->BufUsed - Offset;
1171 if (NCharsRemain >= dest->BufSize)
1172 IncreaseBuf(dest, 0, NCharsRemain + 1);
1173 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1174 dest->BufUsed = NCharsRemain;
1175 dest->buf[dest->BufUsed] = '\0';
1176 return NCharsRemain;
1180 * @brief sprintf like function appending the formated string to the buffer
1181 * vsnprintf version to wrap into own calls
1182 * @param Buf Buffer to extend by format and @params
1183 * @param format printf alike format to add
1184 * @param ap va_list containing the items for format
1186 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
1194 if ((Buf == NULL) || (format == NULL))
1197 BufSize = Buf->BufSize;
1198 nWritten = Buf->BufSize + 1;
1199 Offset = Buf->BufUsed;
1200 newused = Offset + nWritten;
1202 while (newused >= BufSize) {
1204 nWritten = vsnprintf(Buf->buf + Offset,
1205 Buf->BufSize - Offset,
1208 newused = Offset + nWritten;
1209 if (newused >= Buf->BufSize) {
1210 IncreaseBuf(Buf, 1, newused);
1211 newused = Buf->BufSize + 1;
1214 Buf->BufUsed = Offset + nWritten;
1215 BufSize = Buf->BufSize;
1222 * @brief sprintf like function appending the formated string to the buffer
1223 * @param Buf Buffer to extend by format and @params
1224 * @param format printf alike format to add
1225 * @param ap va_list containing the items for format
1227 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
1235 if ((Buf == NULL) || (format == NULL))
1238 BufSize = Buf->BufSize;
1239 nWritten = Buf->BufSize + 1;
1240 Offset = Buf->BufUsed;
1241 newused = Offset + nWritten;
1243 while (newused >= BufSize) {
1244 va_start(arg_ptr, format);
1245 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
1246 Buf->BufSize - Buf->BufUsed,
1249 newused = Buf->BufUsed + nWritten;
1250 if (newused >= Buf->BufSize) {
1251 IncreaseBuf(Buf, 1, newused);
1252 newused = Buf->BufSize + 1;
1255 Buf->BufUsed += nWritten;
1256 BufSize = Buf->BufSize;
1263 * @brief sprintf like function putting the formated string into the buffer
1264 * @param Buf Buffer to extend by format and @params
1265 * @param format printf alike format to add
1266 * @param ap va_list containing the items for format
1268 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
1273 if ((Buf == NULL) || (format == NULL))
1276 nWritten = Buf->BufSize + 1;
1277 while (nWritten >= Buf->BufSize) {
1278 va_start(arg_ptr, format);
1279 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1281 if (nWritten >= Buf->BufSize) {
1282 IncreaseBuf(Buf, 0, 0);
1283 nWritten = Buf->BufSize + 1;
1286 Buf->BufUsed = nWritten ;
1292 * @brief Counts the numbmer of tokens in a buffer
1293 * @param Source String to count tokens in
1294 * @param tok Tokenizer char to count
1295 * @returns numbers of tokenizer chars found
1297 int StrBufNum_tokens(const StrBuf *source, char tok)
1301 return num_tokens(source->buf, tok);
1305 * remove_token() - a tokenizer that kills, maims, and destroys
1308 * @brief a string tokenizer
1309 * @param Source StringBuffer to read into
1310 * @param parmnum n'th @parameter to remove
1311 * @param separator tokenizer @param
1312 * @returns -1 if not found, else length of token.
1314 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1317 char *d, *s, *end; /* dest, source */
1320 /* Find desired @parameter */
1321 end = Source->buf + Source->BufUsed;
1323 while ((count < parmnum) &&
1326 /* End of string, bail! */
1331 if (*d == separator) {
1336 if ((d == NULL) || (d >= end))
1337 return 0; /* @Parameter not found */
1339 /* Find next @parameter */
1341 while ((*s && *s != separator) &&
1346 if (*s == separator)
1350 /* Hack and slash */
1352 memmove(d, s, Source->BufUsed - (s - Source->buf));
1353 Source->BufUsed += ReducedBy;
1355 else if (d == Source->buf) {
1357 Source->BufUsed = 0;
1361 Source->BufUsed += ReducedBy;
1374 * @brief a string tokenizer
1375 * @param dest Destination StringBuffer
1376 * @param Source StringBuffer to read into
1377 * @param parmnum n'th @parameter to extract
1378 * @param separator tokenizer @param
1379 * @returns -1 if not found, else length of token.
1381 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1383 const char *s, *e; //* source * /
1384 int len = 0; //* running total length of extracted string * /
1385 int current_token = 0; //* token currently being processed * /
1388 dest->buf[0] = '\0';
1394 if ((Source == NULL) || (Source->BufUsed ==0)) {
1398 e = s + Source->BufUsed;
1401 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1403 while ((s<e) && !IsEmptyStr(s)) {
1404 if (*s == separator) {
1407 if (len >= dest->BufSize) {
1408 dest->BufUsed = len;
1409 if (IncreaseBuf(dest, 1, -1) < 0) {
1414 if ( (current_token == parmnum) &&
1415 (*s != separator)) {
1416 dest->buf[len] = *s;
1419 else if (current_token > parmnum) {
1425 dest->buf[len] = '\0';
1426 dest->BufUsed = len;
1428 if (current_token < parmnum) {
1429 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1432 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1441 * @brief a string tokenizer to fetch an integer
1442 * @param dest Destination StringBuffer
1443 * @param parmnum n'th @parameter to extract
1444 * @param separator tokenizer @param
1445 * @returns 0 if not found, else integer representation of the token
1447 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1457 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1464 * @brief a string tokenizer to fetch a long integer
1465 * @param dest Destination StringBuffer
1466 * @param parmnum n'th @parameter to extract
1467 * @param separator tokenizer @param
1468 * @returns 0 if not found, else long integer representation of the token
1470 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1480 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1488 * @brief a string tokenizer to fetch an unsigned long
1489 * @param dest Destination StringBuffer
1490 * @param parmnum n'th @parameter to extract
1491 * @param separator tokenizer @param
1492 * @returns 0 if not found, else unsigned long representation of the token
1494 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1505 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1509 return (unsigned long) atol(pnum);
1518 * @briefa string tokenizer; Bounds checker
1519 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1520 * @param Source our tokenbuffer
1521 * @param pStart the token iterator pointer to inspect
1522 * @returns whether the revolving pointer is inside of the search range
1524 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1526 if ((Source == NULL) ||
1527 (*pStart == StrBufNOTNULL) ||
1528 (Source->BufUsed == 0))
1532 if (*pStart == NULL)
1536 else if (*pStart > Source->buf + Source->BufUsed)
1540 else if (*pStart <= Source->buf)
1549 * @brief a string tokenizer
1550 * @param dest Destination StringBuffer
1551 * @param Source StringBuffer to read into
1552 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1553 * @param separator tokenizer @param
1554 * @returns -1 if not found, else length of token.
1556 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1558 const char *s; /* source */
1559 const char *EndBuffer; /* end stop of source buffer */
1560 int current_token = 0; /* token currently being processed */
1561 int len = 0; /* running total length of extracted string */
1563 if ((Source == NULL) ||
1564 (Source->BufUsed == 0) )
1566 *pStart = StrBufNOTNULL;
1570 EndBuffer = Source->buf + Source->BufUsed;
1574 dest->buf[0] = '\0';
1579 *pStart = EndBuffer + 1;
1583 if (*pStart == NULL)
1585 *pStart = Source->buf; /* we're starting to examine this buffer. */
1587 else if ((*pStart < Source->buf) ||
1588 (*pStart > EndBuffer ) )
1590 return -1; /* no more tokens to find. */
1594 /* start to find the next token */
1595 while ((s <= EndBuffer) &&
1596 (current_token == 0) )
1598 if (*s == separator)
1600 /* we found the next token */
1604 if (len >= dest->BufSize)
1606 /* our Dest-buffer isn't big enough, increase it. */
1607 dest->BufUsed = len;
1609 if (IncreaseBuf(dest, 1, -1) < 0) {
1610 /* WHUT? no more mem? bail out. */
1617 if ( (current_token == 0 ) && /* are we in our target token? */
1618 (!IsEmptyStr(s) ) &&
1619 (separator != *s) ) /* don't copy the token itself */
1621 dest->buf[len] = *s; /* Copy the payload */
1622 ++len; /* remember the bigger size. */
1628 /* did we reach the end? */
1629 if ((s > EndBuffer)) {
1630 EndBuffer = StrBufNOTNULL;
1631 *pStart = EndBuffer;
1634 *pStart = s; /* remember the position for the next run */
1637 /* sanitize our extracted token */
1638 dest->buf[len] = '\0';
1639 dest->BufUsed = len;
1646 * @brief a string tokenizer
1647 * @param dest Destination StringBuffer
1648 * @param Source StringBuffer to read into
1649 * @param pStart pointer to the end of the last token. Feed with NULL.
1650 * @param separator tokenizer @param
1651 * @returns -1 if not found, else length of token.
1653 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1655 const char *s, *EndBuffer; //* source * /
1656 int len = 0; //* running total length of extracted string * /
1657 int current_token = 0; //* token currently being processed * /
1659 if ((Source == NULL) ||
1660 (Source->BufUsed ==0)) {
1664 return Source->BufUsed;
1666 if (*pStart == NULL)
1667 *pStart = Source->buf;
1669 EndBuffer = Source->buf + Source->BufUsed;
1671 if ((*pStart < Source->buf) ||
1672 (*pStart > EndBuffer)) {
1680 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1682 while ((s<EndBuffer) && !IsEmptyStr(s)) {
1683 if (*s == separator) {
1686 if (current_token >= nTokens) {
1698 * @brief a string tokenizer to fetch an integer
1699 * @param dest Destination StringBuffer
1700 * @param parmnum n'th @parameter to extract
1701 * @param separator tokenizer @param
1702 * @returns 0 if not found, else integer representation of the token
1704 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1714 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1721 * @brief a string tokenizer to fetch a long integer
1722 * @param dest Destination StringBuffer
1723 * @param parmnum n'th @parameter to extract
1724 * @param separator tokenizer @param
1725 * @returns 0 if not found, else long integer representation of the token
1727 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1737 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1745 * @brief a string tokenizer to fetch an unsigned long
1746 * @param dest Destination StringBuffer
1747 * @param parmnum n'th @parameter to extract
1748 * @param separator tokenizer @param
1749 * @returns 0 if not found, else unsigned long representation of the token
1751 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1762 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1766 return (unsigned long) atol(pnum);
1775 * @brief Read a line from socket
1776 * flushes and closes the FD on error
1777 * @param buf the buffer to get the input to
1778 * @param fd pointer to the filedescriptor to read
1779 * @param append Append to an existing string or replace?
1780 * @param Error strerror() on error
1781 * @returns numbers of chars read
1783 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
1785 int len, rlen, slen;
1790 slen = len = buf->BufUsed;
1792 rlen = read(*fd, &buf->buf[len], 1);
1794 *Error = strerror(errno);
1801 if (buf->buf[len] == '\n')
1803 if (buf->buf[len] != '\r')
1805 if (len + 2 >= buf->BufSize) {
1807 buf->buf[len+1] = '\0';
1808 IncreaseBuf(buf, 1, -1);
1812 buf->buf[len] = '\0';
1817 * @brief Read a line from socket
1818 * flushes and closes the FD on error
1819 * @param buf the buffer to get the input to
1820 * @param fd pointer to the filedescriptor to read
1821 * @param append Append to an existing string or replace?
1822 * @param Error strerror() on error
1823 * @returns numbers of chars read
1825 int StrBufTCP_read_buffered_line(StrBuf *Line,
1829 int selectresolution,
1833 int nSuccessLess = 0;
1840 if (buf->BufUsed > 0) {
1841 pch = strchr(buf->buf, '\n');
1844 len = pch - buf->buf;
1845 if (len > 0 && (*(pch - 1) == '\r') )
1847 StrBufSub(Line, buf, 0, len - rlen);
1848 StrBufCutLeft(buf, len + 1);
1853 if (buf->BufSize - buf->BufUsed < 10)
1854 IncreaseBuf(buf, 1, -1);
1856 fdflags = fcntl(*fd, F_GETFL);
1857 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
1859 while ((nSuccessLess < timeout) && (pch == NULL)) {
1861 tv.tv_sec = selectresolution;
1866 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
1867 *Error = strerror(errno);
1873 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
1878 &buf->buf[buf->BufUsed],
1879 buf->BufSize - buf->BufUsed - 1);
1881 *Error = strerror(errno);
1886 else if (rlen > 0) {
1888 buf->BufUsed += rlen;
1889 buf->buf[buf->BufUsed] = '\0';
1890 if (buf->BufUsed + 10 > buf->BufSize) {
1891 IncreaseBuf(buf, 1, -1);
1893 pch = strchr(buf->buf, '\n');
1900 len = pch - buf->buf;
1901 if (len > 0 && (*(pch - 1) == '\r') )
1903 StrBufSub(Line, buf, 0, len - rlen);
1904 StrBufCutLeft(buf, len + 1);
1911 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
1912 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
1914 * @brief Read a line from socket
1915 * flushes and closes the FD on error
1916 * @param buf the buffer to get the input to
1917 * @param Pos pointer to the current read position, should be NULL initialized!
1918 * @param fd pointer to the filedescriptor to read
1919 * @param append Append to an existing string or replace?
1920 * @param Error strerror() on error
1921 * @returns numbers of chars read
1923 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
1928 int selectresolution,
1931 const char *pche = NULL;
1932 const char *pos = NULL;
1934 int nSuccessLess = 0;
1936 const char *pch = NULL;
1942 if ((IOBuf->BufUsed > 0) &&
1944 (pos < IOBuf->buf + IOBuf->BufUsed))
1946 pche = IOBuf->buf + IOBuf->BufUsed;
1948 while ((pch < pche) && (*pch != '\n'))
1950 if ((pch >= pche) || (*pch == '\0'))
1952 if ((pch != NULL) &&
1957 if (len > 0 && (*(pch - 1) == '\r') )
1959 StrBufSub(Line, IOBuf, (pos - IOBuf->buf), len - rlen);
1969 StrBufCutLeft(IOBuf, (pos - IOBuf->buf));
1973 if (IOBuf->BufSize - IOBuf->BufUsed < 10) {
1974 IncreaseBuf(IOBuf, 1, -1);
1977 fdflags = fcntl(*fd, F_GETFL);
1978 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
1981 while ((nSuccessLess < timeout) &&
1991 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
1992 *Error = strerror(errno);
1996 *Error = ErrRBLF_SelectFailed;
1999 if (! FD_ISSET(*fd, &rfds) != 0) {
2005 &IOBuf->buf[IOBuf->BufUsed],
2006 IOBuf->BufSize - IOBuf->BufUsed - 1);
2008 *Error = strerror(errno);
2013 else if (rlen > 0) {
2015 IOBuf->BufUsed += rlen;
2016 IOBuf->buf[IOBuf->BufUsed] = '\0';
2017 if (IOBuf->BufUsed + 10 > IOBuf->BufSize) {
2018 IncreaseBuf(IOBuf, 1, -1);
2021 pche = IOBuf->buf + IOBuf->BufUsed;
2023 while ((pch < pche) && (*pch != '\n'))
2025 if ((pch >= pche) || (*pch == '\0'))
2034 if (len > 0 && (*(pch - 1) == '\r') )
2036 StrBufSub(Line, IOBuf, 0, len - rlen);
2037 *Pos = pos + len + 1;
2040 *Error = ErrRBLF_NotEnoughSentFromServer;
2046 * @brief Input binary data from socket
2047 * flushes and closes the FD on error
2048 * @param buf the buffer to get the input to
2049 * @param fd pointer to the filedescriptor to read
2050 * @param append Append to an existing string or replace?
2051 * @param nBytes the maximal number of bytes to read
2052 * @param Error strerror() on error
2053 * @returns numbers of chars read
2055 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
2058 int len, rlen, slen;
2065 if ((Buf == NULL) || (*fd == -1))
2069 if (Buf->BufUsed + nBytes >= Buf->BufSize)
2070 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
2072 ptr = Buf->buf + Buf->BufUsed;
2074 slen = len = Buf->BufUsed;
2076 fdflags = fcntl(*fd, F_GETFL);
2077 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
2079 while ((nRead < nBytes) &&
2089 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
2090 *Error = strerror(errno);
2094 *Error = ErrRBLF_SelectFailed;
2097 if (! FD_ISSET(*fd, &rfds) != 0) {
2103 if ((rlen = read(*fd,
2105 nBytes - nRead)) == -1) {
2108 *Error = strerror(errno);
2113 Buf->BufUsed += rlen;
2115 Buf->buf[Buf->BufUsed] = '\0';
2119 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
2121 * @brief Input binary data from socket
2122 * flushes and closes the FD on error
2123 * @param buf the buffer to get the input to
2124 * @param fd pointer to the filedescriptor to read
2125 * @param append Append to an existing string or replace?
2126 * @param nBytes the maximal number of bytes to read
2127 * @param Error strerror() on error
2128 * @returns numbers of chars read
2130 int StrBufReadBLOBBuffered(StrBuf *Blob,
2147 int nAlreadyRead = 0;
2155 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
2159 if (Blob->BufUsed + nBytes >= Blob->BufSize)
2160 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
2165 len = pos - IOBuf->buf;
2166 rlen = IOBuf->BufUsed - len;
2169 if ((IOBuf->BufUsed > 0) &&
2171 (pos < IOBuf->buf + IOBuf->BufUsed))
2173 pche = IOBuf->buf + IOBuf->BufUsed;
2176 if (rlen < nBytes) {
2177 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
2178 Blob->BufUsed += rlen;
2179 Blob->buf[Blob->BufUsed] = '\0';
2180 nAlreadyRead = nRead = rlen;
2183 if (rlen >= nBytes) {
2184 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
2185 Blob->BufUsed += nBytes;
2186 Blob->buf[Blob->BufUsed] = '\0';
2187 if (rlen == nBytes) {
2198 if (IOBuf->BufSize < nBytes - nRead)
2199 IncreaseBuf(IOBuf, 0, nBytes - nRead);
2202 slen = len = Blob->BufUsed;
2204 fdflags = fcntl(*fd, F_GETFL);
2205 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
2210 while ((nRead < nBytes) &&
2219 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
2220 *Error = strerror(errno);
2224 *Error = ErrRBLF_SelectFailed;
2227 if (! FD_ISSET(*fd, &rfds) != 0) {
2239 *Error = strerror(errno);
2242 else if (rlen == 0){
2244 if ((check == NNN_TERM) &&
2246 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
2248 StrBufPlain(Blob, HKEY("\n000\n"));
2249 StrBufCutRight(Blob, 5);
2250 return Blob->BufUsed;
2252 if (nSelects > 10) {
2254 *Error = ErrRBB_too_many_selects;
2258 else if (rlen > 0) {
2261 IOBuf->BufUsed += rlen;
2264 if (nRead > nBytes) {
2265 *Pos = IOBuf->buf + nBytes;
2267 Blob->buf[Blob->BufUsed] = '\0';
2268 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
2272 return nRead + nAlreadyRead;
2276 * @brief Cut nChars from the start of the string
2277 * @param Buf Buffer to modify
2278 * @param nChars how many chars should be skipped?
2280 void StrBufCutLeft(StrBuf *Buf, int nChars)
2282 if (nChars >= Buf->BufUsed) {
2286 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
2287 Buf->BufUsed -= nChars;
2288 Buf->buf[Buf->BufUsed] = '\0';
2292 * @brief Cut the trailing n Chars from the string
2293 * @param Buf Buffer to modify
2294 * @param nChars how many chars should be trunkated?
2296 void StrBufCutRight(StrBuf *Buf, int nChars)
2298 if (nChars >= Buf->BufUsed) {
2302 Buf->BufUsed -= nChars;
2303 Buf->buf[Buf->BufUsed] = '\0';
2307 * @brief Cut the string after n Chars
2308 * @param Buf Buffer to modify
2309 * @param AfternChars after how many chars should we trunkate the string?
2310 * @param At if non-null and points inside of our string, cut it there.
2312 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
2315 AfternChars = At - Buf->buf;
2318 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
2320 Buf->BufUsed = AfternChars;
2321 Buf->buf[Buf->BufUsed] = '\0';
2326 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
2327 * @param buf the string to modify
2328 * @param len length of the string.
2330 void StrBufTrim(StrBuf *Buf)
2333 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
2335 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
2338 if (delta > 0) StrBufCutLeft(Buf, delta);
2340 if (Buf->BufUsed == 0) return;
2341 while (isspace(Buf->buf[Buf->BufUsed - 1])){
2344 Buf->buf[Buf->BufUsed] = '\0';
2348 * @brief uppercase the contents of a buffer
2349 * @param Buf the buffer to translate
2351 void StrBufUpCase(StrBuf *Buf)
2356 pche = pch + Buf->BufUsed;
2357 while (pch < pche) {
2358 *pch = toupper(*pch);
2365 * @brief lowercase the contents of a buffer
2366 * @param Buf the buffer to translate
2368 void StrBufLowerCase(StrBuf *Buf)
2373 pche = pch + Buf->BufUsed;
2374 while (pch < pche) {
2375 *pch = tolower(*pch);
2381 * @brief removes double slashes from pathnames
2382 * @param Dir directory string to filter
2383 * @param RemoveTrailingSlash allows / disallows trailing slashes
2385 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
2391 while (!IsEmptyStr(a)) {
2403 if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
2408 Dir->BufUsed = b - Dir->buf;
2412 * @brief unhide special chars hidden to the HTML escaper
2413 * @param target buffer to put the unescaped string in
2414 * @param source buffer to unescape
2416 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2422 FlushStrBuf(target);
2424 if (source == NULL ||target == NULL)
2429 len = source->BufUsed;
2430 for (a = 0; a < len; ++a) {
2431 if (target->BufUsed >= target->BufSize)
2432 IncreaseBuf(target, 1, -1);
2434 if (source->buf[a] == '=') {
2435 hex[0] = source->buf[a + 1];
2436 hex[1] = source->buf[a + 2];
2439 sscanf(hex, "%02x", &b);
2440 target->buf[target->BufUsed] = b;
2441 target->buf[++target->BufUsed] = 0;
2445 target->buf[target->BufUsed] = source->buf[a];
2446 target->buf[++target->BufUsed] = 0;
2453 * @brief hide special chars from the HTML escapers and friends
2454 * @param target buffer to put the escaped string in
2455 * @param source buffer to escape
2457 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2462 FlushStrBuf(target);
2464 if (source == NULL ||target == NULL)
2469 len = source->BufUsed;
2470 for (i=0; i<len; ++i) {
2471 if (target->BufUsed + 4 >= target->BufSize)
2472 IncreaseBuf(target, 1, -1);
2473 if ( (isalnum(source->buf[i])) ||
2474 (source->buf[i]=='-') ||
2475 (source->buf[i]=='_') ) {
2476 target->buf[target->BufUsed++] = source->buf[i];
2479 sprintf(&target->buf[target->BufUsed],
2481 (0xFF &source->buf[i]));
2482 target->BufUsed += 3;
2485 target->buf[target->BufUsed + 1] = '\0';
2489 * @brief uses the same calling syntax as compress2(), but it
2490 * creates a stream compatible with HTTP "Content-encoding: gzip"
2493 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
2494 #define OS_CODE 0x03 /*< unix */
2495 int ZEXPORT compress_gzip(Bytef * dest, /*< compressed buffer*/
2496 size_t * destLen, /*< length of the compresed data */
2497 const Bytef * source, /*< source to encode */
2498 uLong sourceLen, /*< length of source to encode */
2499 int level) /*< compression level */
2501 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
2503 /* write gzip header */
2504 snprintf((char *) dest, *destLen,
2505 "%c%c%c%c%c%c%c%c%c%c",
2506 gz_magic[0], gz_magic[1], Z_DEFLATED,
2507 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2510 /* normal deflate */
2513 stream.next_in = (Bytef *) source;
2514 stream.avail_in = (uInt) sourceLen;
2515 stream.next_out = dest + 10L; // after header
2516 stream.avail_out = (uInt) * destLen;
2517 if ((uLong) stream.avail_out != *destLen)
2520 stream.zalloc = (alloc_func) 0;
2521 stream.zfree = (free_func) 0;
2522 stream.opaque = (voidpf) 0;
2524 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
2525 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
2529 err = deflate(&stream, Z_FINISH);
2530 if (err != Z_STREAM_END) {
2531 deflateEnd(&stream);
2532 return err == Z_OK ? Z_BUF_ERROR : err;
2534 *destLen = stream.total_out + 10L;
2536 /* write CRC and Length */
2537 uLong crc = crc32(0L, source, sourceLen);
2539 for (n = 0; n < 4; ++n, ++*destLen) {
2540 dest[*destLen] = (int) (crc & 0xff);
2543 uLong len = stream.total_in;
2544 for (n = 0; n < 4; ++n, ++*destLen) {
2545 dest[*destLen] = (int) (len & 0xff);
2548 err = deflateEnd(&stream);
2555 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
2557 int CompressBuffer(StrBuf *Buf)
2560 char *compressed_data = NULL;
2561 size_t compressed_len, bufsize;
2564 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
2565 compressed_data = malloc(compressed_len);
2567 if (compressed_data == NULL)
2569 /* Flush some space after the used payload so valgrind shuts up... */
2570 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
2571 Buf->buf[Buf->BufUsed + i++] = '\0';
2572 if (compress_gzip((Bytef *) compressed_data,
2575 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
2578 Buf->buf = compressed_data;
2579 Buf->BufUsed = compressed_len;
2580 Buf->BufSize = bufsize;
2581 /* Flush some space after the used payload so valgrind shuts up... */
2583 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
2584 Buf->buf[Buf->BufUsed + i++] = '\0';
2587 free(compressed_data);
2589 #endif /* HAVE_ZLIB */
2594 * @brief decode a buffer from base 64 encoding; destroys original
2595 * @param Buf Buffor to transform
2597 int StrBufDecodeBase64(StrBuf *Buf)
2601 if (Buf == NULL) return -1;
2603 xferbuf = (char*) malloc(Buf->BufSize);
2604 siz = CtdlDecodeBase64(xferbuf,
2614 * @brief decode a buffer from base 64 encoding; destroys original
2615 * @param Buf Buffor to transform
2617 int StrBufDecodeHex(StrBuf *Buf)
2620 char *pch, *pche, *pchi;
2622 if (Buf == NULL) return -1;
2624 pch = pchi = Buf->buf;
2625 pche = pch + Buf->BufUsed;
2627 while (pchi < pche){
2628 ch = decode_hex(pchi);
2635 Buf->BufUsed = pch - Buf->buf;
2636 return Buf->BufUsed;
2640 * @brief replace all chars >0x20 && < 0x7F with Mute
2641 * @param Mute char to put over invalid chars
2642 * @param Buf Buffor to transform
2644 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2648 if (Buf == NULL) return -1;
2649 pch = (unsigned char *)Buf->buf;
2650 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2651 if ((*pch < 0x20) || (*pch > 0x7F))
2655 return Buf->BufUsed;
2660 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2661 * @param Buf Buffer to translate
2662 * @param StripBlanks Reduce several blanks to one?
2664 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2670 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2671 Buf->buf[Buf->BufUsed - 1] = '\0';
2676 while (a < Buf->BufUsed) {
2677 if (Buf->buf[a] == '+')
2679 else if (Buf->buf[a] == '%') {
2680 /* don't let % chars through, rather truncate the input. */
2681 if (a + 2 > Buf->BufUsed) {
2686 hex[0] = Buf->buf[a + 1];
2687 hex[1] = Buf->buf[a + 2];
2690 sscanf(hex, "%02x", &b);
2691 Buf->buf[a] = (char) b;
2692 len = Buf->BufUsed - a - 2;
2694 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2706 * @brief RFC2047-encode a header field if necessary.
2707 * If no non-ASCII characters are found, the string
2708 * will be copied verbatim without encoding.
2710 * @param target Target buffer.
2711 * @param source Source string to be encoded.
2712 * @returns encoded length; -1 if non success.
2714 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2716 const char headerStr[] = "=?UTF-8?Q?";
2717 int need_to_encode = 0;
2721 if ((source == NULL) ||
2725 while ((i < source->BufUsed) &&
2726 (!IsEmptyStr (&source->buf[i])) &&
2727 (need_to_encode == 0)) {
2728 if (((unsigned char) source->buf[i] < 32) ||
2729 ((unsigned char) source->buf[i] > 126)) {
2735 if (!need_to_encode) {
2736 if (*target == NULL) {
2737 *target = NewStrBufPlain(source->buf, source->BufUsed);
2740 FlushStrBuf(*target);
2741 StrBufAppendBuf(*target, source, 0);
2743 return (*target)->BufUsed;
2745 if (*target == NULL)
2746 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2747 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2748 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2749 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2750 (*target)->BufUsed = sizeof(headerStr) - 1;
2751 for (i=0; (i < source->BufUsed); ++i) {
2752 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2753 IncreaseBuf(*target, 1, 0);
2754 ch = (unsigned char) source->buf[i];
2755 if ((ch < 32) || (ch > 126) || (ch == 61)) {
2756 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2757 (*target)->BufUsed += 3;
2760 (*target)->buf[(*target)->BufUsed] = ch;
2761 (*target)->BufUsed++;
2765 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2766 IncreaseBuf(*target, 1, 0);
2768 (*target)->buf[(*target)->BufUsed++] = '?';
2769 (*target)->buf[(*target)->BufUsed++] = '=';
2770 (*target)->buf[(*target)->BufUsed] = '\0';
2771 return (*target)->BufUsed;;
2775 * @brief replaces all occurances of 'search' by 'replace'
2776 * @param buf Buffer to modify
2777 * @param search character to search
2778 * @param relpace character to replace search by
2780 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2785 for (i=0; i<buf->BufUsed; i++)
2786 if (buf->buf[i] == search)
2787 buf->buf[i] = replace;
2794 * @brief Wrapper around iconv_open()
2795 * Our version adds aliases for non-standard Microsoft charsets
2796 * such as 'MS950', aliasing them to names like 'CP950'
2798 * @param tocode Target encoding
2799 * @param fromcode Source encoding
2801 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2804 iconv_t ic = (iconv_t)(-1) ;
2805 ic = iconv_open(tocode, fromcode);
2806 if (ic == (iconv_t)(-1) ) {
2807 char alias_fromcode[64];
2808 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2809 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2810 alias_fromcode[0] = 'C';
2811 alias_fromcode[1] = 'P';
2812 ic = iconv_open(tocode, alias_fromcode);
2815 *(iconv_t *)pic = ic;
2821 * @brief find one chunk of a RFC822 encoded string
2822 * @param Buffer where to search
2823 * @param bptr where to start searching
2824 * @returns found position, NULL if none.
2826 static inline char *FindNextEnd (const StrBuf *Buf, char *bptr)
2829 /* Find the next ?Q? */
2830 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
2833 end = strchr(bptr + 2, '?');
2838 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2839 ((*(end + 1) == 'B') || (*(end + 1) == 'Q')) &&
2840 (*(end + 2) == '?')) {
2841 /* skip on to the end of the cluster, the next ?= */
2842 end = strstr(end + 3, "?=");
2845 /* sort of half valid encoding, try to find an end. */
2846 end = strstr(bptr, "?=");
2851 * @brief swaps the contents of two StrBufs
2852 * this is to be used to have cheap switched between a work-buffer and a target buffer
2853 * @param A First one
2854 * @param B second one
2856 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
2860 memcpy(&C, A, sizeof(*A));
2861 memcpy(A, B, sizeof(*B));
2862 memcpy(B, &C, sizeof(C));
2868 * @brief convert one buffer according to the preselected iconv pointer PIC
2869 * @param ConvertBuf buffer we need to translate
2870 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
2871 * @param pic Pointer to the iconv-session Object
2873 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
2879 char *ibuf; /**< Buffer of characters to be converted */
2880 char *obuf; /**< Buffer for converted characters */
2881 size_t ibuflen; /**< Length of input buffer */
2882 size_t obuflen; /**< Length of output buffer */
2885 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
2886 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
2887 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
2889 ic = *(iconv_t*)pic;
2890 ibuf = ConvertBuf->buf;
2891 ibuflen = ConvertBuf->BufUsed;
2893 obuflen = TmpBuf->BufSize;
2895 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
2898 if (errno == E2BIG) {
2900 IncreaseBuf(TmpBuf, 0, 0);
2905 else if (errno == EILSEQ){
2906 /* hm, invalid utf8 sequence... what to do now? */
2907 /* An invalid multibyte sequence has been encountered in the input */
2909 else if (errno == EINVAL) {
2910 /* An incomplete multibyte sequence has been encountered in the input. */
2913 FlushStrBuf(TmpBuf);
2916 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
2917 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
2919 /* little card game: wheres the red lady? */
2920 SwapBuffers(ConvertBuf, TmpBuf);
2921 FlushStrBuf(TmpBuf);
2928 * @brief catches one RFC822 encoded segment, and decodes it.
2929 * @param Target buffer to fill with result
2930 * @param DecodeMe buffer with stuff to process
2931 * @param SegmentStart points to our current segment in DecodeMe
2932 * @param SegmentEnd Points to the end of our current segment in DecodeMe
2933 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
2934 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
2935 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
2937 inline static void DecodeSegment(StrBuf *Target,
2938 const StrBuf *DecodeMe,
2942 StrBuf *ConvertBuf2,
2943 StrBuf *FoundCharset)
2949 iconv_t ic = (iconv_t)(-1);
2953 /* Now we handle foreign character sets properly encoded
2954 * in RFC2047 format.
2956 StaticBuf.buf = SegmentStart;
2957 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
2958 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
2959 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
2960 if (FoundCharset != NULL) {
2961 FlushStrBuf(FoundCharset);
2962 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
2964 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
2965 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
2967 *encoding = toupper(*encoding);
2968 if (*encoding == 'B') { /**< base64 */
2969 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
2971 ConvertBuf->BufUsed);
2973 else if (*encoding == 'Q') { /**< quoted-printable */
2977 while (pos < ConvertBuf->BufUsed)
2979 if (ConvertBuf->buf[pos] == '_')
2980 ConvertBuf->buf[pos] = ' ';
2984 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
2987 ConvertBuf->BufUsed);
2990 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
2993 ctdl_iconv_open("UTF-8", charset, &ic);
2994 if (ic != (iconv_t)(-1) ) {
2996 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
2997 StrBufAppendBuf(Target, ConvertBuf2, 0);
3002 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3008 * @brief Handle subjects with RFC2047 encoding such as:
3009 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3010 * @param Target where to put the decoded string to
3011 * @param DecodeMe buffer with encoded string
3012 * @param DefaultCharset if we don't find one, which should we use?
3013 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3014 * put it here for later use where no string might be known.
3016 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3018 StrBuf *DecodedInvalidBuf = NULL;
3019 StrBuf *ConvertBuf, *ConvertBuf2;
3020 const StrBuf *DecodeMee = DecodeMe;
3021 char *start, *end, *next, *nextend, *ptr = NULL;
3023 iconv_t ic = (iconv_t)(-1) ;
3028 int illegal_non_rfc2047_encoding = 0;
3030 /* Sometimes, badly formed messages contain strings which were simply
3031 * written out directly in some foreign character set instead of
3032 * using RFC2047 encoding. This is illegal but we will attempt to
3033 * handle it anyway by converting from a user-specified default
3034 * charset to UTF-8 if we see any nonprintable characters.
3037 len = StrLength(DecodeMe);
3038 for (i=0; i<DecodeMe->BufUsed; ++i) {
3039 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3040 illegal_non_rfc2047_encoding = 1;
3045 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3046 if ((illegal_non_rfc2047_encoding) &&
3047 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3048 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3051 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3052 if (ic != (iconv_t)(-1) ) {
3053 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3054 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3055 DecodeMee = DecodedInvalidBuf;
3061 /* pre evaluate the first pair */
3062 nextend = end = NULL;
3063 len = StrLength(DecodeMee);
3064 start = strstr(DecodeMee->buf, "=?");
3065 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3067 end = FindNextEnd (DecodeMee, start);
3069 StrBufAppendBuf(Target, DecodeMee, 0);
3070 FreeStrBuf(&ConvertBuf);
3071 FreeStrBuf(&DecodedInvalidBuf);
3075 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMee));
3077 if (start != DecodeMee->buf) {
3080 nFront = start - DecodeMee->buf;
3081 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3085 * Since spammers will go to all sorts of absurd lengths to get their
3086 * messages through, there are LOTS of corrupt headers out there.
3087 * So, prevent a really badly formed RFC2047 header from throwing
3088 * this function into an infinite loop.
3090 while ((start != NULL) &&
3097 DecodeSegment(Target,
3105 next = strstr(end, "=?");
3107 if ((next != NULL) &&
3109 nextend = FindNextEnd(DecodeMee, next);
3110 if (nextend == NULL)
3113 /* did we find two partitions */
3114 if ((next != NULL) &&
3118 while ((ptr < next) &&
3124 /* did we find a gab just filled with blanks? */
3127 long gap = next - start;
3132 /* now terminate the gab at the end */
3133 delta = (next - end) - 2; ////TODO: const!
3134 ((StrBuf*)DecodeMee)->BufUsed -= delta;
3135 ((StrBuf*)DecodeMee)->buf[DecodeMee->BufUsed] = '\0';
3137 /* move next to its new location. */
3142 /* our next-pair is our new first pair now. */
3148 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3149 if ((end != NULL) && (end < nextend)) {
3151 while ( (ptr < nextend) &&
3158 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3160 FreeStrBuf(&ConvertBuf);
3161 FreeStrBuf(&ConvertBuf2);
3162 FreeStrBuf(&DecodedInvalidBuf);
3166 * @brief evaluate the length of an utf8 special character sequence
3167 * @param Char the character to examine
3168 * @returns width of utf8 chars in bytes
3170 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3175 while ((n < 8) && ((test & *CharS) != 0)) {
3179 if ((n > 6) || ((CharE - CharS) > n))
3185 * @brief detect whether this char starts an utf-8 encoded char
3186 * @param Char character to inspect
3187 * @returns yes or no
3189 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3191 /** 11??.???? indicates an UTF8 Sequence. */
3192 return ((Char & 0xC0) != 0);
3196 * @brief measure the number of glyphs in an UTF8 string...
3197 * @param str string to measure
3198 * @returns the length of str
3200 long StrBuf_Utf8StrLen(StrBuf *Buf)
3206 if ((Buf == NULL) || (Buf->BufUsed == 0))
3209 eptr = Buf->buf + Buf->BufUsed;
3210 while ((aptr < eptr) && (*aptr != '\0')) {
3211 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3212 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3213 while ((aptr < eptr) && (m-- > 0) && (*aptr++ != '\0'))
3226 * @brief cuts a string after maxlen glyphs
3227 * @param str string to cut to maxlen glyphs
3228 * @param maxlen how long may the string become?
3229 * @returns pointer to maxlen or the end of the string
3231 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3237 eptr = Buf->buf + Buf->BufUsed;
3238 while ((aptr < eptr) && (*aptr != '\0')) {
3239 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3240 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3241 while ((m-- > 0) && (*aptr++ != '\0'))
3250 Buf->BufUsed = aptr - Buf->buf;
3251 return Buf->BufUsed;
3254 return Buf->BufUsed;
3260 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3261 * @param LineBuf your line will be copied here.
3262 * @param Buf BLOB with lines of text...
3263 * @param Ptr moved arround to keep the next-line across several iterations
3264 * has to be &NULL on start; will be &NotNULL on end of buffer
3265 * @returns size of copied buffer
3267 int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
3269 const char *aptr, *ptr, *eptr;
3272 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
3273 *Ptr = StrBufNOTNULL;
3277 FlushStrBuf(LineBuf);
3279 ptr = aptr = Buf->buf;
3283 optr = LineBuf->buf;
3284 eptr = Buf->buf + Buf->BufUsed;
3285 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3287 while ((ptr <= eptr) &&
3294 LineBuf->BufUsed = optr - LineBuf->buf;
3295 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3296 optr = LineBuf->buf + LineBuf->BufUsed;
3297 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3301 if ((ptr >= eptr) && (optr > LineBuf->buf))
3303 LineBuf->BufUsed = optr - LineBuf->buf;
3305 if ((ptr <= eptr) && (*ptr == '\r'))
3307 if ((ptr <= eptr) && (*ptr == '\n'))
3314 *Ptr = StrBufNOTNULL;
3317 return Buf->BufUsed - (ptr - Buf->buf);