From: Wilfried Göesgens Date: Sun, 13 Dec 2009 20:26:27 +0000 (+0000) Subject: * shuffle arround stuff a little bit X-Git-Tag: v7.86~567 X-Git-Url: https://code.citadel.org/?a=commitdiff_plain;h=17b1fb50274e441d3d0d70b587e36ea8e8475ff6;p=citadel.git * shuffle arround stuff a little bit * move debug stuff into own functions so its a little cleaner --- diff --git a/libcitadel/lib/stringbuf.c b/libcitadel/lib/stringbuf.c index c41be76fc..ba0b94e18 100644 --- a/libcitadel/lib/stringbuf.c +++ b/libcitadel/lib/stringbuf.c @@ -155,6 +155,64 @@ static void StrBufBacktrace(StrBuf *Buf, int which) } #endif + +void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere) +{ + if (hFreeDbglog == -1){ + pid_t pid = getpid(); + char path [SIZ]; + snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid); + hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY); + } + if ((*FreeMe)->nIncreases > 0) + { + char buf[SIZ * 3]; + long n; + n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n", + FromWhere, + (*FreeMe)->nIncreases, + (*FreeMe)->BufUsed, + (*FreeMe)->BufSize, + (*FreeMe)->bt, + (*FreeMe)->bt_lastinc); + n = write(hFreeDbglog, buf, n); + } + else + { + char buf[128]; + long n; + n = snprintf(buf, 128, "%c_|0|%ld%ld|\n", + FromWhere, + (*FreeMe)->BufUsed, + (*FreeMe)->BufSize); + n = write(hFreeDbglog, buf, n); + } +} + +void dbg_IncreaseBuf(StrBuf *IncMe) +{ + Buf->nIncreases++; +#ifdef HAVE_BACKTRACE + StrBufBacktrace(Buf, 1); +#endif +} + +void dbg_Init(StrBuf *Buf) +{ + Buf->nIncreases = 0; + Buf->bt[0] = '\0'; + Buf->bt_lastinc[0] = '\0'; +#ifdef HAVE_BACKTRACE + StrBufBacktrace(Buf, 0); +#endif +} + +#else +/* void it... */ +#define dbg_FreeStrBuf(a, b) +#define dbg_IncreaseBuf(a) +#define dbg_Init(a) + #endif /** @@ -220,12 +278,9 @@ static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) free (Buf->buf); Buf->buf = NewBuf; Buf->BufSize = NewSize; -#ifdef SIZE_DEBUG - Buf->nIncreases++; -#ifdef HAVE_BACKTRACE - StrBufBacktrace(Buf, 1); -#endif -#endif + + dbg_IncreaseBuf(Buf); + return Buf->BufSize; } @@ -282,14 +337,9 @@ StrBuf* NewStrBuf(void) NewBuf->BufSize = BaseStrBufSize; NewBuf->BufUsed = 0; NewBuf->ConstBuf = 0; -#ifdef SIZE_DEBUG - NewBuf->nIncreases = 0; - NewBuf->bt[0] = '\0'; - NewBuf->bt_lastinc[0] = '\0'; -#ifdef HAVE_BACKTRACE - StrBufBacktrace(NewBuf, 0); -#endif -#endif + + dbg_Init (NewBuf); + return NewBuf; } @@ -312,14 +362,9 @@ StrBuf* NewStrBufDup(const StrBuf *CopyMe) NewBuf->BufUsed = CopyMe->BufUsed; NewBuf->BufSize = CopyMe->BufSize; NewBuf->ConstBuf = 0; -#ifdef SIZE_DEBUG - NewBuf->nIncreases = 0; - NewBuf->bt[0] = '\0'; - NewBuf->bt_lastinc[0] = '\0'; -#ifdef HAVE_BACKTRACE - StrBufBacktrace(NewBuf, 0); -#endif -#endif + + dbg_Init(NewBuf); + return NewBuf; } @@ -359,14 +404,9 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars) NewBuf->BufUsed = 0; } NewBuf->ConstBuf = 0; -#ifdef SIZE_DEBUG - NewBuf->nIncreases = 0; - NewBuf->bt[0] = '\0'; - NewBuf->bt_lastinc[0] = '\0'; -#ifdef HAVE_BACKTRACE - StrBufBacktrace(NewBuf, 0); -#endif -#endif + + dbg_Init(NewBuf) + return NewBuf; } @@ -416,11 +456,9 @@ StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant) NewBuf->BufSize = SizeOfStrConstant; NewBuf->BufUsed = SizeOfStrConstant; NewBuf->ConstBuf = 1; -#ifdef SIZE_DEBUG - NewBuf->nIncreases = 0; - NewBuf->bt[0] = '\0'; - NewBuf->bt_lastinc[0] = '\0'; -#endif + + dbg_Init(NewBuf); + return NewBuf; } @@ -473,35 +511,9 @@ void FreeStrBuf (StrBuf **FreeMe) { if (*FreeMe == NULL) return; -#ifdef SIZE_DEBUG - if (hFreeDbglog == -1){ - pid_t pid = getpid(); - char path [SIZ]; - snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid); - hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY); - } - if ((*FreeMe)->nIncreases > 0) - { - char buf[SIZ * 3]; - long n; - n = snprintf(buf, SIZ * 3, "+|%ld|%ld|%ld|%s|%s|\n", - (*FreeMe)->nIncreases, - (*FreeMe)->BufUsed, - (*FreeMe)->BufSize, - (*FreeMe)->bt, - (*FreeMe)->bt_lastinc); - n = write(hFreeDbglog, buf, n); - } - else - { - char buf[128]; - long n; - n = snprintf(buf, 128, "_|0|%ld%ld|\n", - (*FreeMe)->BufUsed, - (*FreeMe)->BufSize); - n = write(hFreeDbglog, buf, n); - } -#endif + + dbg_FreeStrBuf(FreeMe, 'F'); + if (!(*FreeMe)->ConstBuf) free((*FreeMe)->buf); free(*FreeMe); @@ -523,35 +535,9 @@ char *SmashStrBuf (StrBuf **SmashMe) if ((SmashMe == NULL) || (*SmashMe == NULL)) return NULL; -#ifdef SIZE_DEBUG - if (hFreeDbglog == -1){ - pid_t pid = getpid(); - char path [SIZ]; - snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid); - hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY); - } - if ((*SmashMe)->nIncreases > 0) - { - char buf[SIZ * 3]; - long n; - n = snprintf(buf, SIZ * 3, "S+|%ld|%ld|%ld|%s|%s|\n", - (*SmashMe)->nIncreases, - (*SmashMe)->BufUsed, - (*SmashMe)->BufSize, - (*SmashMe)->bt, - (*SmashMe)->bt_lastinc); - n = write(hFreeDbglog, buf, n); - } - else - { - char buf[128]; - long n; - n = snprintf(buf, 128, "S_|0|%ld%ld|\n", - (*SmashMe)->BufUsed, - (*SmashMe)->BufSize); - n = write(hFreeDbglog, buf, n); - } -#endif + + dbg_FreeStrBuf(SmashMe, 'S'); + Ret = (*SmashMe)->buf; free(*SmashMe); *SmashMe = NULL; @@ -569,40 +555,19 @@ void HFreeStrBuf (void *VFreeMe) StrBuf *FreeMe = (StrBuf*)VFreeMe; if (FreeMe == NULL) return; -#ifdef SIZE_DEBUG - if (hFreeDbglog == -1){ - pid_t pid = getpid(); - char path [SIZ]; - snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid); - hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY); - } - if (FreeMe->nIncreases > 0) - { - char buf[SIZ * 3]; - long n; - n = snprintf(buf, SIZ * 3, "+|%ld|%ld|%ld|%s|%s|\n", - FreeMe->nIncreases, - FreeMe->BufUsed, - FreeMe->BufSize, - FreeMe->bt, - FreeMe->bt_lastinc); - write(hFreeDbglog, buf, n); - } - else - { - char buf[128]; - long n; - n = snprintf(buf, 128, "_|%ld|%ld%ld|\n", - FreeMe->nIncreases, - FreeMe->BufUsed, - FreeMe->BufSize); - } -#endif + + dbg_FreeStrBuf(SmashMe, 'H'); + if (!FreeMe->ConstBuf) free(FreeMe->buf); free(FreeMe); } + +/******************************************************************************* + * Simple string transformations * + *******************************************************************************/ + /** * @ingroup StrBuf * @brief Wrapper around atol @@ -724,6 +689,118 @@ void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, u Buf->buf[Buf->BufUsed] = '\0'; } +/** + * @ingroup StrBuf + * @brief sprintf like function appending the formated string to the buffer + * vsnprintf version to wrap into own calls + * @param Buf Buffer to extend by format and Params + * @param format printf alike format to add + * @param ap va_list containing the items for format + */ +void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap) +{ + va_list apl; + size_t BufSize; + size_t nWritten; + size_t Offset; + size_t newused; + + if ((Buf == NULL) || (format == NULL)) + return; + + BufSize = Buf->BufSize; + nWritten = Buf->BufSize + 1; + Offset = Buf->BufUsed; + newused = Offset + nWritten; + + while (newused >= BufSize) { + va_copy(apl, ap); + nWritten = vsnprintf(Buf->buf + Offset, + Buf->BufSize - Offset, + format, apl); + va_end(apl); + newused = Offset + nWritten; + if (newused >= Buf->BufSize) { + IncreaseBuf(Buf, 1, newused); + newused = Buf->BufSize + 1; + } + else { + Buf->BufUsed = Offset + nWritten; + BufSize = Buf->BufSize; + } + + } +} + +/** + * @ingroup StrBuf + * @brief sprintf like function appending the formated string to the buffer + * @param Buf Buffer to extend by format and Params + * @param format printf alike format to add + */ +void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...) +{ + size_t BufSize; + size_t nWritten; + size_t Offset; + size_t newused; + va_list arg_ptr; + + if ((Buf == NULL) || (format == NULL)) + return; + + BufSize = Buf->BufSize; + nWritten = Buf->BufSize + 1; + Offset = Buf->BufUsed; + newused = Offset + nWritten; + + while (newused >= BufSize) { + va_start(arg_ptr, format); + nWritten = vsnprintf(Buf->buf + Buf->BufUsed, + Buf->BufSize - Buf->BufUsed, + format, arg_ptr); + va_end(arg_ptr); + newused = Buf->BufUsed + nWritten; + if (newused >= Buf->BufSize) { + IncreaseBuf(Buf, 1, newused); + newused = Buf->BufSize + 1; + } + else { + Buf->BufUsed += nWritten; + BufSize = Buf->BufSize; + } + + } +} + +/** + * @ingroup StrBuf + * @brief sprintf like function putting the formated string into the buffer + * @param Buf Buffer to extend by format and Parameters + * @param format printf alike format to add + */ +void StrBufPrintf(StrBuf *Buf, const char *format, ...) +{ + size_t nWritten; + va_list arg_ptr; + + if ((Buf == NULL) || (format == NULL)) + return; + + nWritten = Buf->BufSize + 1; + while (nWritten >= Buf->BufSize) { + va_start(arg_ptr, format); + nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr); + va_end(arg_ptr); + if (nWritten >= Buf->BufSize) { + IncreaseBuf(Buf, 0, 0); + nWritten = Buf->BufSize + 1; + continue; + } + Buf->BufUsed = nWritten ; + } +} + /** * @ingroup StrBuf * @brief Callback for cURL to append the webserver reply to a buffer @@ -746,986 +823,475 @@ size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *strea } -/** - * @ingroup StrBuf_DeEnCoder - * @brief Escape a string for feeding out as a URL while appending it to a Buffer - * @param OutBuf the output buffer - * @param In Buffer to encode - * @param PlainIn way in from plain old c strings +/** + * @ingroup StrBuf + * @brief extracts a substring from Source into dest + * @param dest buffer to place substring into + * @param Source string to copy substring from + * @param Offset chars to skip from start + * @param nChars number of chars to copy + * @returns the number of chars copied; may be different from nChars due to the size of Source */ -void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) +int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars) { - const char *pch, *pche; - char *pt, *pte; - int len; - - if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) ) - return; - if (PlainIn != NULL) { - len = strlen(PlainIn); - pch = PlainIn; - pche = pch + len; + size_t NCharsRemain; + if (Offset > Source->BufUsed) + { + FlushStrBuf(dest); + return 0; } - else { - pch = In->buf; - pche = pch + In->BufUsed; - len = In->BufUsed; + if (Offset + nChars < Source->BufUsed) + { + if (nChars >= dest->BufSize) + IncreaseBuf(dest, 0, nChars + 1); + memcpy(dest->buf, Source->buf + Offset, nChars); + dest->BufUsed = nChars; + dest->buf[dest->BufUsed] = '\0'; + return nChars; } + NCharsRemain = Source->BufUsed - Offset; + if (NCharsRemain >= dest->BufSize) + IncreaseBuf(dest, 0, NCharsRemain + 1); + memcpy(dest->buf, Source->buf + Offset, NCharsRemain); + dest->BufUsed = NCharsRemain; + dest->buf[dest->BufUsed] = '\0'; + return NCharsRemain; +} - if (len == 0) +/** + * @ingroup StrBuf + * @brief Cut nChars from the start of the string + * @param Buf Buffer to modify + * @param nChars how many chars should be skipped? + */ +void StrBufCutLeft(StrBuf *Buf, int nChars) +{ + if (nChars >= Buf->BufUsed) { + FlushStrBuf(Buf); return; - - pt = OutBuf->buf + OutBuf->BufUsed; - pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */ - - while (pch < pche) { - if (pt >= pte) { - IncreaseBuf(OutBuf, 1, -1); - pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */ - pt = OutBuf->buf + OutBuf->BufUsed; - } - - if((*pch >= 'a' && *pch <= 'z') || - (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */ - (*pch >= '0' && *pch <= ':') || /* 0-9 : */ - (*pch == '!') || (*pch == '_') || - (*pch == ',') || (*pch == '.') || - (*pch == ',')) - { - *(pt++) = *(pch++); - OutBuf->BufUsed++; - } - else { - *pt = '%'; - *(pt + 1) = HexList[(unsigned char)*pch][0]; - *(pt + 2) = HexList[(unsigned char)*pch][1]; - pt += 3; - OutBuf->BufUsed += 3; - pch ++; - } } - *pt = '\0'; + memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars); + Buf->BufUsed -= nChars; + Buf->buf[Buf->BufUsed] = '\0'; } /** - * @ingroup StrBuf_DeEnCoder - * @brief Append a string, escaping characters which have meaning in HTML. - * - * @param Target target buffer - * @param Source source buffer; set to NULL if you just have a C-String - * @param PlainIn Plain-C string to append; set to NULL if unused - * @param nbsp If nonzero, spaces are converted to non-breaking spaces. - * @param nolinebreaks if set to 1, linebreaks are removed from the string. - * if set to 2, linebreaks are replaced by <br/> + * @ingroup StrBuf + * @brief Cut the trailing n Chars from the string + * @param Buf Buffer to modify + * @param nChars how many chars should be trunkated? */ -long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks) +void StrBufCutRight(StrBuf *Buf, int nChars) { - const char *aptr, *eiptr; - char *bptr, *eptr; - long len; - - if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) - return -1; - - if (PlainIn != NULL) { - aptr = PlainIn; - len = strlen(PlainIn); - eiptr = aptr + len; + if (nChars >= Buf->BufUsed) { + FlushStrBuf(Buf); + return; } - else { - aptr = Source->buf; - eiptr = aptr + Source->BufUsed; - len = Source->BufUsed; + Buf->BufUsed -= nChars; + Buf->buf[Buf->BufUsed] = '\0'; +} + +/** + * @ingroup StrBuf + * @brief Cut the string after n Chars + * @param Buf Buffer to modify + * @param AfternChars after how many chars should we trunkate the string? + * @param At if non-null and points inside of our string, cut it there. + */ +void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At) +{ + if (At != NULL){ + AfternChars = At - Buf->buf; } - if (len == 0) - return -1; + if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed)) + return; + Buf->BufUsed = AfternChars; + Buf->buf[Buf->BufUsed] = '\0'; +} - bptr = Target->buf + Target->BufUsed; - eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ - while (aptr < eiptr){ - if(bptr >= eptr) { - IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ - bptr = Target->buf + Target->BufUsed; - } - if (*aptr == '<') { - memcpy(bptr, "<", 4); - bptr += 4; - Target->BufUsed += 4; - } - else if (*aptr == '>') { - memcpy(bptr, ">", 4); - bptr += 4; - Target->BufUsed += 4; - } - else if (*aptr == '&') { - memcpy(bptr, "&", 5); - bptr += 5; - Target->BufUsed += 5; - } - else if (*aptr == '"') { - memcpy(bptr, """, 6); - bptr += 6; - Target->BufUsed += 6; - } - else if (*aptr == '\'') { - memcpy(bptr, "'", 5); - bptr += 5; - Target->BufUsed += 5; - } - else if (*aptr == LB) { - *bptr = '<'; - bptr ++; - Target->BufUsed ++; - } - else if (*aptr == RB) { - *bptr = '>'; - bptr ++; - Target->BufUsed ++; - } - else if (*aptr == QU) { - *bptr ='"'; - bptr ++; - Target->BufUsed ++; - } - else if ((*aptr == 32) && (nbsp == 1)) { - memcpy(bptr, " ", 6); - bptr += 6; - Target->BufUsed += 6; - } - else if ((*aptr == '\n') && (nolinebreaks == 1)) { - *bptr='\0'; /* nothing */ - } - else if ((*aptr == '\n') && (nolinebreaks == 2)) { - memcpy(bptr, "<br/>", 11); - bptr += 11; - Target->BufUsed += 11; - } +/** + * @ingroup StrBuf + * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length. + * @param Buf the string to modify + */ +void StrBufTrim(StrBuf *Buf) +{ + int delta = 0; + if ((Buf == NULL) || (Buf->BufUsed == 0)) return; + while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){ + delta ++; + } + if (delta > 0) StrBufCutLeft(Buf, delta); - else if ((*aptr == '\r') && (nolinebreaks != 0)) { - *bptr='\0'; /* nothing */ - } - else{ - *bptr = *aptr; - bptr++; - Target->BufUsed ++; - } - aptr ++; + if (Buf->BufUsed == 0) return; + while (isspace(Buf->buf[Buf->BufUsed - 1])){ + Buf->BufUsed --; } - *bptr = '\0'; - if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) ) - return -1; - return Target->BufUsed; + Buf->buf[Buf->BufUsed] = '\0'; } /** - * @ingroup StrBuf_DeEnCoder - * @brief Append a string, escaping characters which have meaning in HTML. - * Converts linebreaks into blanks; escapes single quotes - * @param Target target buffer - * @param Source source buffer; set to NULL if you just have a C-String - * @param PlainIn Plain-C string to append; set to NULL if unused + * @ingroup StrBuf + * @brief uppercase the contents of a buffer + * @param Buf the buffer to translate */ -void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn) +void StrBufUpCase(StrBuf *Buf) { - const char *aptr, *eiptr; - char *tptr, *eptr; - long len; + char *pch, *pche; - if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) - return ; - - if (PlainIn != NULL) { - aptr = PlainIn; - len = strlen(PlainIn); - eiptr = aptr + len; - } - else { - aptr = Source->buf; - eiptr = aptr + Source->BufUsed; - len = Source->BufUsed; + pch = Buf->buf; + pche = pch + Buf->BufUsed; + while (pch < pche) { + *pch = toupper(*pch); + pch ++; } +} - if (len == 0) - return; - eptr = Target->buf + Target->BufSize - 8; - tptr = Target->buf + Target->BufUsed; - - while (aptr < eiptr){ - if(tptr >= eptr) { - IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 8; - tptr = Target->buf + Target->BufUsed; - } - - if (*aptr == '\n') { - *tptr = ' '; - Target->BufUsed++; - } - else if (*aptr == '\r') { - *tptr = ' '; - Target->BufUsed++; - } - else if (*aptr == '\'') { - *(tptr++) = '&'; - *(tptr++) = '#'; - *(tptr++) = '3'; - *(tptr++) = '9'; - *tptr = ';'; - Target->BufUsed += 5; - } else { - *tptr = *aptr; - Target->BufUsed++; - } - tptr++; aptr++; +/** + * @ingroup StrBuf + * @brief lowercase the contents of a buffer + * @param Buf the buffer to translate + */ +void StrBufLowerCase(StrBuf *Buf) +{ + char *pch, *pche; + + pch = Buf->buf; + pche = pch + Buf->BufUsed; + while (pch < pche) { + *pch = tolower(*pch); + pch ++; } - *tptr = '\0'; } +/******************************************************************************* + * a tokenizer that kills, maims, and destroys * + *******************************************************************************/ /** - * @ingroup StrBuf_DeEnCoder - * @brief Append a string, escaping characters which have meaning in ICAL. - * [\n,] - * @param Target target buffer - * @param Source source buffer; set to NULL if you just have a C-String - * @param PlainIn Plain-C string to append; set to NULL if unused + * @ingroup StrBuf_Tokenizer + * @brief Counts the numbmer of tokens in a buffer + * @param source String to count tokens in + * @param tok Tokenizer char to count + * @returns numbers of tokenizer chars found */ -void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn) +int StrBufNum_tokens(const StrBuf *source, char tok) { - const char *aptr, *eiptr; - char *tptr, *eptr; - long len; + if (source == NULL) + return 0; + return num_tokens(source->buf, tok); +} - if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) - return ; +/** + * @ingroup StrBuf_Tokenizer + * @brief a string tokenizer + * @param Source StringBuffer to read into + * @param parmnum n'th Parameter to remove + * @param separator tokenizer character + * @returns -1 if not found, else length of token. + */ +int StrBufRemove_token(StrBuf *Source, int parmnum, char separator) +{ + int ReducedBy; + char *d, *s, *end; /* dest, source */ + int count = 0; - if (PlainIn != NULL) { - aptr = PlainIn; - len = strlen(PlainIn); - eiptr = aptr + len; - } - else { - aptr = Source->buf; - eiptr = aptr + Source->BufUsed; - len = Source->BufUsed; + /* Find desired @parameter */ + end = Source->buf + Source->BufUsed; + d = Source->buf; + while ((d <= end) && + (count < parmnum)) + { + /* End of string, bail! */ + if (!*d) { + d = NULL; + break; + } + if (*d == separator) { + count++; + } + d++; } + if ((d == NULL) || (d >= end)) + return 0; /* @Parameter not found */ - if (len == 0) - return; + /* Find next @parameter */ + s = d; + while ((s <= end) && + (*s && *s != separator)) + { + s++; + } + if (*s == separator) + s++; + ReducedBy = d - s; - eptr = Target->buf + Target->BufSize - 8; - tptr = Target->buf + Target->BufUsed; - - while (aptr < eiptr){ - if(tptr + 3 >= eptr) { - IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 8; - tptr = Target->buf + Target->BufUsed; - } - - if (*aptr == '\n') { - *tptr = '\\'; - Target->BufUsed++; - tptr++; - *tptr = 'n'; - Target->BufUsed++; - } - else if (*aptr == '\r') { - *tptr = '\\'; - Target->BufUsed++; - tptr++; - *tptr = 'r'; - Target->BufUsed++; - } - else if (*aptr == ',') { - *tptr = '\\'; - Target->BufUsed++; - tptr++; - *tptr = ','; - Target->BufUsed++; - } else { - *tptr = *aptr; - Target->BufUsed++; - } - tptr++; aptr++; + /* Hack and slash */ + if (s >= end) { + return 0; } - *tptr = '\0'; + else if (*s) { + memmove(d, s, Source->BufUsed - (s - Source->buf)); + Source->BufUsed += ReducedBy; + Source->buf[Source->BufUsed] = '\0'; + } + else if (d == Source->buf) { + *d = 0; + Source->BufUsed = 0; + } + else { + *--d = '\0'; + Source->BufUsed += ReducedBy; + } + /* + while (*s) { + *d++ = *s++; + } + *d = 0; + */ + return ReducedBy; } + /** - * @ingroup StrBuf_DeEnCoder - * @brief Append a string, escaping characters which have meaning in JavaScript strings . - * - * @param Target target buffer - * @param Source source buffer; set to NULL if you just have a C-String - * @param PlainIn Plain-C string to append; set to NULL if unused - * @returns size of result or -1 + * @ingroup StrBuf_Tokenizer + * @brief a string tokenizer + * @param dest Destination StringBuffer + * @param Source StringBuffer to read into + * @param parmnum n'th Parameter to extract + * @param separator tokenizer character + * @returns -1 if not found, else length of token. */ -long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn) +int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator) { - const char *aptr, *eiptr; - char *bptr, *eptr; - long len; - - if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) - return -1; - - if (PlainIn != NULL) { - aptr = PlainIn; - len = strlen(PlainIn); - eiptr = aptr + len; - } - else { - aptr = Source->buf; - eiptr = aptr + Source->BufUsed; - len = Source->BufUsed; + const char *s, *e; //* source * / + int len = 0; //* running total length of extracted string * / + int current_token = 0; //* token currently being processed * / + + if (dest != NULL) { + dest->buf[0] = '\0'; + dest->BufUsed = 0; } + else + return(-1); - if (len == 0) - return -1; + if ((Source == NULL) || (Source->BufUsed ==0)) { + return(-1); + } + s = Source->buf; + e = s + Source->BufUsed; - bptr = Target->buf + Target->BufUsed; - eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in... */ + //cit_backtrace(); + //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source); - while (aptr < eiptr){ - if(bptr >= eptr) { - IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 3; - bptr = Target->buf + Target->BufUsed; - } - if (*aptr == '"') { - *bptr = '\\'; - bptr ++; - *bptr = '"'; - bptr ++; - Target->BufUsed += 2; - } else if (*aptr == '\\') { - *bptr = '\\'; - bptr ++; - *bptr = '\\'; - bptr ++; - Target->BufUsed += 2; - } - else{ - *bptr = *aptr; - bptr++; - Target->BufUsed ++; - } - aptr ++; - } - *bptr = '\0'; - if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) ) - return -1; - return Target->BufUsed; -} - -/** - * @ingroup StrBuf_DeEnCoder - * @brief Append a string, escaping characters which have meaning in HTML + json. - * - * @param Target target buffer - * @param Source source buffer; set to NULL if you just have a C-String - * @param PlainIn Plain-C string to append; set to NULL if unused - * @param nbsp If nonzero, spaces are converted to non-breaking spaces. - * @param nolinebreaks if set to 1, linebreaks are removed from the string. - * if set to 2, linebreaks are replaced by <br/> - */ -long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks) -{ - const char *aptr, *eiptr; - char *bptr, *eptr; - long len; - int IsUtf8Sequence = 0; - - if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) - return -1; - - if (PlainIn != NULL) { - aptr = PlainIn; - len = strlen(PlainIn); - eiptr = aptr + len; - } - else { - aptr = Source->buf; - eiptr = aptr + Source->BufUsed; - len = Source->BufUsed; - } - - if (len == 0) - return -1; - - bptr = Target->buf + Target->BufUsed; - eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ - - while (aptr < eiptr){ - if(bptr >= eptr) { - IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ - bptr = Target->buf + Target->BufUsed; - } - if (*aptr == '<') { - memcpy(bptr, "<", 4); - bptr += 4; - Target->BufUsed += 4; - } - else if (*aptr == '>') { - memcpy(bptr, ">", 4); - bptr += 4; - Target->BufUsed += 4; - } - else if (*aptr == '&') { - memcpy(bptr, "&", 5); - bptr += 5; - Target->BufUsed += 5; - } - else if (*aptr == LB) { - *bptr = '<'; - bptr ++; - Target->BufUsed ++; - } - else if (*aptr == RB) { - *bptr = '>'; - bptr ++; - Target->BufUsed ++; + while ((sBufUsed += 6; + if (len >= dest->BufSize) { + dest->BufUsed = len; + if (IncreaseBuf(dest, 1, -1) < 0) { + dest->BufUsed --; + break; + } } - else if ((*aptr == '\n') && (nolinebreaks == 1)) { - *bptr='\0'; /* nothing */ + if ( (current_token == parmnum) && + (*s != separator)) { + dest->buf[len] = *s; + ++len; } - else if ((*aptr == '\n') && (nolinebreaks == 2)) { - memcpy(bptr, "<br/>", 11); - bptr += 11; - Target->BufUsed += 11; + else if (current_token > parmnum) { + break; } + ++s; + } + + dest->buf[len] = '\0'; + dest->BufUsed = len; + + if (current_token < parmnum) { + //lprintf (CTDL_DEBUG,"test BufUsed += 2; - } else if (*aptr == '\\') { - *bptr = '\\'; - bptr ++; - *bptr = '\\'; - bptr ++; - Target->BufUsed += 2; - } - else { - if (((unsigned char)*aptr) >= 0x20) - { - IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr); - - *bptr = *aptr; - Target->BufUsed ++; - while (IsUtf8Sequence > 1){ - if(bptr + IsUtf8Sequence >= eptr) { - IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ - bptr = Target->buf + Target->BufUsed - 1; - } - bptr++; aptr++; - IsUtf8Sequence --; - *bptr = *aptr; - Target->BufUsed ++; - } - bptr++; - } - } - aptr ++; - } - *bptr = '\0'; - if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) ) - return -1; - return Target->BufUsed; -} /** - * @ingroup StrBuf - * @brief extracts a substring from Source into dest - * @param dest buffer to place substring into - * @param Source string to copy substring from - * @param Offset chars to skip from start - * @param nChars number of chars to copy - * @returns the number of chars copied; may be different from nChars due to the size of Source + * @ingroup StrBuf_Tokenizer + * @brief a string tokenizer to fetch an integer + * @param Source String containing tokens + * @param parmnum n'th Parameter to extract + * @param separator tokenizer character + * @returns 0 if not found, else integer representation of the token */ -int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars) +int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator) { - size_t NCharsRemain; - if (Offset > Source->BufUsed) - { - FlushStrBuf(dest); + StrBuf tmp; + char buf[64]; + + tmp.buf = buf; + buf[0] = '\0'; + tmp.BufSize = 64; + tmp.BufUsed = 0; + tmp.ConstBuf = 1; + if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) + return(atoi(buf)); + else return 0; - } - if (Offset + nChars < Source->BufUsed) - { - if (nChars >= dest->BufSize) - IncreaseBuf(dest, 0, nChars + 1); - memcpy(dest->buf, Source->buf + Offset, nChars); - dest->BufUsed = nChars; - dest->buf[dest->BufUsed] = '\0'; - return nChars; - } - NCharsRemain = Source->BufUsed - Offset; - if (NCharsRemain >= dest->BufSize) - IncreaseBuf(dest, 0, NCharsRemain + 1); - memcpy(dest->buf, Source->buf + Offset, NCharsRemain); - dest->BufUsed = NCharsRemain; - dest->buf[dest->BufUsed] = '\0'; - return NCharsRemain; } /** - * @ingroup StrBuf - * @brief sprintf like function appending the formated string to the buffer - * vsnprintf version to wrap into own calls - * @param Buf Buffer to extend by format and Params - * @param format printf alike format to add - * @param ap va_list containing the items for format + * @ingroup StrBuf_Tokenizer + * @brief a string tokenizer to fetch a long integer + * @param Source String containing tokens + * @param parmnum n'th Parameter to extract + * @param separator tokenizer character + * @returns 0 if not found, else long integer representation of the token */ -void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap) +long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator) { - va_list apl; - size_t BufSize; - size_t nWritten; - size_t Offset; - size_t newused; - - if ((Buf == NULL) || (format == NULL)) - return; - - BufSize = Buf->BufSize; - nWritten = Buf->BufSize + 1; - Offset = Buf->BufUsed; - newused = Offset + nWritten; + StrBuf tmp; + char buf[64]; - while (newused >= BufSize) { - va_copy(apl, ap); - nWritten = vsnprintf(Buf->buf + Offset, - Buf->BufSize - Offset, - format, apl); - va_end(apl); - newused = Offset + nWritten; - if (newused >= Buf->BufSize) { - IncreaseBuf(Buf, 1, newused); - newused = Buf->BufSize + 1; - } - else { - Buf->BufUsed = Offset + nWritten; - BufSize = Buf->BufSize; - } - - } + tmp.buf = buf; + buf[0] = '\0'; + tmp.BufSize = 64; + tmp.BufUsed = 0; + tmp.ConstBuf = 1; + if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) + return(atoi(buf)); + else + return 0; } -/** - * @ingroup StrBuf - * @brief sprintf like function appending the formated string to the buffer - * @param Buf Buffer to extend by format and Params - * @param format printf alike format to add - */ -void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...) -{ - size_t BufSize; - size_t nWritten; - size_t Offset; - size_t newused; - va_list arg_ptr; - - if ((Buf == NULL) || (format == NULL)) - return; - - BufSize = Buf->BufSize; - nWritten = Buf->BufSize + 1; - Offset = Buf->BufUsed; - newused = Offset + nWritten; - - while (newused >= BufSize) { - va_start(arg_ptr, format); - nWritten = vsnprintf(Buf->buf + Buf->BufUsed, - Buf->BufSize - Buf->BufUsed, - format, arg_ptr); - va_end(arg_ptr); - newused = Buf->BufUsed + nWritten; - if (newused >= Buf->BufSize) { - IncreaseBuf(Buf, 1, newused); - newused = Buf->BufSize + 1; - } - else { - Buf->BufUsed += nWritten; - BufSize = Buf->BufSize; - } - - } -} /** - * @ingroup StrBuf - * @brief sprintf like function putting the formated string into the buffer - * @param Buf Buffer to extend by format and Parameters - * @param format printf alike format to add + * @ingroup StrBuf_Tokenizer + * @brief a string tokenizer to fetch an unsigned long + * @param Source String containing tokens + * @param parmnum n'th Parameter to extract + * @param separator tokenizer character + * @returns 0 if not found, else unsigned long representation of the token */ -void StrBufPrintf(StrBuf *Buf, const char *format, ...) +unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator) { - size_t nWritten; - va_list arg_ptr; + StrBuf tmp; + char buf[64]; + char *pnum; - if ((Buf == NULL) || (format == NULL)) - return; - - nWritten = Buf->BufSize + 1; - while (nWritten >= Buf->BufSize) { - va_start(arg_ptr, format); - nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr); - va_end(arg_ptr); - if (nWritten >= Buf->BufSize) { - IncreaseBuf(Buf, 0, 0); - nWritten = Buf->BufSize + 1; - continue; - } - Buf->BufUsed = nWritten ; + tmp.buf = buf; + buf[0] = '\0'; + tmp.BufSize = 64; + tmp.BufUsed = 0; + tmp.ConstBuf = 1; + if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) { + pnum = &buf[0]; + if (*pnum == '-') + pnum ++; + return (unsigned long) atol(pnum); } + else + return 0; } -/** - * @ingroup StrBuf_Tokenizer - * @brief Counts the numbmer of tokens in a buffer - * @param source String to count tokens in - * @param tok Tokenizer char to count - * @returns numbers of tokenizer chars found - */ -int StrBufNum_tokens(const StrBuf *source, char tok) -{ - if (source == NULL) - return 0; - return num_tokens(source->buf, tok); -} -/* - * remove_token() - a tokenizer that kills, maims, and destroys - */ /** - * @ingroup StrBuf_Tokenizer - * @brief a string tokenizer - * @param Source StringBuffer to read into - * @param parmnum n'th Parameter to remove - * @param separator tokenizer character - * @returns -1 if not found, else length of token. + * @ingroup StrBuf_NextTokenizer + * @brief a string tokenizer; Bounds checker + * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string. + * @param Source our tokenbuffer + * @param pStart the token iterator pointer to inspect + * @returns whether the revolving pointer is inside of the search range */ -int StrBufRemove_token(StrBuf *Source, int parmnum, char separator) +int StrBufHaveNextToken(const StrBuf *Source, const char **pStart) { - int ReducedBy; - char *d, *s, *end; /* dest, source */ - int count = 0; - - /* Find desired @parameter */ - end = Source->buf + Source->BufUsed; - d = Source->buf; - while ((d <= end) && - (count < parmnum)) + if ((Source == NULL) || + (*pStart == StrBufNOTNULL) || + (Source->BufUsed == 0)) { - /* End of string, bail! */ - if (!*d) { - d = NULL; - break; - } - if (*d == separator) { - count++; - } - d++; + return 0; } - if ((d == NULL) || (d >= end)) - return 0; /* @Parameter not found */ - - /* Find next @parameter */ - s = d; - while ((s <= end) && - (*s && *s != separator)) + if (*pStart == NULL) { - s++; + return 1; } - if (*s == separator) - s++; - ReducedBy = d - s; - - /* Hack and slash */ - if (s >= end) { + else if (*pStart > Source->buf + Source->BufUsed) + { return 0; } - else if (*s) { - memmove(d, s, Source->BufUsed - (s - Source->buf)); - Source->BufUsed += ReducedBy; - Source->buf[Source->BufUsed] = '\0'; - } - else if (d == Source->buf) { - *d = 0; - Source->BufUsed = 0; - } - else { - *--d = '\0'; - Source->BufUsed += ReducedBy; - } - /* - while (*s) { - *d++ = *s++; + else if (*pStart <= Source->buf) + { + return 0; } - *d = 0; - */ - return ReducedBy; -} + return 1; +} /** - * @ingroup StrBuf_Tokenizer + * @ingroup StrBuf_NextTokenizer * @brief a string tokenizer * @param dest Destination StringBuffer * @param Source StringBuffer to read into - * @param parmnum n'th Parameter to extract - * @param separator tokenizer character + * @param pStart pointer to the end of the last token. Feed with NULL on start. + * @param separator tokenizer * @returns -1 if not found, else length of token. */ -int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator) +int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator) { - const char *s, *e; //* source * / - int len = 0; //* running total length of extracted string * / - int current_token = 0; //* token currently being processed * / + const char *s; /* source */ + const char *EndBuffer; /* end stop of source buffer */ + int current_token = 0; /* token currently being processed */ + int len = 0; /* running total length of extracted string */ + + if ((Source == NULL) || + (Source->BufUsed == 0) ) + { + *pStart = StrBufNOTNULL; + return -1; + } - if (dest != NULL) { + EndBuffer = Source->buf + Source->BufUsed; + + if (dest != NULL) + { dest->buf[0] = '\0'; dest->BufUsed = 0; } else - return(-1); - - if ((Source == NULL) || (Source->BufUsed ==0)) { - return(-1); + { + *pStart = EndBuffer + 1; + return -1; } - s = Source->buf; - e = s + Source->BufUsed; - //cit_backtrace(); - //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source); + if (*pStart == NULL) + { + *pStart = Source->buf; /* we're starting to examine this buffer. */ + } + else if ((*pStart < Source->buf) || + (*pStart > EndBuffer ) ) + { + return -1; /* no more tokens to find. */ + } - while ((s= dest->BufSize) { - dest->BufUsed = len; - if (IncreaseBuf(dest, 1, -1) < 0) { - dest->BufUsed --; - break; - } - } - if ( (current_token == parmnum) && - (*s != separator)) { - dest->buf[len] = *s; - ++len; - } - else if (current_token > parmnum) { - break; - } - ++s; - } - - dest->buf[len] = '\0'; - dest->BufUsed = len; - - if (current_token < parmnum) { - //lprintf (CTDL_DEBUG,"test 0) - return(atoi(buf)); - else - return 0; -} - -/** - * @ingroup StrBuf_Tokenizer - * @brief a string tokenizer to fetch a long integer - * @param Source String containing tokens - * @param parmnum n'th Parameter to extract - * @param separator tokenizer character - * @returns 0 if not found, else long integer representation of the token - */ -long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator) -{ - StrBuf tmp; - char buf[64]; - - tmp.buf = buf; - buf[0] = '\0'; - tmp.BufSize = 64; - tmp.BufUsed = 0; - tmp.ConstBuf = 1; - if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) - return(atoi(buf)); - else - return 0; -} - - -/** - * @ingroup StrBuf_Tokenizer - * @brief a string tokenizer to fetch an unsigned long - * @param Source String containing tokens - * @param parmnum n'th Parameter to extract - * @param separator tokenizer character - * @returns 0 if not found, else unsigned long representation of the token - */ -unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator) -{ - StrBuf tmp; - char buf[64]; - char *pnum; - - tmp.buf = buf; - buf[0] = '\0'; - tmp.BufSize = 64; - tmp.BufUsed = 0; - tmp.ConstBuf = 1; - if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) { - pnum = &buf[0]; - if (*pnum == '-') - pnum ++; - return (unsigned long) atol(pnum); - } - else - return 0; -} - - - -/** - * @ingroup StrBuf_NextTokenizer - * @brief a string tokenizer; Bounds checker - * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string. - * @param Source our tokenbuffer - * @param pStart the token iterator pointer to inspect - * @returns whether the revolving pointer is inside of the search range - */ -int StrBufHaveNextToken(const StrBuf *Source, const char **pStart) -{ - if ((Source == NULL) || - (*pStart == StrBufNOTNULL) || - (Source->BufUsed == 0)) - { - return 0; - } - if (*pStart == NULL) - { - return 1; - } - else if (*pStart > Source->buf + Source->BufUsed) - { - return 0; - } - else if (*pStart <= Source->buf) - { - return 0; - } - - return 1; -} - -/** - * @ingroup StrBuf_NextTokenizer - * @brief a string tokenizer - * @param dest Destination StringBuffer - * @param Source StringBuffer to read into - * @param pStart pointer to the end of the last token. Feed with NULL on start. - * @param separator tokenizer - * @returns -1 if not found, else length of token. - */ -int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator) -{ - const char *s; /* source */ - const char *EndBuffer; /* end stop of source buffer */ - int current_token = 0; /* token currently being processed */ - int len = 0; /* running total length of extracted string */ - - if ((Source == NULL) || - (Source->BufUsed == 0) ) - { - *pStart = StrBufNOTNULL; - return -1; - } - - EndBuffer = Source->buf + Source->BufUsed; - - if (dest != NULL) - { - dest->buf[0] = '\0'; - dest->BufUsed = 0; - } - else - { - *pStart = EndBuffer + 1; - return -1; - } - - if (*pStart == NULL) - { - *pStart = Source->buf; /* we're starting to examine this buffer. */ - } - else if ((*pStart < Source->buf) || - (*pStart > EndBuffer ) ) - { - return -1; /* no more tokens to find. */ - } - - s = *pStart; - /* start to find the next token */ - while ((s <= EndBuffer) && - (current_token == 0) ) - { - if (*s == separator) - { - /* we found the next token */ + s = *pStart; + /* start to find the next token */ + while ((s <= EndBuffer) && + (current_token == 0) ) + { + if (*s == separator) + { + /* we found the next token */ ++current_token; } @@ -1903,686 +1469,519 @@ unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char * -/** - * @ingroup StrBuf_IO - * @brief Read a line from socket - * flushes and closes the FD on error - * @param buf the buffer to get the input to - * @param fd pointer to the filedescriptor to read - * @param append Append to an existing string or replace? - * @param Error strerror() on error - * @returns numbers of chars read + + +/******************************************************************************* + * Escape Appending * + *******************************************************************************/ + +/** + * @ingroup StrBuf_DeEnCoder + * @brief Escape a string for feeding out as a URL while appending it to a Buffer + * @param OutBuf the output buffer + * @param In Buffer to encode + * @param PlainIn way in from plain old c strings */ -int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error) +void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) { - int len, rlen, slen; + const char *pch, *pche; + char *pt, *pte; + int len; + + if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) ) + return; + if (PlainIn != NULL) { + len = strlen(PlainIn); + pch = PlainIn; + pche = pch + len; + } + else { + pch = In->buf; + pche = pch + In->BufUsed; + len = In->BufUsed; + } - if (!append) - FlushStrBuf(buf); + if (len == 0) + return; - slen = len = buf->BufUsed; - while (1) { - rlen = read(*fd, &buf->buf[len], 1); - if (rlen < 1) { - *Error = strerror(errno); - - close(*fd); - *fd = -1; - - return -1; - } - if (buf->buf[len] == '\n') - break; - if (buf->buf[len] != '\r') - len ++; - if (len + 2 >= buf->BufSize) { - buf->BufUsed = len; - buf->buf[len+1] = '\0'; - IncreaseBuf(buf, 1, -1); + pt = OutBuf->buf + OutBuf->BufUsed; + pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */ + + while (pch < pche) { + if (pt >= pte) { + IncreaseBuf(OutBuf, 1, -1); + pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */ + pt = OutBuf->buf + OutBuf->BufUsed; + } + + if((*pch >= 'a' && *pch <= 'z') || + (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */ + (*pch >= '0' && *pch <= ':') || /* 0-9 : */ + (*pch == '!') || (*pch == '_') || + (*pch == ',') || (*pch == '.') || + (*pch == ',')) + { + *(pt++) = *(pch++); + OutBuf->BufUsed++; + } + else { + *pt = '%'; + *(pt + 1) = HexList[(unsigned char)*pch][0]; + *(pt + 2) = HexList[(unsigned char)*pch][1]; + pt += 3; + OutBuf->BufUsed += 3; + pch ++; } } - buf->BufUsed = len; - buf->buf[len] = '\0'; - return len - slen; + *pt = '\0'; } /** - * @ingroup StrBuf_BufferedIO - * @brief Read a line from socket - * flushes and closes the FD on error - * @param Line the line to read from the fd / I/O Buffer - * @param buf the buffer to get the input to - * @param fd pointer to the filedescriptor to read - * @param timeout number of successless selects until we bail out - * @param selectresolution how long to wait on each select - * @param Error strerror() on error - * @returns numbers of chars read + * @ingroup StrBuf_DeEnCoder + * @brief Append a string, escaping characters which have meaning in HTML. + * + * @param Target target buffer + * @param Source source buffer; set to NULL if you just have a C-String + * @param PlainIn Plain-C string to append; set to NULL if unused + * @param nbsp If nonzero, spaces are converted to non-breaking spaces. + * @param nolinebreaks if set to 1, linebreaks are removed from the string. + * if set to 2, linebreaks are replaced by <br/> */ -int StrBufTCP_read_buffered_line(StrBuf *Line, - StrBuf *buf, - int *fd, - int timeout, - int selectresolution, - const char **Error) +long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks) { - int len, rlen; - int nSuccessLess = 0; - fd_set rfds; - char *pch = NULL; - int fdflags; - int IsNonBlock; - struct timeval tv; + const char *aptr, *eiptr; + char *bptr, *eptr; + long len; - if (buf->BufUsed > 0) { - pch = strchr(buf->buf, '\n'); - if (pch != NULL) { - rlen = 0; - len = pch - buf->buf; - if (len > 0 && (*(pch - 1) == '\r') ) - rlen ++; - StrBufSub(Line, buf, 0, len - rlen); - StrBufCutLeft(buf, len + 1); - return len - rlen; - } + if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) + return -1; + + if (PlainIn != NULL) { + aptr = PlainIn; + len = strlen(PlainIn); + eiptr = aptr + len; + } + else { + aptr = Source->buf; + eiptr = aptr + Source->BufUsed; + len = Source->BufUsed; } - - if (buf->BufSize - buf->BufUsed < 10) - IncreaseBuf(buf, 1, -1); - fdflags = fcntl(*fd, F_GETFL); - IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; + if (len == 0) + return -1; - while ((nSuccessLess < timeout) && (pch == NULL)) { - if (IsNonBlock){ - tv.tv_sec = selectresolution; - tv.tv_usec = 0; - - FD_ZERO(&rfds); - FD_SET(*fd, &rfds); - if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) { - *Error = strerror(errno); - close (*fd); - *fd = -1; - return -1; - } + bptr = Target->buf + Target->BufUsed; + eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ + + while (aptr < eiptr){ + if(bptr >= eptr) { + IncreaseBuf(Target, 1, -1); + eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ + bptr = Target->buf + Target->BufUsed; } - if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) { - nSuccessLess ++; - continue; + if (*aptr == '<') { + memcpy(bptr, "<", 4); + bptr += 4; + Target->BufUsed += 4; } - rlen = read(*fd, - &buf->buf[buf->BufUsed], - buf->BufSize - buf->BufUsed - 1); - if (rlen < 1) { - *Error = strerror(errno); - close(*fd); - *fd = -1; - return -1; + else if (*aptr == '>') { + memcpy(bptr, ">", 4); + bptr += 4; + Target->BufUsed += 4; } - else if (rlen > 0) { - nSuccessLess = 0; - buf->BufUsed += rlen; - buf->buf[buf->BufUsed] = '\0'; - if (buf->BufUsed + 10 > buf->BufSize) { - IncreaseBuf(buf, 1, -1); - } - pch = strchr(buf->buf, '\n'); - continue; + else if (*aptr == '&') { + memcpy(bptr, "&", 5); + bptr += 5; + Target->BufUsed += 5; + } + else if (*aptr == '"') { + memcpy(bptr, """, 6); + bptr += 6; + Target->BufUsed += 6; + } + else if (*aptr == '\'') { + memcpy(bptr, "'", 5); + bptr += 5; + Target->BufUsed += 5; + } + else if (*aptr == LB) { + *bptr = '<'; + bptr ++; + Target->BufUsed ++; + } + else if (*aptr == RB) { + *bptr = '>'; + bptr ++; + Target->BufUsed ++; + } + else if (*aptr == QU) { + *bptr ='"'; + bptr ++; + Target->BufUsed ++; + } + else if ((*aptr == 32) && (nbsp == 1)) { + memcpy(bptr, " ", 6); + bptr += 6; + Target->BufUsed += 6; + } + else if ((*aptr == '\n') && (nolinebreaks == 1)) { + *bptr='\0'; /* nothing */ + } + else if ((*aptr == '\n') && (nolinebreaks == 2)) { + memcpy(bptr, "<br/>", 11); + bptr += 11; + Target->BufUsed += 11; } - - } - if (pch != NULL) { - rlen = 0; - len = pch - buf->buf; - if (len > 0 && (*(pch - 1) == '\r') ) - rlen ++; - StrBufSub(Line, buf, 0, len - rlen); - StrBufCutLeft(buf, len + 1); - return len - rlen; - } - return -1; + + else if ((*aptr == '\r') && (nolinebreaks != 0)) { + *bptr='\0'; /* nothing */ + } + else{ + *bptr = *aptr; + bptr++; + Target->BufUsed ++; + } + aptr ++; + } + *bptr = '\0'; + if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) ) + return -1; + return Target->BufUsed; } -static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor"; -static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason"; -static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer"; /** - * @ingroup StrBuf_BufferedIO - * @brief Read a line from socket - * flushes and closes the FD on error - * @param Line Line to read from the fd / I/O Buffer - * @param IOBuf the buffer to get the input to - * @param Pos pointer to the current read position, should be NULL initialized! - * @param fd pointer to the filedescriptor to read - * @param timeout number of successless selects until we bail out - * @param selectresolution how long to wait on each select - * @param Error strerror() on error - * @returns numbers of chars read + * @ingroup StrBuf_DeEnCoder + * @brief Append a string, escaping characters which have meaning in HTML. + * Converts linebreaks into blanks; escapes single quotes + * @param Target target buffer + * @param Source source buffer; set to NULL if you just have a C-String + * @param PlainIn Plain-C string to append; set to NULL if unused */ -int StrBufTCP_read_buffered_line_fast(StrBuf *Line, - StrBuf *IOBuf, - const char **Pos, - int *fd, - int timeout, - int selectresolution, - const char **Error) +void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn) { - const char *pche = NULL; - const char *pos = NULL; - int len, rlen; - int nSuccessLess = 0; - fd_set rfds; - const char *pch = NULL; - int fdflags; - int IsNonBlock; - struct timeval tv; - - if ((Line == NULL) || - (Pos == NULL) || - (IOBuf == NULL) || - (*fd == -1)) - { - if (Pos != NULL) - *Pos = NULL; - *Error = ErrRBLF_PreConditionFailed; - return -1; + const char *aptr, *eiptr; + char *tptr, *eptr; + long len; + + if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) + return ; + + if (PlainIn != NULL) { + aptr = PlainIn; + len = strlen(PlainIn); + eiptr = aptr + len; + } + else { + aptr = Source->buf; + eiptr = aptr + Source->BufUsed; + len = Source->BufUsed; } - pos = *Pos; - if ((IOBuf->BufUsed > 0) && - (pos != NULL) && - (pos < IOBuf->buf + IOBuf->BufUsed)) - { - pche = IOBuf->buf + IOBuf->BufUsed; - pch = pos; - while ((pch < pche) && (*pch != '\n')) - pch ++; - if ((pch >= pche) || (*pch == '\0')) - pch = NULL; - if ((pch != NULL) && - (pch <= pche)) - { - rlen = 0; - len = pch - pos; - if (len > 0 && (*(pch - 1) == '\r') ) - rlen ++; - StrBufSub(Line, IOBuf, (pos - IOBuf->buf), len - rlen); - *Pos = pch + 1; - return len - rlen; + if (len == 0) + return; + + eptr = Target->buf + Target->BufSize - 8; + tptr = Target->buf + Target->BufUsed; + + while (aptr < eiptr){ + if(tptr >= eptr) { + IncreaseBuf(Target, 1, -1); + eptr = Target->buf + Target->BufSize - 8; + tptr = Target->buf + Target->BufUsed; + } + + if (*aptr == '\n') { + *tptr = ' '; + Target->BufUsed++; + } + else if (*aptr == '\r') { + *tptr = ' '; + Target->BufUsed++; + } + else if (*aptr == '\'') { + *(tptr++) = '&'; + *(tptr++) = '#'; + *(tptr++) = '3'; + *(tptr++) = '9'; + *tptr = ';'; + Target->BufUsed += 5; + } else { + *tptr = *aptr; + Target->BufUsed++; } + tptr++; aptr++; } - - if (pos != NULL) { - if (pos > pche) - FlushStrBuf(IOBuf); - else - StrBufCutLeft(IOBuf, (pos - IOBuf->buf)); - *Pos = NULL; + *tptr = '\0'; +} + + + +/** + * @ingroup StrBuf_DeEnCoder + * @brief Append a string, escaping characters which have meaning in ICAL. + * [\n,] + * @param Target target buffer + * @param Source source buffer; set to NULL if you just have a C-String + * @param PlainIn Plain-C string to append; set to NULL if unused + */ +void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn) +{ + const char *aptr, *eiptr; + char *tptr, *eptr; + long len; + + if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) + return ; + + if (PlainIn != NULL) { + aptr = PlainIn; + len = strlen(PlainIn); + eiptr = aptr + len; } - - if (IOBuf->BufSize - IOBuf->BufUsed < 10) { - IncreaseBuf(IOBuf, 1, -1); - *Pos = NULL; + else { + aptr = Source->buf; + eiptr = aptr + Source->BufUsed; + len = Source->BufUsed; } - fdflags = fcntl(*fd, F_GETFL); - IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; + if (len == 0) + return; - pch = NULL; - while ((nSuccessLess < timeout) && - (pch == NULL) && - (*fd != -1)) { - if (IsNonBlock) - { - tv.tv_sec = 1; - tv.tv_usec = 0; - - FD_ZERO(&rfds); - FD_SET(*fd, &rfds); - if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) { - *Error = strerror(errno); - close (*fd); - *fd = -1; - if (*Error == NULL) - *Error = ErrRBLF_SelectFailed; - return -1; - } - if (! FD_ISSET(*fd, &rfds) != 0) { - nSuccessLess ++; - continue; - } + eptr = Target->buf + Target->BufSize - 8; + tptr = Target->buf + Target->BufUsed; + + while (aptr < eiptr){ + if(tptr + 3 >= eptr) { + IncreaseBuf(Target, 1, -1); + eptr = Target->buf + Target->BufSize - 8; + tptr = Target->buf + Target->BufUsed; } - rlen = read(*fd, - &IOBuf->buf[IOBuf->BufUsed], - IOBuf->BufSize - IOBuf->BufUsed - 1); - if (rlen < 1) { - *Error = strerror(errno); - close(*fd); - *fd = -1; - return -1; + + if (*aptr == '\n') { + *tptr = '\\'; + Target->BufUsed++; + tptr++; + *tptr = 'n'; + Target->BufUsed++; } - else if (rlen > 0) { - nSuccessLess = 0; - IOBuf->BufUsed += rlen; - IOBuf->buf[IOBuf->BufUsed] = '\0'; - if (IOBuf->BufUsed + 10 > IOBuf->BufSize) { - IncreaseBuf(IOBuf, 1, -1); - *Pos = NULL; - } - - pche = IOBuf->buf + IOBuf->BufUsed; - pch = IOBuf->buf; - while ((pch < pche) && (*pch != '\n')) - pch ++; - if ((pch >= pche) || (*pch == '\0')) - pch = NULL; - continue; + else if (*aptr == '\r') { + *tptr = '\\'; + Target->BufUsed++; + tptr++; + *tptr = 'r'; + Target->BufUsed++; } + else if (*aptr == ',') { + *tptr = '\\'; + Target->BufUsed++; + tptr++; + *tptr = ','; + Target->BufUsed++; + } else { + *tptr = *aptr; + Target->BufUsed++; + } + tptr++; aptr++; } - if (pch != NULL) { - pos = IOBuf->buf; - rlen = 0; - len = pch - pos; - if (len > 0 && (*(pch - 1) == '\r') ) - rlen ++; - StrBufSub(Line, IOBuf, 0, len - rlen); - *Pos = pos + len + 1; - return len - rlen; - } - *Error = ErrRBLF_NotEnoughSentFromServer; - return -1; - + *tptr = '\0'; } -static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor"; /** - * @ingroup StrBuf_IO - * @brief Input binary data from socket - * flushes and closes the FD on error - * @param Buf the buffer to get the input to - * @param fd pointer to the filedescriptor to read - * @param append Append to an existing string or replace? - * @param nBytes the maximal number of bytes to read - * @param Error strerror() on error - * @returns numbers of chars read + * @ingroup StrBuf_DeEnCoder + * @brief Append a string, escaping characters which have meaning in JavaScript strings . + * + * @param Target target buffer + * @param Source source buffer; set to NULL if you just have a C-String + * @param PlainIn Plain-C string to append; set to NULL if unused + * @returns size of result or -1 */ -int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error) +long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn) { - int fdflags; - int len, rlen, slen; - int nSuccessLess; - int nRead = 0; - char *ptr; - int IsNonBlock; - struct timeval tv; - fd_set rfds; + const char *aptr, *eiptr; + char *bptr, *eptr; + long len; - if ((Buf == NULL) || (*fd == -1)) - { - *Error = ErrRBLF_BLOBPreConditionFailed; + if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) return -1; - } - if (!append) - FlushStrBuf(Buf); - if (Buf->BufUsed + nBytes >= Buf->BufSize) - IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes); - ptr = Buf->buf + Buf->BufUsed; + if (PlainIn != NULL) { + aptr = PlainIn; + len = strlen(PlainIn); + eiptr = aptr + len; + } + else { + aptr = Source->buf; + eiptr = aptr + Source->BufUsed; + len = Source->BufUsed; + } - slen = len = Buf->BufUsed; + if (len == 0) + return -1; - fdflags = fcntl(*fd, F_GETFL); - IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; - nSuccessLess = 0; - while ((nRead < nBytes) && - (*fd != -1)) - { - if (IsNonBlock) - { - tv.tv_sec = 1; - tv.tv_usec = 0; - - FD_ZERO(&rfds); - FD_SET(*fd, &rfds); - if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) { - *Error = strerror(errno); - close (*fd); - *fd = -1; - if (*Error == NULL) - *Error = ErrRBLF_SelectFailed; - return -1; - } - if (! FD_ISSET(*fd, &rfds) != 0) { - nSuccessLess ++; - continue; - } - } + bptr = Target->buf + Target->BufUsed; + eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in... */ - if ((rlen = read(*fd, - ptr, - nBytes - nRead)) == -1) { - close(*fd); - *fd = -1; - *Error = strerror(errno); - return rlen; - } - nRead += rlen; - ptr += rlen; - Buf->BufUsed += rlen; + while (aptr < eiptr){ + if(bptr >= eptr) { + IncreaseBuf(Target, 1, -1); + eptr = Target->buf + Target->BufSize - 3; + bptr = Target->buf + Target->BufUsed; + } + if (*aptr == '"') { + *bptr = '\\'; + bptr ++; + *bptr = '"'; + bptr ++; + Target->BufUsed += 2; + } else if (*aptr == '\\') { + *bptr = '\\'; + bptr ++; + *bptr = '\\'; + bptr ++; + Target->BufUsed += 2; + } + else{ + *bptr = *aptr; + bptr++; + Target->BufUsed ++; + } + aptr ++; } - Buf->buf[Buf->BufUsed] = '\0'; - return nRead; + *bptr = '\0'; + if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) ) + return -1; + return Target->BufUsed; } -const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting."; -const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting."; /** - * @ingroup StrBuf_BufferedIO - * @brief Input binary data from socket - * flushes and closes the FD on error - * @param Blob put binary thing here - * @param IOBuf the buffer to get the input to - * @param Pos offset inside of IOBuf - * @param fd pointer to the filedescriptor to read - * @param append Append to an existing string or replace? - * @param nBytes the maximal number of bytes to read - * @param check whether we should search for '000\n' terminators in case of timeouts - * @param Error strerror() on error - * @returns numbers of chars read + * @ingroup StrBuf_DeEnCoder + * @brief Append a string, escaping characters which have meaning in HTML + json. + * + * @param Target target buffer + * @param Source source buffer; set to NULL if you just have a C-String + * @param PlainIn Plain-C string to append; set to NULL if unused + * @param nbsp If nonzero, spaces are converted to non-breaking spaces. + * @param nolinebreaks if set to 1, linebreaks are removed from the string. + * if set to 2, linebreaks are replaced by <br/> */ -int StrBufReadBLOBBuffered(StrBuf *Blob, - StrBuf *IOBuf, - const char **Pos, - int *fd, - int append, - long nBytes, - int check, - const char **Error) +long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks) { - const char *pche; - const char *pos; - int nSelects = 0; - int SelRes; - int fdflags; - int len = 0; - int rlen, slen; - int nRead = 0; - int nAlreadyRead = 0; - int IsNonBlock; - char *ptr; - fd_set rfds; - const char *pch; - struct timeval tv; - int nSuccessLess; + const char *aptr, *eiptr; + char *bptr, *eptr; + long len; + int IsUtf8Sequence = 0; - if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL)) - { - if (*Pos != NULL) - *Pos = NULL; - *Error = ErrRBB_BLOBFPreConditionFailed; + if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) return -1; - } - - if (!append) - FlushStrBuf(Blob); - if (Blob->BufUsed + nBytes >= Blob->BufSize) - IncreaseBuf(Blob, append, Blob->BufUsed + nBytes); - - pos = *Pos; - if (pos > 0) - len = pos - IOBuf->buf; - rlen = IOBuf->BufUsed - len; + if (PlainIn != NULL) { + aptr = PlainIn; + len = strlen(PlainIn); + eiptr = aptr + len; + } + else { + aptr = Source->buf; + eiptr = aptr + Source->BufUsed; + len = Source->BufUsed; + } + if (len == 0) + return -1; - if ((IOBuf->BufUsed > 0) && - (pos != NULL) && - (pos < IOBuf->buf + IOBuf->BufUsed)) - { - pche = IOBuf->buf + IOBuf->BufUsed; - pch = pos; + bptr = Target->buf + Target->BufUsed; + eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ - if (rlen < nBytes) { - memcpy(Blob->buf + Blob->BufUsed, pos, rlen); - Blob->BufUsed += rlen; - Blob->buf[Blob->BufUsed] = '\0'; - nAlreadyRead = nRead = rlen; - *Pos = NULL; + while (aptr < eiptr){ + if(bptr >= eptr) { + IncreaseBuf(Target, 1, -1); + eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ + bptr = Target->buf + Target->BufUsed; } - if (rlen >= nBytes) { - memcpy(Blob->buf + Blob->BufUsed, pos, nBytes); - Blob->BufUsed += nBytes; - Blob->buf[Blob->BufUsed] = '\0'; - if (rlen == nBytes) { - *Pos = NULL; - FlushStrBuf(IOBuf); - } - else - *Pos += nBytes; - return nBytes; + if (*aptr == '<') { + memcpy(bptr, "<", 4); + bptr += 4; + Target->BufUsed += 4; } - } - - FlushStrBuf(IOBuf); - *Pos = NULL; - if (IOBuf->BufSize < nBytes - nRead) - IncreaseBuf(IOBuf, 0, nBytes - nRead); - ptr = IOBuf->buf; - - slen = len = Blob->BufUsed; - - fdflags = fcntl(*fd, F_GETFL); - IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; - - SelRes = 1; - nBytes -= nRead; - nRead = 0; - while ((nRead < nBytes) && - (*fd != -1)) { - if (IsNonBlock) - { - tv.tv_sec = 1; - tv.tv_usec = 0; - - FD_ZERO(&rfds); - FD_SET(*fd, &rfds); - if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) { - *Error = strerror(errno); - close (*fd); - *fd = -1; - if (*Error == NULL) - *Error = ErrRBLF_SelectFailed; - return -1; - } - if (! FD_ISSET(*fd, &rfds) != 0) { - nSuccessLess ++; - continue; - } + else if (*aptr == '>') { + memcpy(bptr, ">", 4); + bptr += 4; + Target->BufUsed += 4; } - nSuccessLess = 0; - rlen = read(*fd, - ptr, - nBytes - nRead); - if (rlen == -1) { - close(*fd); - *fd = -1; - *Error = strerror(errno); - return rlen; + else if (*aptr == '&') { + memcpy(bptr, "&", 5); + bptr += 5; + Target->BufUsed += 5; } - else if (rlen == 0){ - nSuccessLess ++; - if ((check == NNN_TERM) && - (nRead > 5) && - (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) - { - StrBufPlain(Blob, HKEY("\n000\n")); - StrBufCutRight(Blob, 5); - return Blob->BufUsed; - } - if (nSelects > 10) { - FlushStrBuf(IOBuf); - *Error = ErrRBB_too_many_selects; - return -1; - } + else if (*aptr == LB) { + *bptr = '<'; + bptr ++; + Target->BufUsed ++; } - else if (rlen > 0) { - nRead += rlen; - ptr += rlen; - IOBuf->BufUsed += rlen; + else if (*aptr == RB) { + *bptr = '>'; + bptr ++; + Target->BufUsed ++; + } + else if ((*aptr == 32) && (nbsp == 1)) { + memcpy(bptr, " ", 6); + bptr += 6; + Target->BufUsed += 6; + } + else if ((*aptr == '\n') && (nolinebreaks == 1)) { + *bptr='\0'; /* nothing */ + } + else if ((*aptr == '\n') && (nolinebreaks == 2)) { + memcpy(bptr, "<br/>", 11); + bptr += 11; + Target->BufUsed += 11; } - } - if (nRead > nBytes) { - *Pos = IOBuf->buf + nBytes; - } - Blob->buf[Blob->BufUsed] = '\0'; - StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0); - if (*Pos == NULL) { - FlushStrBuf(IOBuf); - } - return nRead + nAlreadyRead; -} - -/** - * @ingroup StrBuf - * @brief Cut nChars from the start of the string - * @param Buf Buffer to modify - * @param nChars how many chars should be skipped? - */ -void StrBufCutLeft(StrBuf *Buf, int nChars) -{ - if (nChars >= Buf->BufUsed) { - FlushStrBuf(Buf); - return; - } - memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars); - Buf->BufUsed -= nChars; - Buf->buf[Buf->BufUsed] = '\0'; -} - -/** - * @ingroup StrBuf - * @brief Cut the trailing n Chars from the string - * @param Buf Buffer to modify - * @param nChars how many chars should be trunkated? - */ -void StrBufCutRight(StrBuf *Buf, int nChars) -{ - if (nChars >= Buf->BufUsed) { - FlushStrBuf(Buf); - return; - } - Buf->BufUsed -= nChars; - Buf->buf[Buf->BufUsed] = '\0'; -} - -/** - * @ingroup StrBuf - * @brief Cut the string after n Chars - * @param Buf Buffer to modify - * @param AfternChars after how many chars should we trunkate the string? - * @param At if non-null and points inside of our string, cut it there. - */ -void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At) -{ - if (At != NULL){ - AfternChars = At - Buf->buf; - } - - if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed)) - return; - Buf->BufUsed = AfternChars; - Buf->buf[Buf->BufUsed] = '\0'; -} - - -/** - * @ingroup StrBuf - * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length. - * @param Buf the string to modify - */ -void StrBufTrim(StrBuf *Buf) -{ - int delta = 0; - if ((Buf == NULL) || (Buf->BufUsed == 0)) return; - - while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){ - delta ++; - } - if (delta > 0) StrBufCutLeft(Buf, delta); - - if (Buf->BufUsed == 0) return; - while (isspace(Buf->buf[Buf->BufUsed - 1])){ - Buf->BufUsed --; - } - Buf->buf[Buf->BufUsed] = '\0'; -} - -/** - * @ingroup StrBuf - * @brief uppercase the contents of a buffer - * @param Buf the buffer to translate - */ -void StrBufUpCase(StrBuf *Buf) -{ - char *pch, *pche; - - pch = Buf->buf; - pche = pch + Buf->BufUsed; - while (pch < pche) { - *pch = toupper(*pch); - pch ++; - } -} - - -/** - * @ingroup StrBuf - * @brief lowercase the contents of a buffer - * @param Buf the buffer to translate - */ -void StrBufLowerCase(StrBuf *Buf) -{ - char *pch, *pche; - - pch = Buf->buf; - pche = pch + Buf->BufUsed; - while (pch < pche) { - *pch = tolower(*pch); - pch ++; - } -} - -/** - * @ingroup StrBuf - * @brief removes double slashes from pathnames - * @param Dir directory string to filter - * @param RemoveTrailingSlash allows / disallows trailing slashes - */ -void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash) -{ - char *a, *b; - a = b = Dir->buf; + else if ((*aptr == '\r') && (nolinebreaks != 0)) { + *bptr='\0'; /* nothing */ + } - while (!IsEmptyStr(a)) { - if (*a == '/') { - while (*a == '/') - a++; - *b = '/'; - b++; + else if ((*aptr == '"') || (*aptr == QU)) { + *bptr = '\\'; + bptr ++; + *bptr = '"'; + bptr ++; + Target->BufUsed += 2; + } else if (*aptr == '\\') { + *bptr = '\\'; + bptr ++; + *bptr = '\\'; + bptr ++; + Target->BufUsed += 2; } else { - *b = *a; - b++; a++; + if (((unsigned char)*aptr) >= 0x20) + { + IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr); + + *bptr = *aptr; + Target->BufUsed ++; + while (IsUtf8Sequence > 1){ + if(bptr + IsUtf8Sequence >= eptr) { + IncreaseBuf(Target, 1, -1); + eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ + bptr = Target->buf + Target->BufUsed - 1; + } + bptr++; aptr++; + IsUtf8Sequence --; + *bptr = *aptr; + Target->BufUsed ++; + } + bptr++; + } + } + aptr ++; } - if ((RemoveTrailingSlash) && (*(b - 1) != '/')){ - *b = '/'; - b++; - } - *b = '\0'; - Dir->BufUsed = b - Dir->buf; + *bptr = '\0'; + if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) ) + return -1; + return Target->BufUsed; } /** @@ -2664,131 +2063,21 @@ void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) target->buf[target->BufUsed + 1] = '\0'; } -#ifdef HAVE_ZLIB -#define DEF_MEM_LEVEL 8 /*< memlevel??? */ -#define OS_CODE 0x03 /*< unix */ + +/******************************************************************************* + * Quoted Printable de/encoding * + *******************************************************************************/ /** * @ingroup StrBuf_DeEnCoder - * @brief uses the same calling syntax as compress2(), but it - * creates a stream compatible with HTTP "Content-encoding: gzip" - * @param dest compressed buffer - * @param destLen length of the compresed data - * @param source source to encode - * @param sourceLen length of source to encode - * @param level compression level + * @brief decode a buffer from base 64 encoding; destroys original + * @param Buf Buffor to transform */ -int ZEXPORT compress_gzip(Bytef * dest, - size_t * destLen, - const Bytef * source, - uLong sourceLen, - int level) +int StrBufDecodeBase64(StrBuf *Buf) { - const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */ - - /* write gzip header */ - snprintf((char *) dest, *destLen, - "%c%c%c%c%c%c%c%c%c%c", - gz_magic[0], gz_magic[1], Z_DEFLATED, - 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ , - OS_CODE); - - /* normal deflate */ - z_stream stream; - int err; - stream.next_in = (Bytef *) source; - stream.avail_in = (uInt) sourceLen; - stream.next_out = dest + 10L; // after header - stream.avail_out = (uInt) * destLen; - if ((uLong) stream.avail_out != *destLen) - return Z_BUF_ERROR; - - stream.zalloc = (alloc_func) 0; - stream.zfree = (free_func) 0; - stream.opaque = (voidpf) 0; - - err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, - DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (err != Z_OK) - return err; - - err = deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - deflateEnd(&stream); - return err == Z_OK ? Z_BUF_ERROR : err; - } - *destLen = stream.total_out + 10L; - - /* write CRC and Length */ - uLong crc = crc32(0L, source, sourceLen); - int n; - for (n = 0; n < 4; ++n, ++*destLen) { - dest[*destLen] = (int) (crc & 0xff); - crc >>= 8; - } - uLong len = stream.total_in; - for (n = 0; n < 4; ++n, ++*destLen) { - dest[*destLen] = (int) (len & 0xff); - len >>= 8; - } - err = deflateEnd(&stream); - return err; -} -#endif - - -/** - * @ingroup StrBuf_DeEnCoder - * @brief compress the buffer with gzip - * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself! - * @param Buf buffer whose content is to be gzipped - */ -int CompressBuffer(StrBuf *Buf) -{ -#ifdef HAVE_ZLIB - char *compressed_data = NULL; - size_t compressed_len, bufsize; - int i = 0; - - bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100; - compressed_data = malloc(compressed_len); - - if (compressed_data == NULL) - return -1; - /* Flush some space after the used payload so valgrind shuts up... */ - while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize)) - Buf->buf[Buf->BufUsed + i++] = '\0'; - if (compress_gzip((Bytef *) compressed_data, - &compressed_len, - (Bytef *) Buf->buf, - (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) { - if (!Buf->ConstBuf) - free(Buf->buf); - Buf->buf = compressed_data; - Buf->BufUsed = compressed_len; - Buf->BufSize = bufsize; - /* Flush some space after the used payload so valgrind shuts up... */ - i = 0; - while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize)) - Buf->buf[Buf->BufUsed + i++] = '\0'; - return 1; - } else { - free(compressed_data); - } -#endif /* HAVE_ZLIB */ - return 0; -} - -/** - * @ingroup StrBuf_DeEnCoder - * @brief decode a buffer from base 64 encoding; destroys original - * @param Buf Buffor to transform - */ -int StrBufDecodeBase64(StrBuf *Buf) -{ - char *xferbuf; - size_t siz; - if (Buf == NULL) return -1; + char *xferbuf; + size_t siz; + if (Buf == NULL) return -1; xferbuf = (char*) malloc(Buf->BufSize); siz = CtdlDecodeBase64(xferbuf, @@ -2984,6 +2273,9 @@ void StrBufReplaceChars(StrBuf *buf, char search, char replace) } +/******************************************************************************* + * Iconv Wrapper; RFC822 de/encoding * + *******************************************************************************/ /** * @ingroup StrBuf_DeEnCoder @@ -3181,288 +2473,960 @@ inline static void DecodeSegment(StrBuf *Target, ConvertBuf->buf[pos] = ' '; pos++; } - - ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable( - ConvertBuf2->buf, - ConvertBuf->buf, - ConvertBuf->BufUsed); - } - else { - StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0); - } -#ifdef HAVE_ICONV - ctdl_iconv_open("UTF-8", charset, &ic); - if (ic != (iconv_t)(-1) ) { -#endif - StrBufConvert(ConvertBuf2, ConvertBuf, &ic); - StrBufAppendBuf(Target, ConvertBuf2, 0); -#ifdef HAVE_ICONV - iconv_close(ic); + + ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable( + ConvertBuf2->buf, + ConvertBuf->buf, + ConvertBuf->BufUsed); + } + else { + StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0); + } +#ifdef HAVE_ICONV + ctdl_iconv_open("UTF-8", charset, &ic); + if (ic != (iconv_t)(-1) ) { +#endif + StrBufConvert(ConvertBuf2, ConvertBuf, &ic); + StrBufAppendBuf(Target, ConvertBuf2, 0); +#ifdef HAVE_ICONV + iconv_close(ic); + } + else { + StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0); + } +#endif +} + +/** + * @ingroup StrBuf_DeEnCoder + * @brief Handle subjects with RFC2047 encoding such as: + * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?= + * @param Target where to put the decoded string to + * @param DecodeMe buffer with encoded string + * @param DefaultCharset if we don't find one, which should we use? + * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, + * put it here for later use where no string might be known. + */ +void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset) +{ + StrBuf *DecodedInvalidBuf = NULL; + StrBuf *ConvertBuf, *ConvertBuf2; + const StrBuf *DecodeMee = DecodeMe; + char *start, *end, *next, *nextend, *ptr = NULL; +#ifdef HAVE_ICONV + iconv_t ic = (iconv_t)(-1) ; +#endif + const char *eptr; + int passes = 0; + int i, len, delta; + int illegal_non_rfc2047_encoding = 0; + + /* Sometimes, badly formed messages contain strings which were simply + * written out directly in some foreign character set instead of + * using RFC2047 encoding. This is illegal but we will attempt to + * handle it anyway by converting from a user-specified default + * charset to UTF-8 if we see any nonprintable characters. + */ + + len = StrLength(DecodeMe); + for (i=0; iBufUsed; ++i) { + if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) { + illegal_non_rfc2047_encoding = 1; + break; + } + } + + ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe)); + if ((illegal_non_rfc2047_encoding) && + (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && + (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) ) + { +#ifdef HAVE_ICONV + ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic); + if (ic != (iconv_t)(-1) ) { + DecodedInvalidBuf = NewStrBufDup(DecodeMe); + StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const? + DecodeMee = DecodedInvalidBuf; + iconv_close(ic); + } +#endif + } + + /* pre evaluate the first pair */ + nextend = end = NULL; + len = StrLength(DecodeMee); + start = strstr(DecodeMee->buf, "=?"); + eptr = DecodeMee->buf + DecodeMee->BufUsed; + if (start != NULL) + end = FindNextEnd (DecodeMee, start); + else { + StrBufAppendBuf(Target, DecodeMee, 0); + FreeStrBuf(&ConvertBuf); + FreeStrBuf(&DecodedInvalidBuf); + return; + } + + ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMee)); + + if (start != DecodeMee->buf) { + long nFront; + + nFront = start - DecodeMee->buf; + StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0); + len -= nFront; + } + /* + * Since spammers will go to all sorts of absurd lengths to get their + * messages through, there are LOTS of corrupt headers out there. + * So, prevent a really badly formed RFC2047 header from throwing + * this function into an infinite loop. + */ + while ((start != NULL) && + (end != NULL) && + (start < eptr) && + (end < eptr) && + (passes < 20)) + { + passes++; + DecodeSegment(Target, + DecodeMee, + start, + end, + ConvertBuf, + ConvertBuf2, + FoundCharset); + + next = strstr(end, "=?"); + nextend = NULL; + if ((next != NULL) && + (next < eptr)) + nextend = FindNextEnd(DecodeMee, next); + if (nextend == NULL) + next = NULL; + + /* did we find two partitions */ + if ((next != NULL) && + ((next - end) > 2)) + { + ptr = end + 2; + while ((ptr < next) && + (isspace(*ptr) || + (*ptr == '\r') || + (*ptr == '\n') || + (*ptr == '\t'))) + ptr ++; + /* did we find a gab just filled with blanks? */ + if (ptr == next) + { + long gap = next - start; + memmove (end + 2, + next, + len - (gap)); + len -= gap; + /* now terminate the gab at the end */ + delta = (next - end) - 2; ////TODO: const! + ((StrBuf*)DecodeMee)->BufUsed -= delta; + ((StrBuf*)DecodeMee)->buf[DecodeMee->BufUsed] = '\0'; + + /* move next to its new location. */ + next -= delta; + nextend -= delta; + } + } + /* our next-pair is our new first pair now. */ + ptr = end + 2; + start = next; + end = nextend; + } + end = ptr; + nextend = DecodeMee->buf + DecodeMee->BufUsed; + if ((end != NULL) && (end < nextend)) { + ptr = end; + while ( (ptr < nextend) && + (isspace(*ptr) || + (*ptr == '\r') || + (*ptr == '\n') || + (*ptr == '\t'))) + ptr ++; + if (ptr < nextend) + StrBufAppendBufPlain(Target, end, nextend - end, 0); + } + FreeStrBuf(&ConvertBuf); + FreeStrBuf(&ConvertBuf2); + FreeStrBuf(&DecodedInvalidBuf); +} + +/******************************************************************************* + * Manipulating UTF-8 Strings * + *******************************************************************************/ + +/** + * @ingroup StrBuf + * @brief evaluate the length of an utf8 special character sequence + * @param Char the character to examine + * @returns width of utf8 chars in bytes + */ +static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE) +{ + int n = 1; + char test = (1<<7); + + while ((n < 8) && ((test & *CharS) != 0)) { + test = test << 1; + n ++; + } + if ((n > 6) || ((CharE - CharS) < n)) + n = 1; + return n; +} + +/** + * @ingroup StrBuf + * @brief detect whether this char starts an utf-8 encoded char + * @param Char character to inspect + * @returns yes or no + */ +static inline int Ctdl_IsUtf8SequenceStart(const char Char) +{ +/** 11??.???? indicates an UTF8 Sequence. */ + return ((Char & 0xC0) != 0); +} + +/** + * @ingroup StrBuf + * @brief measure the number of glyphs in an UTF8 string... + * @param Buf string to measure + * @returns the number of glyphs in Buf + */ +long StrBuf_Utf8StrLen(StrBuf *Buf) +{ + int n = 0; + int m = 0; + char *aptr, *eptr; + + if ((Buf == NULL) || (Buf->BufUsed == 0)) + return 0; + aptr = Buf->buf; + eptr = Buf->buf + Buf->BufUsed; + while ((aptr < eptr) && (*aptr != '\0')) { + if (Ctdl_IsUtf8SequenceStart(*aptr)){ + m = Ctdl_GetUtf8SequenceLength(aptr, eptr); + while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) ); + n ++; + } + else { + n++; + aptr++; + } + } + return n; +} + +/** + * @ingroup StrBuf + * @brief cuts a string after maxlen glyphs + * @param Buf string to cut to maxlen glyphs + * @param maxlen how long may the string become? + * @returns current length of the string + */ +long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen) +{ + char *aptr, *eptr; + int n = 0, m = 0; + + aptr = Buf->buf; + eptr = Buf->buf + Buf->BufUsed; + while ((aptr < eptr) && (*aptr != '\0')) { + if (Ctdl_IsUtf8SequenceStart(*aptr)){ + m = Ctdl_GetUtf8SequenceLength(aptr, eptr); + while ((*aptr++ != '\0') && (m-- > 0)); + n ++; + } + else { + n++; + aptr++; + } + if (n > maxlen) { + *aptr = '\0'; + Buf->BufUsed = aptr - Buf->buf; + return Buf->BufUsed; + } + } + return Buf->BufUsed; + +} + + + + + +/******************************************************************************* + * wrapping ZLib * + *******************************************************************************/ + +#ifdef HAVE_ZLIB +#define DEF_MEM_LEVEL 8 /*< memlevel??? */ +#define OS_CODE 0x03 /*< unix */ + +/** + * @ingroup StrBuf_DeEnCoder + * @brief uses the same calling syntax as compress2(), but it + * creates a stream compatible with HTTP "Content-encoding: gzip" + * @param dest compressed buffer + * @param destLen length of the compresed data + * @param source source to encode + * @param sourceLen length of source to encode + * @param level compression level + */ +int ZEXPORT compress_gzip(Bytef * dest, + size_t * destLen, + const Bytef * source, + uLong sourceLen, + int level) +{ + const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */ + + /* write gzip header */ + snprintf((char *) dest, *destLen, + "%c%c%c%c%c%c%c%c%c%c", + gz_magic[0], gz_magic[1], Z_DEFLATED, + 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ , + OS_CODE); + + /* normal deflate */ + z_stream stream; + int err; + stream.next_in = (Bytef *) source; + stream.avail_in = (uInt) sourceLen; + stream.next_out = dest + 10L; // after header + stream.avail_out = (uInt) * destLen; + if ((uLong) stream.avail_out != *destLen) + return Z_BUF_ERROR; + + stream.zalloc = (alloc_func) 0; + stream.zfree = (free_func) 0; + stream.opaque = (voidpf) 0; + + err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out + 10L; + + /* write CRC and Length */ + uLong crc = crc32(0L, source, sourceLen); + int n; + for (n = 0; n < 4; ++n, ++*destLen) { + dest[*destLen] = (int) (crc & 0xff); + crc >>= 8; + } + uLong len = stream.total_in; + for (n = 0; n < 4; ++n, ++*destLen) { + dest[*destLen] = (int) (len & 0xff); + len >>= 8; + } + err = deflateEnd(&stream); + return err; +} +#endif + + +/** + * @ingroup StrBuf_DeEnCoder + * @brief compress the buffer with gzip + * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself! + * @param Buf buffer whose content is to be gzipped + */ +int CompressBuffer(StrBuf *Buf) +{ +#ifdef HAVE_ZLIB + char *compressed_data = NULL; + size_t compressed_len, bufsize; + int i = 0; + + bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100; + compressed_data = malloc(compressed_len); + + if (compressed_data == NULL) + return -1; + /* Flush some space after the used payload so valgrind shuts up... */ + while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize)) + Buf->buf[Buf->BufUsed + i++] = '\0'; + if (compress_gzip((Bytef *) compressed_data, + &compressed_len, + (Bytef *) Buf->buf, + (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) { + if (!Buf->ConstBuf) + free(Buf->buf); + Buf->buf = compressed_data; + Buf->BufUsed = compressed_len; + Buf->BufSize = bufsize; + /* Flush some space after the used payload so valgrind shuts up... */ + i = 0; + while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize)) + Buf->buf[Buf->BufUsed + i++] = '\0'; + return 1; + } else { + free(compressed_data); + } +#endif /* HAVE_ZLIB */ + return 0; +} + + + +/******************************************************************************* + * File I/O; Prefer buffered read since its faster! * + *******************************************************************************/ + +/** + * @ingroup StrBuf_IO + * @brief Read a line from socket + * flushes and closes the FD on error + * @param buf the buffer to get the input to + * @param fd pointer to the filedescriptor to read + * @param append Append to an existing string or replace? + * @param Error strerror() on error + * @returns numbers of chars read + */ +int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error) +{ + int len, rlen, slen; + + if (!append) + FlushStrBuf(buf); + + slen = len = buf->BufUsed; + while (1) { + rlen = read(*fd, &buf->buf[len], 1); + if (rlen < 1) { + *Error = strerror(errno); + + close(*fd); + *fd = -1; + + return -1; + } + if (buf->buf[len] == '\n') + break; + if (buf->buf[len] != '\r') + len ++; + if (len + 2 >= buf->BufSize) { + buf->BufUsed = len; + buf->buf[len+1] = '\0'; + IncreaseBuf(buf, 1, -1); + } + } + buf->BufUsed = len; + buf->buf[len] = '\0'; + return len - slen; +} + +/** + * @ingroup StrBuf_BufferedIO + * @brief Read a line from socket + * flushes and closes the FD on error + * @param Line the line to read from the fd / I/O Buffer + * @param buf the buffer to get the input to + * @param fd pointer to the filedescriptor to read + * @param timeout number of successless selects until we bail out + * @param selectresolution how long to wait on each select + * @param Error strerror() on error + * @returns numbers of chars read + */ +int StrBufTCP_read_buffered_line(StrBuf *Line, + StrBuf *buf, + int *fd, + int timeout, + int selectresolution, + const char **Error) +{ + int len, rlen; + int nSuccessLess = 0; + fd_set rfds; + char *pch = NULL; + int fdflags; + int IsNonBlock; + struct timeval tv; + + if (buf->BufUsed > 0) { + pch = strchr(buf->buf, '\n'); + if (pch != NULL) { + rlen = 0; + len = pch - buf->buf; + if (len > 0 && (*(pch - 1) == '\r') ) + rlen ++; + StrBufSub(Line, buf, 0, len - rlen); + StrBufCutLeft(buf, len + 1); + return len - rlen; + } + } + + if (buf->BufSize - buf->BufUsed < 10) + IncreaseBuf(buf, 1, -1); + + fdflags = fcntl(*fd, F_GETFL); + IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; + + while ((nSuccessLess < timeout) && (pch == NULL)) { + if (IsNonBlock){ + tv.tv_sec = selectresolution; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(*fd, &rfds); + if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) { + *Error = strerror(errno); + close (*fd); + *fd = -1; + return -1; + } + } + if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) { + nSuccessLess ++; + continue; + } + rlen = read(*fd, + &buf->buf[buf->BufUsed], + buf->BufSize - buf->BufUsed - 1); + if (rlen < 1) { + *Error = strerror(errno); + close(*fd); + *fd = -1; + return -1; + } + else if (rlen > 0) { + nSuccessLess = 0; + buf->BufUsed += rlen; + buf->buf[buf->BufUsed] = '\0'; + if (buf->BufUsed + 10 > buf->BufSize) { + IncreaseBuf(buf, 1, -1); + } + pch = strchr(buf->buf, '\n'); + continue; + } + + } + if (pch != NULL) { + rlen = 0; + len = pch - buf->buf; + if (len > 0 && (*(pch - 1) == '\r') ) + rlen ++; + StrBufSub(Line, buf, 0, len - rlen); + StrBufCutLeft(buf, len + 1); + return len - rlen; + } + return -1; + +} + +static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor"; +static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason"; +static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer"; +/** + * @ingroup StrBuf_BufferedIO + * @brief Read a line from socket + * flushes and closes the FD on error + * @param Line Line to read from the fd / I/O Buffer + * @param IOBuf the buffer to get the input to + * @param Pos pointer to the current read position, should be NULL initialized! + * @param fd pointer to the filedescriptor to read + * @param timeout number of successless selects until we bail out + * @param selectresolution how long to wait on each select + * @param Error strerror() on error + * @returns numbers of chars read + */ +int StrBufTCP_read_buffered_line_fast(StrBuf *Line, + StrBuf *IOBuf, + const char **Pos, + int *fd, + int timeout, + int selectresolution, + const char **Error) +{ + const char *pche = NULL; + const char *pos = NULL; + int len, rlen; + int nSuccessLess = 0; + fd_set rfds; + const char *pch = NULL; + int fdflags; + int IsNonBlock; + struct timeval tv; + + if ((Line == NULL) || + (Pos == NULL) || + (IOBuf == NULL) || + (*fd == -1)) + { + if (Pos != NULL) + *Pos = NULL; + *Error = ErrRBLF_PreConditionFailed; + return -1; + } + + pos = *Pos; + if ((IOBuf->BufUsed > 0) && + (pos != NULL) && + (pos < IOBuf->buf + IOBuf->BufUsed)) + { + pche = IOBuf->buf + IOBuf->BufUsed; + pch = pos; + while ((pch < pche) && (*pch != '\n')) + pch ++; + if ((pch >= pche) || (*pch == '\0')) + pch = NULL; + if ((pch != NULL) && + (pch <= pche)) + { + rlen = 0; + len = pch - pos; + if (len > 0 && (*(pch - 1) == '\r') ) + rlen ++; + StrBufSub(Line, IOBuf, (pos - IOBuf->buf), len - rlen); + *Pos = pch + 1; + return len - rlen; + } + } + + if (pos != NULL) { + if (pos > pche) + FlushStrBuf(IOBuf); + else + StrBufCutLeft(IOBuf, (pos - IOBuf->buf)); + *Pos = NULL; + } + + if (IOBuf->BufSize - IOBuf->BufUsed < 10) { + IncreaseBuf(IOBuf, 1, -1); + *Pos = NULL; + } + + fdflags = fcntl(*fd, F_GETFL); + IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; + + pch = NULL; + while ((nSuccessLess < timeout) && + (pch == NULL) && + (*fd != -1)) { + if (IsNonBlock) + { + tv.tv_sec = 1; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(*fd, &rfds); + if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) { + *Error = strerror(errno); + close (*fd); + *fd = -1; + if (*Error == NULL) + *Error = ErrRBLF_SelectFailed; + return -1; + } + if (! FD_ISSET(*fd, &rfds) != 0) { + nSuccessLess ++; + continue; + } + } + rlen = read(*fd, + &IOBuf->buf[IOBuf->BufUsed], + IOBuf->BufSize - IOBuf->BufUsed - 1); + if (rlen < 1) { + *Error = strerror(errno); + close(*fd); + *fd = -1; + return -1; + } + else if (rlen > 0) { + nSuccessLess = 0; + IOBuf->BufUsed += rlen; + IOBuf->buf[IOBuf->BufUsed] = '\0'; + if (IOBuf->BufUsed + 10 > IOBuf->BufSize) { + IncreaseBuf(IOBuf, 1, -1); + *Pos = NULL; + } + + pche = IOBuf->buf + IOBuf->BufUsed; + pch = IOBuf->buf; + while ((pch < pche) && (*pch != '\n')) + pch ++; + if ((pch >= pche) || (*pch == '\0')) + pch = NULL; + continue; + } } - else { - StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0); + if (pch != NULL) { + pos = IOBuf->buf; + rlen = 0; + len = pch - pos; + if (len > 0 && (*(pch - 1) == '\r') ) + rlen ++; + StrBufSub(Line, IOBuf, 0, len - rlen); + *Pos = pos + len + 1; + return len - rlen; } -#endif + *Error = ErrRBLF_NotEnoughSentFromServer; + return -1; + } +static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor"; /** - * @ingroup StrBuf_DeEnCoder - * @brief Handle subjects with RFC2047 encoding such as: - * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?= - * @param Target where to put the decoded string to - * @param DecodeMe buffer with encoded string - * @param DefaultCharset if we don't find one, which should we use? - * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, - * put it here for later use where no string might be known. + * @ingroup StrBuf_IO + * @brief Input binary data from socket + * flushes and closes the FD on error + * @param Buf the buffer to get the input to + * @param fd pointer to the filedescriptor to read + * @param append Append to an existing string or replace? + * @param nBytes the maximal number of bytes to read + * @param Error strerror() on error + * @returns numbers of chars read */ -void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset) +int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error) { - StrBuf *DecodedInvalidBuf = NULL; - StrBuf *ConvertBuf, *ConvertBuf2; - const StrBuf *DecodeMee = DecodeMe; - char *start, *end, *next, *nextend, *ptr = NULL; -#ifdef HAVE_ICONV - iconv_t ic = (iconv_t)(-1) ; -#endif - const char *eptr; - int passes = 0; - int i, len, delta; - int illegal_non_rfc2047_encoding = 0; - - /* Sometimes, badly formed messages contain strings which were simply - * written out directly in some foreign character set instead of - * using RFC2047 encoding. This is illegal but we will attempt to - * handle it anyway by converting from a user-specified default - * charset to UTF-8 if we see any nonprintable characters. - */ - - len = StrLength(DecodeMe); - for (i=0; iBufUsed; ++i) { - if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) { - illegal_non_rfc2047_encoding = 1; - break; - } - } + int fdflags; + int len, rlen, slen; + int nSuccessLess; + int nRead = 0; + char *ptr; + int IsNonBlock; + struct timeval tv; + fd_set rfds; - ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe)); - if ((illegal_non_rfc2047_encoding) && - (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && - (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) ) + if ((Buf == NULL) || (*fd == -1)) { -#ifdef HAVE_ICONV - ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic); - if (ic != (iconv_t)(-1) ) { - DecodedInvalidBuf = NewStrBufDup(DecodeMe); - StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const? - DecodeMee = DecodedInvalidBuf; - iconv_close(ic); - } -#endif + *Error = ErrRBLF_BLOBPreConditionFailed; + return -1; } + if (!append) + FlushStrBuf(Buf); + if (Buf->BufUsed + nBytes >= Buf->BufSize) + IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes); - /* pre evaluate the first pair */ - nextend = end = NULL; - len = StrLength(DecodeMee); - start = strstr(DecodeMee->buf, "=?"); - eptr = DecodeMee->buf + DecodeMee->BufUsed; - if (start != NULL) - end = FindNextEnd (DecodeMee, start); - else { - StrBufAppendBuf(Target, DecodeMee, 0); - FreeStrBuf(&ConvertBuf); - FreeStrBuf(&DecodedInvalidBuf); - return; - } + ptr = Buf->buf + Buf->BufUsed; - ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMee)); + slen = len = Buf->BufUsed; - if (start != DecodeMee->buf) { - long nFront; - - nFront = start - DecodeMee->buf; - StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0); - len -= nFront; - } - /* - * Since spammers will go to all sorts of absurd lengths to get their - * messages through, there are LOTS of corrupt headers out there. - * So, prevent a really badly formed RFC2047 header from throwing - * this function into an infinite loop. - */ - while ((start != NULL) && - (end != NULL) && - (start < eptr) && - (end < eptr) && - (passes < 20)) + fdflags = fcntl(*fd, F_GETFL); + IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; + nSuccessLess = 0; + while ((nRead < nBytes) && + (*fd != -1)) { - passes++; - DecodeSegment(Target, - DecodeMee, - start, - end, - ConvertBuf, - ConvertBuf2, - FoundCharset); - - next = strstr(end, "=?"); - nextend = NULL; - if ((next != NULL) && - (next < eptr)) - nextend = FindNextEnd(DecodeMee, next); - if (nextend == NULL) - next = NULL; - - /* did we find two partitions */ - if ((next != NULL) && - ((next - end) > 2)) + if (IsNonBlock) { - ptr = end + 2; - while ((ptr < next) && - (isspace(*ptr) || - (*ptr == '\r') || - (*ptr == '\n') || - (*ptr == '\t'))) - ptr ++; - /* did we find a gab just filled with blanks? */ - if (ptr == next) - { - long gap = next - start; - memmove (end + 2, - next, - len - (gap)); - len -= gap; - /* now terminate the gab at the end */ - delta = (next - end) - 2; ////TODO: const! - ((StrBuf*)DecodeMee)->BufUsed -= delta; - ((StrBuf*)DecodeMee)->buf[DecodeMee->BufUsed] = '\0'; - - /* move next to its new location. */ - next -= delta; - nextend -= delta; + tv.tv_sec = 1; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(*fd, &rfds); + if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) { + *Error = strerror(errno); + close (*fd); + *fd = -1; + if (*Error == NULL) + *Error = ErrRBLF_SelectFailed; + return -1; + } + if (! FD_ISSET(*fd, &rfds) != 0) { + nSuccessLess ++; + continue; } } - /* our next-pair is our new first pair now. */ - ptr = end + 2; - start = next; - end = nextend; - } - end = ptr; - nextend = DecodeMee->buf + DecodeMee->BufUsed; - if ((end != NULL) && (end < nextend)) { - ptr = end; - while ( (ptr < nextend) && - (isspace(*ptr) || - (*ptr == '\r') || - (*ptr == '\n') || - (*ptr == '\t'))) - ptr ++; - if (ptr < nextend) - StrBufAppendBufPlain(Target, end, nextend - end, 0); - } - FreeStrBuf(&ConvertBuf); - FreeStrBuf(&ConvertBuf2); - FreeStrBuf(&DecodedInvalidBuf); -} -/** - * @ingroup StrBuf - * @brief evaluate the length of an utf8 special character sequence - * @param Char the character to examine - * @returns width of utf8 chars in bytes - */ -static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE) -{ - int n = 1; - char test = (1<<7); - - while ((n < 8) && ((test & *CharS) != 0)) { - test = test << 1; - n ++; + if ((rlen = read(*fd, + ptr, + nBytes - nRead)) == -1) { + close(*fd); + *fd = -1; + *Error = strerror(errno); + return rlen; + } + nRead += rlen; + ptr += rlen; + Buf->BufUsed += rlen; } - if ((n > 6) || ((CharE - CharS) < n)) - n = 1; - return n; + Buf->buf[Buf->BufUsed] = '\0'; + return nRead; } +const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting."; +const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting."; /** - * @ingroup StrBuf - * @brief detect whether this char starts an utf-8 encoded char - * @param Char character to inspect - * @returns yes or no + * @ingroup StrBuf_BufferedIO + * @brief Input binary data from socket + * flushes and closes the FD on error + * @param Blob put binary thing here + * @param IOBuf the buffer to get the input to + * @param Pos offset inside of IOBuf + * @param fd pointer to the filedescriptor to read + * @param append Append to an existing string or replace? + * @param nBytes the maximal number of bytes to read + * @param check whether we should search for '000\n' terminators in case of timeouts + * @param Error strerror() on error + * @returns numbers of chars read */ -static inline int Ctdl_IsUtf8SequenceStart(const char Char) +int StrBufReadBLOBBuffered(StrBuf *Blob, + StrBuf *IOBuf, + const char **Pos, + int *fd, + int append, + long nBytes, + int check, + const char **Error) { -/** 11??.???? indicates an UTF8 Sequence. */ - return ((Char & 0xC0) != 0); -} + const char *pche; + const char *pos; + int nSelects = 0; + int SelRes; + int fdflags; + int len = 0; + int rlen, slen; + int nRead = 0; + int nAlreadyRead = 0; + int IsNonBlock; + char *ptr; + fd_set rfds; + const char *pch; + struct timeval tv; + int nSuccessLess; -/** - * @ingroup StrBuf - * @brief measure the number of glyphs in an UTF8 string... - * @param Buf string to measure - * @returns the number of glyphs in Buf - */ -long StrBuf_Utf8StrLen(StrBuf *Buf) -{ - int n = 0; - int m = 0; - char *aptr, *eptr; + if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL)) + { + if (*Pos != NULL) + *Pos = NULL; + *Error = ErrRBB_BLOBFPreConditionFailed; + return -1; + } - if ((Buf == NULL) || (Buf->BufUsed == 0)) - return 0; - aptr = Buf->buf; - eptr = Buf->buf + Buf->BufUsed; - while ((aptr < eptr) && (*aptr != '\0')) { - if (Ctdl_IsUtf8SequenceStart(*aptr)){ - m = Ctdl_GetUtf8SequenceLength(aptr, eptr); - while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) ); - n ++; + if (!append) + FlushStrBuf(Blob); + if (Blob->BufUsed + nBytes >= Blob->BufSize) + IncreaseBuf(Blob, append, Blob->BufUsed + nBytes); + + pos = *Pos; + + if (pos > 0) + len = pos - IOBuf->buf; + rlen = IOBuf->BufUsed - len; + + + if ((IOBuf->BufUsed > 0) && + (pos != NULL) && + (pos < IOBuf->buf + IOBuf->BufUsed)) + { + pche = IOBuf->buf + IOBuf->BufUsed; + pch = pos; + + if (rlen < nBytes) { + memcpy(Blob->buf + Blob->BufUsed, pos, rlen); + Blob->BufUsed += rlen; + Blob->buf[Blob->BufUsed] = '\0'; + nAlreadyRead = nRead = rlen; + *Pos = NULL; } - else { - n++; - aptr++; + if (rlen >= nBytes) { + memcpy(Blob->buf + Blob->BufUsed, pos, nBytes); + Blob->BufUsed += nBytes; + Blob->buf[Blob->BufUsed] = '\0'; + if (rlen == nBytes) { + *Pos = NULL; + FlushStrBuf(IOBuf); + } + else + *Pos += nBytes; + return nBytes; } } - return n; -} -/** - * @ingroup StrBuf - * @brief cuts a string after maxlen glyphs - * @param Buf string to cut to maxlen glyphs - * @param maxlen how long may the string become? - * @returns current length of the string - */ -long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen) -{ - char *aptr, *eptr; - int n = 0, m = 0; + FlushStrBuf(IOBuf); + *Pos = NULL; + if (IOBuf->BufSize < nBytes - nRead) + IncreaseBuf(IOBuf, 0, nBytes - nRead); + ptr = IOBuf->buf; - aptr = Buf->buf; - eptr = Buf->buf + Buf->BufUsed; - while ((aptr < eptr) && (*aptr != '\0')) { - if (Ctdl_IsUtf8SequenceStart(*aptr)){ - m = Ctdl_GetUtf8SequenceLength(aptr, eptr); - while ((*aptr++ != '\0') && (m-- > 0)); - n ++; + slen = len = Blob->BufUsed; + + fdflags = fcntl(*fd, F_GETFL); + IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; + + SelRes = 1; + nBytes -= nRead; + nRead = 0; + while ((nRead < nBytes) && + (*fd != -1)) { + if (IsNonBlock) + { + tv.tv_sec = 1; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(*fd, &rfds); + if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) { + *Error = strerror(errno); + close (*fd); + *fd = -1; + if (*Error == NULL) + *Error = ErrRBLF_SelectFailed; + return -1; + } + if (! FD_ISSET(*fd, &rfds) != 0) { + nSuccessLess ++; + continue; + } } - else { - n++; - aptr++; + nSuccessLess = 0; + rlen = read(*fd, + ptr, + nBytes - nRead); + if (rlen == -1) { + close(*fd); + *fd = -1; + *Error = strerror(errno); + return rlen; + } + else if (rlen == 0){ + nSuccessLess ++; + if ((check == NNN_TERM) && + (nRead > 5) && + (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) + { + StrBufPlain(Blob, HKEY("\n000\n")); + StrBufCutRight(Blob, 5); + return Blob->BufUsed; + } + if (nSelects > 10) { + FlushStrBuf(IOBuf); + *Error = ErrRBB_too_many_selects; + return -1; + } + } + else if (rlen > 0) { + nRead += rlen; + ptr += rlen; + IOBuf->BufUsed += rlen; } - if (n > maxlen) { - *aptr = '\0'; - Buf->BufUsed = aptr - Buf->buf; - return Buf->BufUsed; - } } - return Buf->BufUsed; - + if (nRead > nBytes) { + *Pos = IOBuf->buf + nBytes; + } + Blob->buf[Blob->BufUsed] = '\0'; + StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0); + if (*Pos == NULL) { + FlushStrBuf(IOBuf); + } + return nRead + nAlreadyRead; } - /** - * @ingroup StrBuf + * @ingroup StrBuf_IO * @brief extract a "next line" from Buf; Ptr to persist across several iterations * @param LineBuf your line will be copied here. * @param Buf BLOB with lines of text... @@ -3523,3 +3487,37 @@ int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr) return Buf->BufUsed - (ptr - Buf->buf); } + +/** + * @ingroup StrBuf_IO + * @brief removes double slashes from pathnames + * @param Dir directory string to filter + * @param RemoveTrailingSlash allows / disallows trailing slashes + */ +void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash) +{ + char *a, *b; + + a = b = Dir->buf; + + while (!IsEmptyStr(a)) { + if (*a == '/') { + while (*a == '/') + a++; + *b = '/'; + b++; + } + else { + *b = *a; + b++; a++; + } + } + if ((RemoveTrailingSlash) && (*(b - 1) != '/')){ + *b = '/'; + b++; + } + *b = '\0'; + Dir->BufUsed = b - Dir->buf; +} + +