xref: /lighttpd1.4/src/buffer.c (revision 5e14db43)
18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss 
322e8b456SStefan Bühler #include "buffer.h"
422e8b456SStefan Bühler 
5bcdc6a3bSJan Kneschke #include <stdlib.h>
6bcdc6a3bSJan Kneschke #include <string.h>
713ea2d88SGlenn Strauss #include "sys-time.h"   /* strftime() */
8bcdc6a3bSJan Kneschke 
9936db51fSGlenn Strauss static const char hex_chars_lc[] = "0123456789abcdef";
10936db51fSGlenn Strauss static const char hex_chars_uc[] = "0123456789ABCDEF";
11bcdc6a3bSJan Kneschke 
12bcdc6a3bSJan Kneschke 
13af3df29aSGlenn Strauss __attribute_noinline__
buffer_init(void)14bcdc6a3bSJan Kneschke buffer* buffer_init(void) {
15*5e14db43SGlenn Strauss   #if 0 /* buffer_init() and chunk_init() can be hot,
16*5e14db43SGlenn Strauss 	 * so avoid the additional hop of indirection */
17*5e14db43SGlenn Strauss 	return ck_calloc(1, sizeof(buffer));
18*5e14db43SGlenn Strauss   #else
19af3df29aSGlenn Strauss 	buffer * const b = calloc(1, sizeof(*b));
2007dd0bd0SStefan Bühler 	force_assert(b);
21bcdc6a3bSJan Kneschke 	return b;
22*5e14db43SGlenn Strauss   #endif
23bcdc6a3bSJan Kneschke }
24bcdc6a3bSJan Kneschke 
buffer_free(buffer * b)25bcdc6a3bSJan Kneschke void buffer_free(buffer *b) {
266afad87dSStefan Bühler 	if (NULL == b) return;
27bcdc6a3bSJan Kneschke 	free(b->ptr);
28bcdc6a3bSJan Kneschke 	free(b);
29bcdc6a3bSJan Kneschke }
30bcdc6a3bSJan Kneschke 
buffer_free_ptr(buffer * b)31b600b75fSGlenn Strauss void buffer_free_ptr(buffer *b) {
32bcdc6a3bSJan Kneschke 	free(b->ptr);
33bcdc6a3bSJan Kneschke 	b->ptr = NULL;
34babf8112SGlenn Strauss 	b->used = 0;
35bcdc6a3bSJan Kneschke 	b->size = 0;
36bcdc6a3bSJan Kneschke }
37bcdc6a3bSJan Kneschke 
buffer_move(buffer * restrict b,buffer * restrict src)389914bb29SGlenn Strauss void buffer_move(buffer * restrict b, buffer * restrict src) {
396afad87dSStefan Bühler 	buffer tmp;
4044927490SGlenn Strauss 	buffer_clear(b);
416afad87dSStefan Bühler 	tmp = *src; *src = *b; *b = tmp;
426afad87dSStefan Bühler }
43bcdc6a3bSJan Kneschke 
4475bd40aaSGlenn Strauss /* make sure buffer is at least "size" big + 1 for '\0'. keep old data */
4575bd40aaSGlenn Strauss __attribute_cold__
4638c87358SGlenn Strauss __attribute_noinline__
__attribute_nonnull__()47575665adSGlenn Strauss __attribute_nonnull__()
4838c87358SGlenn Strauss __attribute_returns_nonnull__
4938c87358SGlenn Strauss static char* buffer_realloc(buffer * const restrict b, const size_t len) {
502e23b43dSGlenn Strauss     #define BUFFER_PIECE_SIZE 64uL  /*(must be power-of-2)*/
5184fa4b8dSGlenn Strauss     size_t sz = (len + 1 + BUFFER_PIECE_SIZE-1) & ~(BUFFER_PIECE_SIZE-1);
5275bd40aaSGlenn Strauss     force_assert(sz > len);
5384fa4b8dSGlenn Strauss     if ((sz & (sz-1)) && sz < INT_MAX) {/* not power-2; huge val not expected */
5484fa4b8dSGlenn Strauss         /*(optimizer should recognize this and use ffs or clz or equivalent)*/
5584fa4b8dSGlenn Strauss         const size_t psz = sz;
5684fa4b8dSGlenn Strauss         for (sz = 256; sz < psz; sz <<= 1) ;
5784fa4b8dSGlenn Strauss     }
58ee9352b1SGlenn Strauss     sz |= 1; /*(extra +1 for '\0' when needed buffer size is exact power-2)*/
59bcdc6a3bSJan Kneschke 
6075bd40aaSGlenn Strauss     b->size = sz;
6175bd40aaSGlenn Strauss     b->ptr = realloc(b->ptr, sz);
62ad3e93eaSStefan Bühler 
636afad87dSStefan Bühler     force_assert(NULL != b->ptr);
641be163b4SStefan Bühler     return b->ptr;
651be163b4SStefan Bühler }
661be163b4SStefan Bühler 
67d22e7a76SGlenn Strauss __attribute_cold__
68d22e7a76SGlenn Strauss __attribute_noinline__
__attribute_nonnull__()69575665adSGlenn Strauss __attribute_nonnull__()
705c0c4936SGlenn Strauss __attribute_returns_nonnull__
7138c87358SGlenn Strauss static char* buffer_alloc_replace(buffer * const restrict b, const size_t size) {
7238c87358SGlenn Strauss     /*(discard old data so realloc() does not copy)*/
7338c87358SGlenn Strauss     if (NULL != b->ptr) {
7438c87358SGlenn Strauss         free(b->ptr);
7538c87358SGlenn Strauss         b->ptr = NULL;
7638c87358SGlenn Strauss     }
7738c87358SGlenn Strauss     /*(note: if size larger than one lshift, use size instead of power-2)*/
78ee9352b1SGlenn Strauss     const size_t bsize2x = (b->size & ~1uL) << 1;
79ee9352b1SGlenn Strauss     return buffer_realloc(b, bsize2x > size ? bsize2x-1 : size);
8038c87358SGlenn Strauss }
8138c87358SGlenn Strauss 
buffer_string_prepare_copy(buffer * const b,const size_t size)8238c87358SGlenn Strauss char* buffer_string_prepare_copy(buffer * const b, const size_t size) {
8338c87358SGlenn Strauss     b->used = 0;
84a3e9faa4SGlenn Strauss   #ifdef __COVERITY__ /*(b->ptr is not NULL if b->size is not 0)*/
85a3e9faa4SGlenn Strauss     force_assert(size >= b->size || b->ptr);
86a3e9faa4SGlenn Strauss   #endif
8738c87358SGlenn Strauss     return (size < b->size)
8838c87358SGlenn Strauss       ? b->ptr
8938c87358SGlenn Strauss       : buffer_alloc_replace(b, size);
9038c87358SGlenn Strauss }
9138c87358SGlenn Strauss 
9238c87358SGlenn Strauss __attribute_cold__
9338c87358SGlenn Strauss __attribute_noinline__
__attribute_nonnull__()94575665adSGlenn Strauss __attribute_nonnull__()
9538c87358SGlenn Strauss __attribute_returns_nonnull__
9638c87358SGlenn Strauss static char* buffer_string_prepare_append_resize(buffer * const restrict b, const size_t size) {
97af3df29aSGlenn Strauss     if (b->used < 2) {  /* buffer_is_blank(b) */
98af3df29aSGlenn Strauss         char * const s = buffer_string_prepare_copy(b, size);
99af3df29aSGlenn Strauss         *s = '\0'; /*(for case (1 == b->used))*/
100af3df29aSGlenn Strauss         return s;
101af3df29aSGlenn Strauss     }
10238c87358SGlenn Strauss 
103ad3e93eaSStefan Bühler     /* not empty, b->used already includes a terminating 0 */
10438c87358SGlenn Strauss     /*(note: if size larger than one lshift, use size instead of power-2)*/
105ee9352b1SGlenn Strauss     const size_t bsize2x = (b->size & ~1uL) << 1;
106ee9352b1SGlenn Strauss     const size_t req_size = (bsize2x - b->used > size)
107ee9352b1SGlenn Strauss       ? bsize2x-1
10838c87358SGlenn Strauss       : b->used + size;
109ad3e93eaSStefan Bühler 
1104365bdbeSStefan Bühler     /* check for overflow: unsigned overflow is defined to wrap around */
1114365bdbeSStefan Bühler     force_assert(req_size >= b->used);
1124365bdbeSStefan Bühler 
11338c87358SGlenn Strauss     return buffer_realloc(b, req_size) + b->used - 1;
1141be163b4SStefan Bühler }
1151be163b4SStefan Bühler 
buffer_string_prepare_append(buffer * const b,const size_t size)1165bbe5872SGlenn Strauss char* buffer_string_prepare_append(buffer * const b, const size_t size) {
11738c87358SGlenn Strauss     const uint32_t len = b->used ? b->used-1 : 0;
11838c87358SGlenn Strauss     return (b->size - len >= size + 1)
11938c87358SGlenn Strauss       ? b->ptr + len
120d22e7a76SGlenn Strauss       : buffer_string_prepare_append_resize(b, size);
121d22e7a76SGlenn Strauss }
122d22e7a76SGlenn Strauss 
12338c87358SGlenn Strauss /*(prefer smaller code than inlining buffer_extend in many places in buffer.c)*/
12438c87358SGlenn Strauss __attribute_noinline__
12538c87358SGlenn Strauss char*
buffer_extend(buffer * const b,const size_t x)12638c87358SGlenn Strauss buffer_extend (buffer * const b, const size_t x)
12738c87358SGlenn Strauss {
12838c87358SGlenn Strauss     /* extend buffer to append x (reallocate by power-2 (or larger), if needed)
12938c87358SGlenn Strauss      * (combine buffer_string_prepare_append() and buffer_commit())
13038c87358SGlenn Strauss      * (future: might make buffer.h static inline func for HTTP/1.1 performance)
13138c87358SGlenn Strauss      * pre-sets '\0' byte and b->used (unlike buffer_string_prepare_append())*/
132af3df29aSGlenn Strauss   #if 0
133af3df29aSGlenn Strauss     char * const s = buffer_string_prepare_append(b, x);
134af3df29aSGlenn Strauss     b->used += x + (0 == b->used);
135af3df29aSGlenn Strauss   #else
13638c87358SGlenn Strauss     const uint32_t len = b->used ? b->used-1 : 0;
13738c87358SGlenn Strauss     char * const s = (b->size - len >= x + 1)
13838c87358SGlenn Strauss       ? b->ptr + len
13938c87358SGlenn Strauss       : buffer_string_prepare_append_resize(b, x);
14038c87358SGlenn Strauss     b->used = len+x+1;
141af3df29aSGlenn Strauss   #endif
14238c87358SGlenn Strauss     s[x] = '\0';
14338c87358SGlenn Strauss     return s;
14438c87358SGlenn Strauss }
14538c87358SGlenn Strauss 
buffer_commit(buffer * b,size_t size)1466afad87dSStefan Bühler void buffer_commit(buffer *b, size_t size)
1476afad87dSStefan Bühler {
1488b96169bSGlenn Strauss 	size_t sz = b->used;
1498b96169bSGlenn Strauss 	if (0 == sz) sz = 1;
1506afad87dSStefan Bühler 
1516afad87dSStefan Bühler 	if (size > 0) {
1526afad87dSStefan Bühler 		/* check for overflow: unsigned overflow is defined to wrap around */
1538b96169bSGlenn Strauss 		sz += size;
1548b96169bSGlenn Strauss 		force_assert(sz > size);
1556afad87dSStefan Bühler 	}
1566afad87dSStefan Bühler 
1578b96169bSGlenn Strauss 	b->used = sz;
1588b96169bSGlenn Strauss 	b->ptr[sz - 1] = '\0';
1596afad87dSStefan Bühler }
1606afad87dSStefan Bühler 
161e174e7dfSGlenn Strauss __attribute_cold__ /*(reduce code size due to inlining)*/
buffer_copy_string(buffer * restrict b,const char * restrict s)1629914bb29SGlenn Strauss void buffer_copy_string(buffer * restrict b, const char * restrict s) {
163e174e7dfSGlenn Strauss     if (__builtin_expect( (NULL == s), 0)) s = "";
164e174e7dfSGlenn Strauss     buffer_copy_string_len(b, s, strlen(s));
1656afad87dSStefan Bühler }
1666afad87dSStefan Bühler 
buffer_copy_string_len(buffer * const restrict b,const char * const restrict s,const size_t len)16738c87358SGlenn Strauss void buffer_copy_string_len(buffer * const restrict b, const char * const restrict s, const size_t len) {
16838c87358SGlenn Strauss     b->used = len + 1;
16938c87358SGlenn Strauss     char * const restrict d = (len < b->size)
17038c87358SGlenn Strauss       ? b->ptr
17138c87358SGlenn Strauss       : buffer_alloc_replace(b, len);
17238c87358SGlenn Strauss     d[len] = '\0';
17338c87358SGlenn Strauss     memcpy(d, s, len);
174bcdc6a3bSJan Kneschke }
175bcdc6a3bSJan Kneschke 
176e174e7dfSGlenn Strauss __attribute_cold__ /*(reduce code size due to inlining)*/
buffer_append_string(buffer * restrict b,const char * restrict s)1779914bb29SGlenn Strauss void buffer_append_string(buffer * restrict b, const char * restrict s) {
178e174e7dfSGlenn Strauss     if (__builtin_expect( (NULL == s), 0)) s = "";
179e174e7dfSGlenn Strauss     buffer_append_string_len(b, s, strlen(s));
180bcdc6a3bSJan Kneschke }
181bcdc6a3bSJan Kneschke 
182bcdc6a3bSJan Kneschke /**
183bcdc6a3bSJan Kneschke  * append a string to the end of the buffer
184bcdc6a3bSJan Kneschke  *
185bcdc6a3bSJan Kneschke  * the resulting buffer is terminated with a '\0'
186bcdc6a3bSJan Kneschke  * s is treated as a un-terminated string (a \0 is handled a normal character)
187bcdc6a3bSJan Kneschke  *
188bcdc6a3bSJan Kneschke  * @param b a buffer
189bcdc6a3bSJan Kneschke  * @param s the string
190bcdc6a3bSJan Kneschke  * @param s_len size of the string (without the terminating \0)
191bcdc6a3bSJan Kneschke  */
192bcdc6a3bSJan Kneschke 
buffer_append_string_len(buffer * const restrict b,const char * const restrict s,const size_t len)19338c87358SGlenn Strauss void buffer_append_string_len(buffer * const restrict b, const char * const restrict s, const size_t len) {
19438c87358SGlenn Strauss     memcpy(buffer_extend(b, len), s, len);
195bcdc6a3bSJan Kneschke }
196bcdc6a3bSJan Kneschke 
buffer_append_str2(buffer * const restrict b,const char * const s1,const size_t len1,const char * const s2,const size_t len2)197e7464babSGlenn Strauss void buffer_append_str2(buffer * const restrict b, const char * const s1, const size_t len1, const char * const s2, const size_t len2) {
198e7464babSGlenn Strauss     char * const restrict s = buffer_extend(b, len1+len2);
199e7464babSGlenn Strauss   #ifdef HAVE_MEMPCPY
200e7464babSGlenn Strauss     mempcpy(mempcpy(s, s1, len1), s2, len2);
201e7464babSGlenn Strauss   #else
202e7464babSGlenn Strauss     memcpy(s,      s1, len1);
203e7464babSGlenn Strauss     memcpy(s+len1, s2, len2);
204e7464babSGlenn Strauss   #endif
205e7464babSGlenn Strauss }
206e7464babSGlenn Strauss 
buffer_append_str3(buffer * const restrict b,const char * const s1,const size_t len1,const char * const s2,const size_t len2,const char * const s3,const size_t len3)207e7464babSGlenn Strauss void buffer_append_str3(buffer * const restrict b, const char * const s1, const size_t len1, const char * const s2, const size_t len2, const char * const s3, const size_t len3) {
208e7464babSGlenn Strauss     char * restrict s = buffer_extend(b, len1+len2+len3);
209e7464babSGlenn Strauss   #ifdef HAVE_MEMPCPY
210e7464babSGlenn Strauss     mempcpy(mempcpy(mempcpy(s, s1, len1), s2, len2), s3, len3);
211e7464babSGlenn Strauss   #else
212e7464babSGlenn Strauss     memcpy(s,         s1, len1);
213e7464babSGlenn Strauss     memcpy((s+=len1), s2, len2);
214e7464babSGlenn Strauss     memcpy((s+=len2), s3, len3);
215e7464babSGlenn Strauss   #endif
216e7464babSGlenn Strauss }
217e7464babSGlenn Strauss 
buffer_append_iovec(buffer * const restrict b,const struct const_iovec * const iov,const size_t n)218e7464babSGlenn Strauss void buffer_append_iovec(buffer * const restrict b, const struct const_iovec * const iov, const size_t n) {
219e7464babSGlenn Strauss     size_t len = 0;
220e7464babSGlenn Strauss     for (size_t i = 0; i < n; ++i)
221e7464babSGlenn Strauss         len += iov[i].iov_len;
222e7464babSGlenn Strauss     char *s = buffer_extend(b, len);
223e7464babSGlenn Strauss     for (size_t i = 0; i < n; ++i) {
224e7464babSGlenn Strauss         if (0 == iov[i].iov_len) continue;
225e7464babSGlenn Strauss       #ifdef HAVE_MEMPCPY
226e7464babSGlenn Strauss         s = mempcpy(s, iov[i].iov_base, iov[i].iov_len);
227e7464babSGlenn Strauss       #else
228e7464babSGlenn Strauss         memcpy(s, iov[i].iov_base, iov[i].iov_len);
229e7464babSGlenn Strauss         s += iov[i].iov_len;
230e7464babSGlenn Strauss       #endif
231e7464babSGlenn Strauss     }
232e7464babSGlenn Strauss }
233e7464babSGlenn Strauss 
buffer_append_path_len(buffer * restrict b,const char * restrict a,size_t alen)2349914bb29SGlenn Strauss void buffer_append_path_len(buffer * restrict b, const char * restrict a, size_t alen) {
23538c87358SGlenn Strauss     char * restrict s = buffer_string_prepare_append(b, alen+1);
23638c87358SGlenn Strauss     const int aslash = (alen && a[0] == '/');
23738c87358SGlenn Strauss     if (b->used > 1 && s[-1] == '/') {
23838c87358SGlenn Strauss         if (aslash) {
23938c87358SGlenn Strauss             ++a;
24038c87358SGlenn Strauss             --alen;
24138c87358SGlenn Strauss         }
24277c01f98SGlenn Strauss     }
24377c01f98SGlenn Strauss     else {
24438c87358SGlenn Strauss         if (0 == b->used) b->used = 1;
24538c87358SGlenn Strauss         if (!aslash) {
24638c87358SGlenn Strauss             *s++ = '/';
24738c87358SGlenn Strauss             ++b->used;
24877c01f98SGlenn Strauss         }
24938c87358SGlenn Strauss     }
25038c87358SGlenn Strauss     b->used += alen;
25138c87358SGlenn Strauss     s[alen] = '\0';
25238c87358SGlenn Strauss     memcpy(s, a, alen);
25377c01f98SGlenn Strauss }
25477c01f98SGlenn Strauss 
255680e6b3bSGlenn Strauss void
buffer_copy_path_len2(buffer * const restrict b,const char * const restrict s1,size_t len1,const char * const restrict s2,size_t len2)256680e6b3bSGlenn Strauss buffer_copy_path_len2 (buffer * const restrict b, const char * const restrict s1, size_t len1, const char * const restrict s2, size_t len2)
257680e6b3bSGlenn Strauss {
258680e6b3bSGlenn Strauss     /*(similar to buffer_copy_string_len(b, s1, len1) but combined allocation)*/
259680e6b3bSGlenn Strauss     memcpy(buffer_string_prepare_copy(b, len1+len2+1), s1, len1);
260680e6b3bSGlenn Strauss     b->used = len1 + 1;                    /*('\0' byte will be written below)*/
261680e6b3bSGlenn Strauss 
262680e6b3bSGlenn Strauss     buffer_append_path_len(b, s2, len2);/*(choice: not inlined, special-cased)*/
263680e6b3bSGlenn Strauss }
264680e6b3bSGlenn Strauss 
265f490078dSGlenn Strauss void
buffer_copy_string_len_lc(buffer * const restrict b,const char * const restrict s,const size_t len)266f490078dSGlenn Strauss buffer_copy_string_len_lc (buffer * const restrict b, const char * const restrict s, const size_t len)
267f490078dSGlenn Strauss {
268f490078dSGlenn Strauss     char * const restrict d = buffer_string_prepare_copy(b, len);
269f490078dSGlenn Strauss     b->used = len+1;
270f490078dSGlenn Strauss     d[len] = '\0';
271f490078dSGlenn Strauss     for (size_t i = 0; i < len; ++i)
272f490078dSGlenn Strauss         d[i] = (!light_isupper(s[i])) ? s[i] : s[i] | 0x20;
273f490078dSGlenn Strauss }
274f490078dSGlenn Strauss 
buffer_append_uint_hex_lc(buffer * b,uintmax_t value)2753d880810SGlenn Strauss void buffer_append_uint_hex_lc(buffer *b, uintmax_t value) {
276bcdc6a3bSJan Kneschke 	char *buf;
277dadfb5fcSGlenn Strauss 	unsigned int shift = 0;
2786afad87dSStefan Bühler 
2796afad87dSStefan Bühler 	{
28091a9a6b3SStefan Bühler 		uintmax_t copy = value;
2816afad87dSStefan Bühler 		do {
2826afad87dSStefan Bühler 			copy >>= 8;
283dadfb5fcSGlenn Strauss 			shift += 8; /* counting bits */
2846afad87dSStefan Bühler 		} while (0 != copy);
285bcdc6a3bSJan Kneschke 	}
286bcdc6a3bSJan Kneschke 
28738c87358SGlenn Strauss 	buf = buffer_extend(b, shift >> 2); /*nibbles (4 bits)*/
288bcdc6a3bSJan Kneschke 
289bcdc6a3bSJan Kneschke 	while (shift > 0) {
290bcdc6a3bSJan Kneschke 		shift -= 4;
2913d880810SGlenn Strauss 		*(buf++) = hex_chars_lc[(value >> shift) & 0x0F];
292bcdc6a3bSJan Kneschke 	}
293bcdc6a3bSJan Kneschke }
294bcdc6a3bSJan Kneschke 
__attribute_nonnull__()295575665adSGlenn Strauss __attribute_nonnull__()
2965bbe5872SGlenn Strauss __attribute_returns_nonnull__
297f13752f3SGlenn Strauss static char* utostr(char buf[LI_ITOSTRING_LENGTH], uintmax_t val) {
298f13752f3SGlenn Strauss 	char *cur = buf+LI_ITOSTRING_LENGTH;
29905dc3d12SGlenn Strauss 	uintmax_t x;
3006afad87dSStefan Bühler 	do {
30105dc3d12SGlenn Strauss 		*(--cur) = (char) ('0' + (int)(val - (x = val/10) * 10));
30205dc3d12SGlenn Strauss 	} while (0 != (val = x));           /* val % 10 */
3036afad87dSStefan Bühler 	return cur;
304bcdc6a3bSJan Kneschke }
305bcdc6a3bSJan Kneschke 
__attribute_nonnull__()306575665adSGlenn Strauss __attribute_nonnull__()
3075bbe5872SGlenn Strauss __attribute_returns_nonnull__
308f13752f3SGlenn Strauss static char* itostr(char buf[LI_ITOSTRING_LENGTH], intmax_t val) {
30972b133f5SGlenn Strauss 	/* absolute value not defined for INTMAX_MIN, but can take absolute
31072b133f5SGlenn Strauss 	 * value of any negative number via twos complement cast to unsigned.
31172b133f5SGlenn Strauss 	 * negative sign is prepended after (now unsigned) value is converted
31272b133f5SGlenn Strauss 	 * to string */
31372b133f5SGlenn Strauss 	uintmax_t uval = val >= 0 ? (uintmax_t)val : ((uintmax_t)~val) + 1;
314f13752f3SGlenn Strauss 	char *cur = utostr(buf, uval);
31572b133f5SGlenn Strauss 	if (val < 0) *(--cur) = '-';
31675fae49eSJan Kneschke 
3176afad87dSStefan Bühler 	return cur;
318bcdc6a3bSJan Kneschke }
319bcdc6a3bSJan Kneschke 
buffer_append_int(buffer * b,intmax_t val)3206afad87dSStefan Bühler void buffer_append_int(buffer *b, intmax_t val) {
3216afad87dSStefan Bühler 	char buf[LI_ITOSTRING_LENGTH];
322f13752f3SGlenn Strauss 	const char * const str = itostr(buf, val);
323f13752f3SGlenn Strauss 	buffer_append_string_len(b, str, buf+sizeof(buf) - str);
324bcdc6a3bSJan Kneschke }
325bcdc6a3bSJan Kneschke 
buffer_append_strftime(buffer * const restrict b,const char * const restrict format,const struct tm * const restrict tm)3269914bb29SGlenn Strauss void buffer_append_strftime(buffer * const restrict b, const char * const restrict format, const struct tm * const restrict tm) {
327be8ff839SGlenn Strauss     /*(localtime_r() or gmtime_r() producing tm should not have failed)*/
328be8ff839SGlenn Strauss     if (__builtin_expect( (NULL == tm), 0)) return;
329ad3e93eaSStefan Bühler 
330be8ff839SGlenn Strauss     /*(expecting typical format strings to result in < 64 bytes needed;
331be8ff839SGlenn Strauss      * skipping buffer_string_space() calculation and providing fixed size)*/
332be8ff839SGlenn Strauss     size_t rv = strftime(buffer_string_prepare_append(b, 63), 64, format, tm);
333ad3e93eaSStefan Bühler 
334be8ff839SGlenn Strauss     /* 0 (in some apis) signals the string may have been too small;
335be8ff839SGlenn Strauss      * but the format could also just have lead to an empty string */
336be8ff839SGlenn Strauss     if (__builtin_expect( (0 == rv), 0) || __builtin_expect( (rv > 63), 0)) {
337be8ff839SGlenn Strauss         /* unexpected; give it a second try with a larger string */
338be8ff839SGlenn Strauss         rv = strftime(buffer_string_prepare_append(b, 4095), 4096, format, tm);
339be8ff839SGlenn Strauss         if (__builtin_expect( (rv > 4095), 0))/*(input format was ridiculous)*/
340be8ff839SGlenn Strauss             return;
341ad3e93eaSStefan Bühler     }
342ad3e93eaSStefan Bühler 
343be8ff839SGlenn Strauss     /*buffer_commit(b, rv);*/
344be8ff839SGlenn Strauss     b->used += (uint32_t)rv + (0 == b->used);
345ad3e93eaSStefan Bühler }
346ad3e93eaSStefan Bühler 
347ad3e93eaSStefan Bühler 
li_itostrn(char * buf,size_t buf_len,intmax_t val)348f24e6d69SGlenn Strauss size_t li_itostrn(char *buf, size_t buf_len, intmax_t val) {
3496afad87dSStefan Bühler 	char p_buf[LI_ITOSTRING_LENGTH];
350f13752f3SGlenn Strauss 	char* const str = itostr(p_buf, val);
351f24e6d69SGlenn Strauss 	size_t len = (size_t)(p_buf+sizeof(p_buf)-str);
352f24e6d69SGlenn Strauss 	force_assert(len <= buf_len);
353f24e6d69SGlenn Strauss 	memcpy(buf, str, len);
354f24e6d69SGlenn Strauss 	return len;
355bcdc6a3bSJan Kneschke }
356bcdc6a3bSJan Kneschke 
li_utostrn(char * buf,size_t buf_len,uintmax_t val)357f24e6d69SGlenn Strauss size_t li_utostrn(char *buf, size_t buf_len, uintmax_t val) {
3586afad87dSStefan Bühler 	char p_buf[LI_ITOSTRING_LENGTH];
359f13752f3SGlenn Strauss 	char* const str = utostr(p_buf, val);
360f24e6d69SGlenn Strauss 	size_t len = (size_t)(p_buf+sizeof(p_buf)-str);
361f24e6d69SGlenn Strauss 	force_assert(len <= buf_len);
362f24e6d69SGlenn Strauss 	memcpy(buf, str, len);
363f24e6d69SGlenn Strauss 	return len;
364bcdc6a3bSJan Kneschke }
365bcdc6a3bSJan Kneschke 
3669eda625dSGlenn Strauss #define li_ntox_lc(n) ((n) <= 9 ? (n) + '0' : (n) + 'a' - 10)
3679eda625dSGlenn Strauss 
3689eda625dSGlenn Strauss /* c (char) and n (nibble) MUST be unsigned integer types */
3699eda625dSGlenn Strauss #define li_cton(c,n) \
3709eda625dSGlenn Strauss   (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0))
3719eda625dSGlenn Strauss 
372bcdc6a3bSJan Kneschke /* converts hex char (0-9, A-Z, a-z) to decimal.
373bcdc6a3bSJan Kneschke  * returns 0xFF on invalid input.
374bcdc6a3bSJan Kneschke  */
hex2int(unsigned char hex)375bcdc6a3bSJan Kneschke char hex2int(unsigned char hex) {
3769eda625dSGlenn Strauss 	unsigned char n;
3779eda625dSGlenn Strauss 	return li_cton(hex,n) ? (char)n : 0xFF;
378bcdc6a3bSJan Kneschke }
379bcdc6a3bSJan Kneschke 
li_hex2bin(unsigned char * const bin,const size_t binlen,const char * const hexstr,const size_t len)380e110b062SGlenn Strauss int li_hex2bin (unsigned char * const bin, const size_t binlen, const char * const hexstr, const size_t len)
381e110b062SGlenn Strauss {
382e110b062SGlenn Strauss     /* validate and transform 32-byte MD5 hex string to 16-byte binary MD5,
383e110b062SGlenn Strauss      * or 64-byte SHA-256 or SHA-512-256 hex string to 32-byte binary digest */
384e110b062SGlenn Strauss     if (len > (binlen << 1)) return -1;
385e110b062SGlenn Strauss     for (int i = 0, ilen = (int)len; i < ilen; i+=2) {
386e110b062SGlenn Strauss         int hi = hexstr[i];
387e110b062SGlenn Strauss         int lo = hexstr[i+1];
388e110b062SGlenn Strauss         if ('0' <= hi && hi <= '9')                    hi -= '0';
389e110b062SGlenn Strauss         else if ((uint32_t)(hi |= 0x20)-'a' <= 'f'-'a')hi += -'a' + 10;
390e110b062SGlenn Strauss         else                                           return -1;
391e110b062SGlenn Strauss         if ('0' <= lo && lo <= '9')                    lo -= '0';
392e110b062SGlenn Strauss         else if ((uint32_t)(lo |= 0x20)-'a' <= 'f'-'a')lo += -'a' + 10;
393e110b062SGlenn Strauss         else                                           return -1;
394e110b062SGlenn Strauss         bin[(i >> 1)] = (unsigned char)((hi << 4) | lo);
395e110b062SGlenn Strauss     }
396e110b062SGlenn Strauss     return 0;
397e110b062SGlenn Strauss }
398e110b062SGlenn Strauss 
399ac8444f2SGlenn Strauss 
400e89d7513SGlenn Strauss __attribute_noinline__
buffer_eq_icase_ssn(const char * const a,const char * const b,const size_t len)401ac8444f2SGlenn Strauss int buffer_eq_icase_ssn(const char * const a, const char * const b, const size_t len) {
402ac8444f2SGlenn Strauss     for (size_t i = 0; i < len; ++i) {
403ac8444f2SGlenn Strauss         unsigned int ca = ((unsigned char *)a)[i];
404ac8444f2SGlenn Strauss         unsigned int cb = ((unsigned char *)b)[i];
405aaf36f58SGlenn Strauss         if (ca != cb && ((ca ^ cb) != 0x20 || !light_isalpha(ca))) return 0;
406ac8444f2SGlenn Strauss     }
407ac8444f2SGlenn Strauss     return 1;
408ac8444f2SGlenn Strauss }
409ac8444f2SGlenn Strauss 
buffer_eq_icase_ss(const char * const a,const size_t alen,const char * const b,const size_t blen)410ac8444f2SGlenn Strauss int buffer_eq_icase_ss(const char * const a, const size_t alen, const char * const b, const size_t blen) {
411ac8444f2SGlenn Strauss     /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */
412e89d7513SGlenn Strauss     return (alen == blen) ? buffer_eq_icase_ssn(a, b, blen) : 0;
413ac8444f2SGlenn Strauss }
414ac8444f2SGlenn Strauss 
buffer_eq_icase_slen(const buffer * const b,const char * const s,const size_t slen)415ac8444f2SGlenn Strauss int buffer_eq_icase_slen(const buffer * const b, const char * const s, const size_t slen) {
416ac8444f2SGlenn Strauss     /* Note: b must be initialized, i.e. 0 != b->used; uninitialized is not eq*/
417ac8444f2SGlenn Strauss     /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */
418e89d7513SGlenn Strauss     return (b->used == slen + 1) ? buffer_eq_icase_ssn(b->ptr, s, slen) : 0;
419ac8444f2SGlenn Strauss }
420ac8444f2SGlenn Strauss 
buffer_eq_slen(const buffer * const b,const char * const s,const size_t slen)421ac8444f2SGlenn Strauss int buffer_eq_slen(const buffer * const b, const char * const s, const size_t slen) {
422ac8444f2SGlenn Strauss     /* Note: b must be initialized, i.e. 0 != b->used; uninitialized is not eq*/
423ac8444f2SGlenn Strauss     /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */
424ac8444f2SGlenn Strauss     return (b->used == slen + 1 && 0 == memcmp(b->ptr, s, slen));
425ac8444f2SGlenn Strauss }
426ac8444f2SGlenn Strauss 
427ac8444f2SGlenn Strauss 
428bcdc6a3bSJan Kneschke /**
429bcdc6a3bSJan Kneschke  * check if two buffer contain the same data
430bcdc6a3bSJan Kneschke  */
431bcdc6a3bSJan Kneschke 
buffer_is_equal(const buffer * a,const buffer * b)43266ad587fSStefan Bühler int buffer_is_equal(const buffer *a, const buffer *b) {
433877ac294SGlenn Strauss 	/* 1 = equal; 0 = not equal */
434877ac294SGlenn Strauss 	return (a->used == b->used && 0 == memcmp(a->ptr, b->ptr, a->used));
435bcdc6a3bSJan Kneschke }
436bcdc6a3bSJan Kneschke 
437d3cf141dSGlenn Strauss 
li_tohex_lc(char * const restrict buf,size_t buf_len,const char * const restrict s,size_t s_len)4389914bb29SGlenn Strauss void li_tohex_lc(char * const restrict buf, size_t buf_len, const char * const restrict s, size_t s_len) {
439e5006d88SGlenn Strauss 	force_assert(2 * s_len > s_len);
440e5006d88SGlenn Strauss 	force_assert(2 * s_len < buf_len);
441bcdc6a3bSJan Kneschke 
442d3cf141dSGlenn Strauss 	for (size_t i = 0; i < s_len; ++i) {
443d3cf141dSGlenn Strauss 		buf[2*i]   = hex_chars_lc[(s[i] >> 4) & 0x0F];
444d3cf141dSGlenn Strauss 		buf[2*i+1] = hex_chars_lc[s[i] & 0x0F];
445bcdc6a3bSJan Kneschke 	}
446ad3e93eaSStefan Bühler 	buf[2*s_len] = '\0';
447ad3e93eaSStefan Bühler }
448ad3e93eaSStefan Bühler 
li_tohex_uc(char * const restrict buf,size_t buf_len,const char * const restrict s,size_t s_len)4499914bb29SGlenn Strauss void li_tohex_uc(char * const restrict buf, size_t buf_len, const char * const restrict s, size_t s_len) {
450d3cf141dSGlenn Strauss 	force_assert(2 * s_len > s_len);
451d3cf141dSGlenn Strauss 	force_assert(2 * s_len < buf_len);
452ad3e93eaSStefan Bühler 
453d3cf141dSGlenn Strauss 	for (size_t i = 0; i < s_len; ++i) {
454d3cf141dSGlenn Strauss 		buf[2*i]   = hex_chars_uc[(s[i] >> 4) & 0x0F];
455d3cf141dSGlenn Strauss 		buf[2*i+1] = hex_chars_uc[s[i] & 0x0F];
456d3cf141dSGlenn Strauss 	}
457d3cf141dSGlenn Strauss 	buf[2*s_len] = '\0';
458bcdc6a3bSJan Kneschke }
459bcdc6a3bSJan Kneschke 
46005c34ce4SGlenn Strauss 
buffer_substr_replace(buffer * const restrict b,const size_t offset,const size_t len,const buffer * const restrict replace)4619914bb29SGlenn Strauss void buffer_substr_replace (buffer * const restrict b, const size_t offset,
4629914bb29SGlenn Strauss                             const size_t len, const buffer * const restrict replace)
46305c34ce4SGlenn Strauss {
464af3df29aSGlenn Strauss     const size_t blen = buffer_clen(b);
465af3df29aSGlenn Strauss     const size_t rlen = buffer_clen(replace);
46605c34ce4SGlenn Strauss 
46705c34ce4SGlenn Strauss     if (rlen > len) {
46810d5786fSGlenn Strauss         buffer_extend(b, rlen-len);
46905c34ce4SGlenn Strauss         memmove(b->ptr+offset+rlen, b->ptr+offset+len, blen-offset-len);
47005c34ce4SGlenn Strauss     }
47105c34ce4SGlenn Strauss 
47205c34ce4SGlenn Strauss     memcpy(b->ptr+offset, replace->ptr, rlen);
47305c34ce4SGlenn Strauss 
47405c34ce4SGlenn Strauss     if (rlen < len) {
47505c34ce4SGlenn Strauss         memmove(b->ptr+offset+rlen, b->ptr+offset+len, blen-offset-len);
476af3df29aSGlenn Strauss         buffer_truncate(b, blen-len+rlen);
47705c34ce4SGlenn Strauss     }
47805c34ce4SGlenn Strauss }
47905c34ce4SGlenn Strauss 
48005c34ce4SGlenn Strauss 
buffer_append_string_encoded_hex_lc(buffer * const restrict b,const char * const restrict s,size_t len)4819914bb29SGlenn Strauss void buffer_append_string_encoded_hex_lc(buffer * const restrict b, const char * const restrict s, size_t len) {
48238c87358SGlenn Strauss     unsigned char * const p = (unsigned char *)buffer_extend(b, len*2);
483936db51fSGlenn Strauss     for (size_t i = 0; i < len; ++i) {
484936db51fSGlenn Strauss         p[(i<<1)]   = hex_chars_lc[(s[i] >> 4) & 0x0F];
485936db51fSGlenn Strauss         p[(i<<1)+1] = hex_chars_lc[(s[i])      & 0x0F];
486936db51fSGlenn Strauss     }
487936db51fSGlenn Strauss }
488936db51fSGlenn Strauss 
buffer_append_string_encoded_hex_uc(buffer * const restrict b,const char * const restrict s,size_t len)4899914bb29SGlenn Strauss void buffer_append_string_encoded_hex_uc(buffer * const restrict b, const char * const restrict s, size_t len) {
49038c87358SGlenn Strauss     unsigned char * const p = (unsigned char *)buffer_extend(b, len*2);
491936db51fSGlenn Strauss     for (size_t i = 0; i < len; ++i) {
492936db51fSGlenn Strauss         p[(i<<1)]   = hex_chars_uc[(s[i] >> 4) & 0x0F];
493936db51fSGlenn Strauss         p[(i<<1)+1] = hex_chars_uc[(s[i])      & 0x0F];
494936db51fSGlenn Strauss     }
495936db51fSGlenn Strauss }
496936db51fSGlenn Strauss 
497936db51fSGlenn Strauss 
498375022a1SGlenn Strauss /* everything except: ! ( ) * - . 0-9 A-Z _ a-z */
499f4ba2d4fSCyril Brulebois static const char encoded_chars_rel_uri_part[] = {
500976f3218SJan Kneschke 	/*
501976f3218SJan Kneschke 	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
502976f3218SJan Kneschke 	*/
503976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
504976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
505976f3218SJan Kneschke 	1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,  /*  20 -  2F space " # $ % & ' + , / */
506d62f8943SStefan Bühler 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,  /*  30 -  3F : ; < = > ? */
507d62f8943SStefan Bühler 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F @ */
508d62f8943SStefan Bühler 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /*  50 -  5F [ \ ] ^ */
509d62f8943SStefan Bühler 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
510375022a1SGlenn Strauss 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  70 -  7F { | } DEL */
511976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  80 -  8F */
512976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  90 -  9F */
513976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  A0 -  AF */
514976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  B0 -  BF */
515976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  C0 -  CF */
516976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  D0 -  DF */
517976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  E0 -  EF */
518976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  F0 -  FF */
519976f3218SJan Kneschke };
520bcdc6a3bSJan Kneschke 
521375022a1SGlenn Strauss /* everything except: ! ( ) * - . / 0-9 A-Z _ a-z */
522f4ba2d4fSCyril Brulebois static const char encoded_chars_rel_uri[] = {
523976f3218SJan Kneschke 	/*
524976f3218SJan Kneschke 	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
525976f3218SJan Kneschke 	*/
526976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
527976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
528d62f8943SStefan Bühler 	1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0,  /*  20 -  2F space " # $ % & ' + , */
529d62f8943SStefan Bühler 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,  /*  30 -  3F : ; < = > ? */
530d62f8943SStefan Bühler 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F @ */
531d62f8943SStefan Bühler 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /*  50 -  5F [ \ ] ^ */
532d62f8943SStefan Bühler 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
533375022a1SGlenn Strauss 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  70 -  7F { | } DEL */
534976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  80 -  8F */
535976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  90 -  9F */
536976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  A0 -  AF */
537976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  B0 -  BF */
538976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  C0 -  CF */
539976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  D0 -  DF */
540976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  E0 -  EF */
541976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  F0 -  FF */
542976f3218SJan Kneschke };
543bcdc6a3bSJan Kneschke 
544f4ba2d4fSCyril Brulebois static const char encoded_chars_html[] = {
545976f3218SJan Kneschke 	/*
546976f3218SJan Kneschke 	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
547976f3218SJan Kneschke 	*/
548976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
549976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
5505863d05eSGlenn Strauss 	0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,  /*  20 -  2F " & ' */
551976f3218SJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,  /*  30 -  3F < > */
552976f3218SJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F */
553976f3218SJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  50 -  5F */
5545863d05eSGlenn Strauss 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
555976f3218SJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  /*  70 -  7F DEL */
556976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  80 -  8F */
557976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  90 -  9F */
558976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  A0 -  AF */
559976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  B0 -  BF */
560976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  C0 -  CF */
561976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  D0 -  DF */
562976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  E0 -  EF */
563976f3218SJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  F0 -  FF */
564976f3218SJan Kneschke };
565bcdc6a3bSJan Kneschke 
566f4ba2d4fSCyril Brulebois static const char encoded_chars_minimal_xml[] = {
567a1e6331fSJan Kneschke 	/*
568a1e6331fSJan Kneschke 	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
569a1e6331fSJan Kneschke 	*/
570a1e6331fSJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
571a1e6331fSJan Kneschke 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
5725863d05eSGlenn Strauss 	0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,  /*  20 -  2F " & ' */
573a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,  /*  30 -  3F < > */
574a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F */
575a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  50 -  5F */
5765863d05eSGlenn Strauss 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
577a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  /*  70 -  7F DEL */
578a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  80 -  8F */
579a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  90 -  9F */
580a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  A0 -  AF */
581a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  B0 -  BF */
582a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  C0 -  CF */
583a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  D0 -  DF */
584a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  E0 -  EF */
585a1e6331fSJan Kneschke 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  F0 -  FF */
586a1e6331fSJan Kneschke };
587a1e6331fSJan Kneschke 
588a0436ceaSJan Kneschke 
589bcdc6a3bSJan Kneschke 
buffer_append_string_encoded(buffer * const restrict b,const char * const restrict s,size_t s_len,buffer_encoding_t encoding)5909914bb29SGlenn Strauss void buffer_append_string_encoded(buffer * const restrict b, const char * const restrict s, size_t s_len, buffer_encoding_t encoding) {
591bcdc6a3bSJan Kneschke 	unsigned char *ds, *d;
5926770984aSJan Kneschke 	size_t d_len, ndx;
593976f3218SJan Kneschke 	const char *map = NULL;
594bcdc6a3bSJan Kneschke 
595976f3218SJan Kneschke 	switch(encoding) {
596976f3218SJan Kneschke 	case ENCODING_REL_URI:
597976f3218SJan Kneschke 		map = encoded_chars_rel_uri;
598bcdc6a3bSJan Kneschke 		break;
599976f3218SJan Kneschke 	case ENCODING_REL_URI_PART:
600976f3218SJan Kneschke 		map = encoded_chars_rel_uri_part;
601976f3218SJan Kneschke 		break;
602976f3218SJan Kneschke 	case ENCODING_HTML:
603976f3218SJan Kneschke 		map = encoded_chars_html;
604976f3218SJan Kneschke 		break;
605a1e6331fSJan Kneschke 	case ENCODING_MINIMAL_XML:
606a1e6331fSJan Kneschke 		map = encoded_chars_minimal_xml;
607a1e6331fSJan Kneschke 		break;
608bcdc6a3bSJan Kneschke 	}
609976f3218SJan Kneschke 
610976f3218SJan Kneschke 	/* count to-be-encoded-characters */
6116770984aSJan Kneschke 	for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
6122781a3beSGlenn Strauss 		if (map[*ds & 0xFF]) {
613976f3218SJan Kneschke 			switch(encoding) {
614976f3218SJan Kneschke 			case ENCODING_REL_URI:
615976f3218SJan Kneschke 			case ENCODING_REL_URI_PART:
616976f3218SJan Kneschke 				d_len += 3;
617976f3218SJan Kneschke 				break;
618976f3218SJan Kneschke 			case ENCODING_HTML:
619a1e6331fSJan Kneschke 			case ENCODING_MINIMAL_XML:
620976f3218SJan Kneschke 				d_len += 6;
621976f3218SJan Kneschke 				break;
622976f3218SJan Kneschke 			}
623976f3218SJan Kneschke 		} else {
624976f3218SJan Kneschke 			d_len++;
625bcdc6a3bSJan Kneschke 		}
626bcdc6a3bSJan Kneschke 	}
627bcdc6a3bSJan Kneschke 
62838c87358SGlenn Strauss 	d = (unsigned char*) buffer_extend(b, d_len);
629bcdc6a3bSJan Kneschke 
63012acca18SGlenn Strauss 	if (d_len == s_len) { /*(short-circuit; nothing to encoded)*/
63112acca18SGlenn Strauss 		memcpy(d, s, s_len);
63212acca18SGlenn Strauss 		return;
63312acca18SGlenn Strauss 	}
63412acca18SGlenn Strauss 
6356afad87dSStefan Bühler 	for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
6362781a3beSGlenn Strauss 		if (map[*ds & 0xFF]) {
637976f3218SJan Kneschke 			switch(encoding) {
638976f3218SJan Kneschke 			case ENCODING_REL_URI:
639976f3218SJan Kneschke 			case ENCODING_REL_URI_PART:
640bcdc6a3bSJan Kneschke 				d[d_len++] = '%';
64115931906SGlenn Strauss 				d[d_len++] = hex_chars_uc[((*ds) >> 4) & 0x0F];
64215931906SGlenn Strauss 				d[d_len++] = hex_chars_uc[(*ds) & 0x0F];
643976f3218SJan Kneschke 				break;
644976f3218SJan Kneschke 			case ENCODING_HTML:
645a1e6331fSJan Kneschke 			case ENCODING_MINIMAL_XML:
646976f3218SJan Kneschke 				d[d_len++] = '&';
647976f3218SJan Kneschke 				d[d_len++] = '#';
648976f3218SJan Kneschke 				d[d_len++] = 'x';
64915931906SGlenn Strauss 				d[d_len++] = hex_chars_uc[((*ds) >> 4) & 0x0F];
65015931906SGlenn Strauss 				d[d_len++] = hex_chars_uc[(*ds) & 0x0F];
651976f3218SJan Kneschke 				d[d_len++] = ';';
652976f3218SJan Kneschke 				break;
653976f3218SJan Kneschke 			}
654bcdc6a3bSJan Kneschke 		} else {
655bcdc6a3bSJan Kneschke 			d[d_len++] = *ds;
656bcdc6a3bSJan Kneschke 		}
657bcdc6a3bSJan Kneschke 	}
658bcdc6a3bSJan Kneschke }
659bcdc6a3bSJan Kneschke 
buffer_append_string_c_escaped(buffer * const restrict b,const char * const restrict s,size_t s_len)6609914bb29SGlenn Strauss void buffer_append_string_c_escaped(buffer * const restrict b, const char * const restrict s, size_t s_len) {
661427120b4SStefan Bühler 	unsigned char *ds, *d;
662427120b4SStefan Bühler 	size_t d_len, ndx;
663427120b4SStefan Bühler 
664427120b4SStefan Bühler 	/* count to-be-encoded-characters */
665427120b4SStefan Bühler 	for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
666fc32d4c9SGlenn Strauss 		if (__builtin_expect( (*ds >= ' ' && *ds <= '~'), 1))
667fc32d4c9SGlenn Strauss 			d_len++;
668fc32d4c9SGlenn Strauss 		else { /* CTLs or non-ASCII characters */
669427120b4SStefan Bühler 			switch (*ds) {
670427120b4SStefan Bühler 			case '\t':
671427120b4SStefan Bühler 			case '\r':
672427120b4SStefan Bühler 			case '\n':
673427120b4SStefan Bühler 				d_len += 2;
674427120b4SStefan Bühler 				break;
675427120b4SStefan Bühler 			default:
676427120b4SStefan Bühler 				d_len += 4; /* \xCC */
677427120b4SStefan Bühler 				break;
678427120b4SStefan Bühler 			}
679427120b4SStefan Bühler 		}
680427120b4SStefan Bühler 	}
681427120b4SStefan Bühler 
68238c87358SGlenn Strauss 	d = (unsigned char*) buffer_extend(b, d_len);
683427120b4SStefan Bühler 
68412acca18SGlenn Strauss 	if (d_len == s_len) { /*(short-circuit; nothing to encoded)*/
68512acca18SGlenn Strauss 		memcpy(d, s, s_len);
68612acca18SGlenn Strauss 		return;
68712acca18SGlenn Strauss 	}
68812acca18SGlenn Strauss 
689427120b4SStefan Bühler 	for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
690fc32d4c9SGlenn Strauss 		if (__builtin_expect( (*ds >= ' ' && *ds <= '~'), 1))
691fc32d4c9SGlenn Strauss 			d[d_len++] = *ds;
692fc32d4c9SGlenn Strauss 		else { /* CTLs or non-ASCII characters */
693427120b4SStefan Bühler 			d[d_len++] = '\\';
694427120b4SStefan Bühler 			switch (*ds) {
695427120b4SStefan Bühler 			case '\t':
696427120b4SStefan Bühler 				d[d_len++] = 't';
697427120b4SStefan Bühler 				break;
698427120b4SStefan Bühler 			case '\r':
699427120b4SStefan Bühler 				d[d_len++] = 'r';
700427120b4SStefan Bühler 				break;
701427120b4SStefan Bühler 			case '\n':
702427120b4SStefan Bühler 				d[d_len++] = 'n';
703427120b4SStefan Bühler 				break;
704427120b4SStefan Bühler 			default:
705427120b4SStefan Bühler 				d[d_len++] = 'x';
706fc32d4c9SGlenn Strauss 				d[d_len++] = hex_chars_lc[(*ds) >> 4];
70715931906SGlenn Strauss 				d[d_len++] = hex_chars_lc[(*ds) & 0x0F];
708427120b4SStefan Bühler 				break;
709427120b4SStefan Bühler 			}
710427120b4SStefan Bühler 		}
711427120b4SStefan Bühler 	}
712427120b4SStefan Bühler }
713427120b4SStefan Bühler 
714427120b4SStefan Bühler 
715d22e88b7SGlenn Strauss void
buffer_append_bs_escaped(buffer * const restrict b,const char * restrict s,const size_t len)716d22e88b7SGlenn Strauss buffer_append_bs_escaped (buffer * const restrict b,
71791ce3b08SGlenn Strauss                           const char * restrict s, const size_t len)
718d22e88b7SGlenn Strauss {
719d22e88b7SGlenn Strauss     /* replaces non-printable chars with escaped string
720d22e88b7SGlenn Strauss      * default: \xHH where HH is the hex representation of the byte
721d22e88b7SGlenn Strauss      * exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
722d22e88b7SGlenn Strauss     /* Intended for use escaping string to be surrounded by double-quotes */
723d22e88b7SGlenn Strauss     /* Performs single pass over string and is optimized for ASCII;
724d22e88b7SGlenn Strauss      * non-ASCII escaping might be slightly sped up by walking input twice,
725d22e88b7SGlenn Strauss      * first to calculate escaped length and extend the destination b, and
726d22e88b7SGlenn Strauss      * second to do the escaping. (This non-ASCII optim is not done here) */
727d22e88b7SGlenn Strauss     buffer_string_prepare_append(b, len);
728d22e88b7SGlenn Strauss     for (const char * const end = s+len; s < end; ++s) {
729d22e88b7SGlenn Strauss         unsigned int c;
730d22e88b7SGlenn Strauss         const char * const ptr = s;
731d22e88b7SGlenn Strauss         do {
732d22e88b7SGlenn Strauss             c = *(const unsigned char *)s;
733d22e88b7SGlenn Strauss         } while (c >= ' ' && c <= '~' && c != '"' && c != '\\' && ++s < end);
734d22e88b7SGlenn Strauss         if (s - ptr) buffer_append_string_len(b, ptr, s - ptr);
735d22e88b7SGlenn Strauss 
736d22e88b7SGlenn Strauss         if (s == end)
737d22e88b7SGlenn Strauss             return;
738d22e88b7SGlenn Strauss 
739d22e88b7SGlenn Strauss         /* ('\a', '\v' shortcuts are technically not json-escaping) */
740d22e88b7SGlenn Strauss         /* ('\0' is also omitted due to the possibility of string corruption if
741d22e88b7SGlenn Strauss          *  the receiver supports decoding octal escapes (\000) and the escaped
742d22e88b7SGlenn Strauss          *  string contains \0 followed by two digits not part of escaping)*/
743d22e88b7SGlenn Strauss 
744d22e88b7SGlenn Strauss         char *d;
745d22e88b7SGlenn Strauss         switch (c) {
746d22e88b7SGlenn Strauss           case '\a':case '\b':case '\t':case '\n':case '\v':case '\f':case '\r':
747d22e88b7SGlenn Strauss             c = "0000000abtnvfr"[c];
748d22e88b7SGlenn Strauss             __attribute_fallthrough__
749d22e88b7SGlenn Strauss           case '"': case '\\':
750d22e88b7SGlenn Strauss             d = buffer_extend(b, 2);
751d22e88b7SGlenn Strauss             d[0] = '\\';
752d22e88b7SGlenn Strauss             d[1] = c;
753d22e88b7SGlenn Strauss             break;
754d22e88b7SGlenn Strauss           default:
755d22e88b7SGlenn Strauss             /* non printable char => \xHH */
756d22e88b7SGlenn Strauss             d = buffer_extend(b, 4);
757d22e88b7SGlenn Strauss             d[0] = '\\';
758d22e88b7SGlenn Strauss             d[1] = 'x';
75991ce3b08SGlenn Strauss             d[2] = hex_chars_uc[c >> 4];
76091ce3b08SGlenn Strauss             d[3] = hex_chars_uc[c & 0xF];
761ca407dcaSGlenn Strauss             break;
762ca407dcaSGlenn Strauss         }
76391ce3b08SGlenn Strauss     }
76491ce3b08SGlenn Strauss }
76591ce3b08SGlenn Strauss 
76691ce3b08SGlenn Strauss 
76791ce3b08SGlenn Strauss void
buffer_append_bs_escaped_json(buffer * const restrict b,const char * restrict s,const size_t len)76891ce3b08SGlenn Strauss buffer_append_bs_escaped_json (buffer * const restrict b,
76991ce3b08SGlenn Strauss                                const char * restrict s, const size_t len)
77091ce3b08SGlenn Strauss {
77191ce3b08SGlenn Strauss     /* replaces non-printable chars with escaped string
77291ce3b08SGlenn Strauss      * json: \u00HH where HH is the hex representation of the byte
77391ce3b08SGlenn Strauss      * exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
77491ce3b08SGlenn Strauss     /* Intended for use escaping string to be surrounded by double-quotes */
77591ce3b08SGlenn Strauss     buffer_string_prepare_append(b, len);
77691ce3b08SGlenn Strauss     for (const char * const end = s+len; s < end; ++s) {
77791ce3b08SGlenn Strauss         unsigned int c;
77891ce3b08SGlenn Strauss         const char * const ptr = s;
77991ce3b08SGlenn Strauss         do {
78091ce3b08SGlenn Strauss             c = *(const unsigned char *)s;
78191ce3b08SGlenn Strauss         } while (c >= ' ' && c != '"' && c != '\\' && ++s < end);
78291ce3b08SGlenn Strauss         if (s - ptr) buffer_append_string_len(b, ptr, s - ptr);
78391ce3b08SGlenn Strauss 
78491ce3b08SGlenn Strauss         if (s == end)
78591ce3b08SGlenn Strauss             return;
78691ce3b08SGlenn Strauss 
78791ce3b08SGlenn Strauss         /* ('\a', '\v' shortcuts are technically not json-escaping) */
78891ce3b08SGlenn Strauss         /* ('\0' is also omitted due to the possibility of string corruption if
78991ce3b08SGlenn Strauss          *  the receiver supports decoding octal escapes (\000) and the escaped
79091ce3b08SGlenn Strauss          *  string contains \0 followed by two digits not part of escaping)*/
79191ce3b08SGlenn Strauss 
79291ce3b08SGlenn Strauss         char *d;
79391ce3b08SGlenn Strauss         switch (c) {
79491ce3b08SGlenn Strauss           case '\a':case '\b':case '\t':case '\n':case '\v':case '\f':case '\r':
79591ce3b08SGlenn Strauss             c = "0000000abtnvfr"[c];
79691ce3b08SGlenn Strauss             __attribute_fallthrough__
79791ce3b08SGlenn Strauss           case '"': case '\\':
79891ce3b08SGlenn Strauss             d = buffer_extend(b, 2);
79991ce3b08SGlenn Strauss             d[0] = '\\';
80091ce3b08SGlenn Strauss             d[1] = c;
80191ce3b08SGlenn Strauss             break;
80291ce3b08SGlenn Strauss           default:
803d22e88b7SGlenn Strauss             d = buffer_extend(b, 6);
804d22e88b7SGlenn Strauss             d[0] = '\\';
805d22e88b7SGlenn Strauss             d[1] = 'u';
806d22e88b7SGlenn Strauss             d[2] = '0';
807d22e88b7SGlenn Strauss             d[3] = '0';
80891ce3b08SGlenn Strauss             d[4] = hex_chars_uc[c >> 4];
80991ce3b08SGlenn Strauss             d[5] = hex_chars_uc[c & 0xF];
810d22e88b7SGlenn Strauss             break;
811d22e88b7SGlenn Strauss         }
812d22e88b7SGlenn Strauss     }
813d22e88b7SGlenn Strauss }
814d22e88b7SGlenn Strauss 
815d22e88b7SGlenn Strauss 
816bcdc6a3bSJan Kneschke /* decodes url-special-chars inplace.
81775fae49eSJan Kneschke  * replaces non-printable characters with '_'
818006c5efcSGlenn Strauss  * (If this is used on a portion of query string, then query string should be
819006c5efcSGlenn Strauss  *  split on '&', and '+' replaced with ' ' before calling this routine)
820bcdc6a3bSJan Kneschke  */
821acfe706dSJan Kneschke 
buffer_urldecode_path(buffer * const b)822006c5efcSGlenn Strauss void buffer_urldecode_path(buffer * const b) {
823af3df29aSGlenn Strauss     const size_t len = buffer_clen(b);
824006c5efcSGlenn Strauss     char *src = len ? memchr(b->ptr, '%', len) : NULL;
825006c5efcSGlenn Strauss     if (NULL == src) return;
826bcdc6a3bSJan Kneschke 
827006c5efcSGlenn Strauss     char *dst = src;
828006c5efcSGlenn Strauss     do {
829006c5efcSGlenn Strauss         /* *src == '%' */
83007c8a6f0SGlenn Strauss         unsigned char high = ((unsigned char *)src)[1];
83107c8a6f0SGlenn Strauss         unsigned char low = high ? hex2int(((unsigned char *)src)[2]) : 0xFF;
83207c8a6f0SGlenn Strauss         if (0xFF != (high = hex2int(high)) && 0xFF != low) {
833006c5efcSGlenn Strauss             high = (high << 4) | low;   /* map ctrls to '_' */
834006c5efcSGlenn Strauss             *dst = (high >= 32 && high != 127) ? high : '_';
835bcdc6a3bSJan Kneschke             src += 2;
836006c5efcSGlenn Strauss         } /* else ignore this '%'; leave as-is and move on */
837bcdc6a3bSJan Kneschke 
838006c5efcSGlenn Strauss         while ((*++dst = *++src) != '%' && *src) ;
839006c5efcSGlenn Strauss     } while (*src);
840006c5efcSGlenn Strauss     b->used = (dst - b->ptr) + 1;
841acfe706dSJan Kneschke }
842acfe706dSJan Kneschke 
buffer_is_valid_UTF8(const buffer * b)84380638252SGlenn Strauss int buffer_is_valid_UTF8(const buffer *b) {
84480638252SGlenn Strauss     /* https://www.w3.org/International/questions/qa-forms-utf-8 */
84557c0859fSGlenn Strauss     /*assert(b->used);*//*(b->ptr must exist and be '\0'-terminated)*/
84680638252SGlenn Strauss     const unsigned char *c = (unsigned char *)b->ptr;
84780638252SGlenn Strauss     while (*c) {
84880638252SGlenn Strauss 
84980638252SGlenn Strauss         /*(note: includes ctrls)*/
85080638252SGlenn Strauss         if (                         c[0] <  0x80 ) { ++c;  continue; }
85180638252SGlenn Strauss 
85280638252SGlenn Strauss         if (         0xc2 <= c[0] && c[0] <= 0xdf
85380638252SGlenn Strauss             &&       0x80 <= c[1] && c[1] <= 0xbf ) { c+=2; continue; }
85480638252SGlenn Strauss 
85580638252SGlenn Strauss         if ( (   (   0xe0 == c[0]
85680638252SGlenn Strauss                   && 0xa0 <= c[1] && c[1] <= 0xbf)
85780638252SGlenn Strauss               || (   0xe1 <= c[0] && c[0] <= 0xef && c[0] != 0xed
85880638252SGlenn Strauss                   && 0x80 <= c[1] && c[1] <= 0xbf)
85980638252SGlenn Strauss               || (   0xed == c[0]
86080638252SGlenn Strauss                   && 0x80 <= c[1] && c[1] <= 0x9f)   )
86180638252SGlenn Strauss             &&       0x80 <= c[2] && c[2] <= 0xbf ) { c+=3; continue; }
86280638252SGlenn Strauss 
86380638252SGlenn Strauss         if ( (   (   0xf0 == c[0]
86480638252SGlenn Strauss                   && 0x90 <= c[1] && c[1] <= 0xbf)
86580638252SGlenn Strauss               || (   0xf1 <= c[0] && c[0] <= 0xf3
86680638252SGlenn Strauss                   && 0x80 <= c[1] && c[1] <= 0xbf)
86780638252SGlenn Strauss               || (   0xf4 == c[0]
86880638252SGlenn Strauss                   && 0x80 <= c[1] && c[1] <= 0x8f)   )
86980638252SGlenn Strauss             &&       0x80 <= c[2] && c[2] <= 0xbf
87080638252SGlenn Strauss             &&       0x80 <= c[3] && c[3] <= 0xbf ) { c+=4; continue; }
87180638252SGlenn Strauss 
87280638252SGlenn Strauss         return 0; /* invalid */
87380638252SGlenn Strauss     }
87480638252SGlenn Strauss     return 1; /* valid */
87580638252SGlenn Strauss }
87680638252SGlenn Strauss 
8770a61fdecSStefan Bühler /* - special case: empty string returns empty string
8780a61fdecSStefan Bühler  * - on windows or cygwin: replace \ with /
8790a61fdecSStefan Bühler  * - strip leading spaces
8800a61fdecSStefan Bühler  * - prepends "/" if not present already
8810a61fdecSStefan Bühler  * - resolve "/../", "//" and "/./" the usual way:
8820a61fdecSStefan Bühler  *   the first one removes a preceding component, the other two
8830a61fdecSStefan Bühler  *   get compressed to "/".
8840a61fdecSStefan Bühler  * - "/." and "/.." at the end are similar, but always leave a trailing
8850a61fdecSStefan Bühler  *   "/"
88675fae49eSJan Kneschke  *
88775fae49eSJan Kneschke  * /blah/..         gets  /
88875fae49eSJan Kneschke  * /blah/../foo     gets  /foo
88975fae49eSJan Kneschke  * /abc/./xyz       gets  /abc/xyz
89075fae49eSJan Kneschke  * /abc//xyz        gets  /abc/xyz
891bcdc6a3bSJan Kneschke  */
892bcdc6a3bSJan Kneschke 
buffer_path_simplify(buffer * b)893980554bcSGlenn Strauss void buffer_path_simplify(buffer *b)
89475fae49eSJan Kneschke {
895af3df29aSGlenn Strauss     char *out = b->ptr;
896af3df29aSGlenn Strauss     char * const end = b->ptr + b->used - 1;
897af3df29aSGlenn Strauss 
898af3df29aSGlenn Strauss     if (__builtin_expect( (buffer_is_blank(b)), 0)) {
899af3df29aSGlenn Strauss         buffer_blank(b);
9006afad87dSStefan Bühler         return;
9016afad87dSStefan Bühler     }
9026afad87dSStefan Bühler 
9036afad87dSStefan Bühler   #if defined(__WIN32) || defined(__CYGWIN__)
9046afad87dSStefan Bühler     /* cygwin is treating \ and / the same, so we have to that too */
905980554bcSGlenn Strauss     for (char *p = b->ptr; *p; p++) {
9066afad87dSStefan Bühler         if (*p == '\\') *p = '/';
9076afad87dSStefan Bühler     }
9086afad87dSStefan Bühler   #endif
909bcdc6a3bSJan Kneschke 
910980554bcSGlenn Strauss     *end = '/'; /*(end of path modified to avoid need to check '\0')*/
91171e66c88SJan Kneschke 
912980554bcSGlenn Strauss     char *walk = out;
9135beee8b2SGlenn Strauss     if (__builtin_expect( (*walk == '/'), 1)) {
9145beee8b2SGlenn Strauss         /* scan to detect (potential) need for path simplification
9155beee8b2SGlenn Strauss          * (repeated '/' or "/.") */
9165beee8b2SGlenn Strauss         do {
9175beee8b2SGlenn Strauss             if (*++walk == '.' || *walk == '/')
9185beee8b2SGlenn Strauss                 break;
9195beee8b2SGlenn Strauss             do { ++walk; } while (*walk != '/');
9205beee8b2SGlenn Strauss         } while (walk != end);
9215beee8b2SGlenn Strauss         if (__builtin_expect( (walk == end), 1)) {
9225beee8b2SGlenn Strauss             /* common case: no repeated '/' or "/." */
9235beee8b2SGlenn Strauss             *end = '\0'; /* overwrite extra '/' added to end of path */
9245beee8b2SGlenn Strauss             return;
9255beee8b2SGlenn Strauss         }
9265beee8b2SGlenn Strauss         out = walk-1;
9275beee8b2SGlenn Strauss     }
9285beee8b2SGlenn Strauss     else {
929980554bcSGlenn Strauss         if (walk[0] == '.' && walk[1] == '/')
930980554bcSGlenn Strauss             *out = *++walk;
931980554bcSGlenn Strauss         else if (walk[0] == '.' && walk[1] == '.' && walk[2] == '/')
932980554bcSGlenn Strauss             *out = *(walk += 2);
933980554bcSGlenn Strauss         else {
934980554bcSGlenn Strauss             while (*++walk != '/') ;
935980554bcSGlenn Strauss             out = walk;
936bcdc6a3bSJan Kneschke         }
937e8e59396SGlenn Strauss         ++walk;
9385beee8b2SGlenn Strauss     }
939980554bcSGlenn Strauss 
940980554bcSGlenn Strauss     while (walk <= end) {
941980554bcSGlenn Strauss         /* previous char is '/' at this point (or start of string w/o '/') */
942980554bcSGlenn Strauss         if (__builtin_expect( (walk[0] == '/'), 0)) {
943980554bcSGlenn Strauss             /* skip repeated '/' (e.g. "///" -> "/") */
944980554bcSGlenn Strauss             if (++walk < end)
945980554bcSGlenn Strauss                 continue;
946980554bcSGlenn Strauss             else {
947980554bcSGlenn Strauss                 ++out;
948980554bcSGlenn Strauss                 break;
949e8e59396SGlenn Strauss             }
9500a61fdecSStefan Bühler         }
951980554bcSGlenn Strauss         else if (__builtin_expect( (walk[0] == '.'), 0)) {
952980554bcSGlenn Strauss             /* handle "./" and "../" */
953980554bcSGlenn Strauss             if (walk[1] == '.' && walk[2] == '/') {
954980554bcSGlenn Strauss                 /* handle "../" */
955980554bcSGlenn Strauss                 while (out > b->ptr && *--out != '/') ;
956980554bcSGlenn Strauss                 *out = '/'; /*(in case path had not started with '/')*/
957980554bcSGlenn Strauss                 if ((walk += 3) >= end) {
958980554bcSGlenn Strauss                     ++out;
959980554bcSGlenn Strauss                     break;
9600a61fdecSStefan Bühler                 }
961980554bcSGlenn Strauss                 else
962980554bcSGlenn Strauss                 continue;
963980554bcSGlenn Strauss             }
964980554bcSGlenn Strauss             else if (walk[1] == '/') {
965980554bcSGlenn Strauss                 /* handle "./" */
966980554bcSGlenn Strauss                 if ((walk += 2) >= end) {
967980554bcSGlenn Strauss                     ++out;
968980554bcSGlenn Strauss                     break;
969980554bcSGlenn Strauss                 }
970980554bcSGlenn Strauss                 continue;
971980554bcSGlenn Strauss             }
972980554bcSGlenn Strauss             else {
973980554bcSGlenn Strauss                 /* accept "." if not part of "../" or "./" */
974980554bcSGlenn Strauss                 *++out = '.';
975980554bcSGlenn Strauss                 ++walk;
9760a61fdecSStefan Bühler             }
977bcdc6a3bSJan Kneschke         }
978bcdc6a3bSJan Kneschke 
979980554bcSGlenn Strauss         while ((*++out = *walk++) != '/') ;
980980554bcSGlenn Strauss     }
981980554bcSGlenn Strauss     *out = *end = '\0'; /* overwrite extra '/' added to end of path */
982980554bcSGlenn Strauss     b->used = (out - b->ptr) + 1;
983af3df29aSGlenn Strauss     /*buffer_truncate(b, out - b->ptr);*/
984bcdc6a3bSJan Kneschke }
985bcdc6a3bSJan Kneschke 
buffer_to_lower(buffer * const b)986b04f0311SGlenn Strauss void buffer_to_lower(buffer * const b) {
987c58b95f2SGlenn Strauss     unsigned char * const restrict s = (unsigned char *)b->ptr;
98857c0859fSGlenn Strauss     const uint_fast32_t used = b->used;
98957c0859fSGlenn Strauss     for (uint_fast32_t i = 0; i < used; ++i) {
990c58b95f2SGlenn Strauss         if (light_isupper(s[i])) s[i] |= 0x20;
991f8687d3fSJan Kneschke     }
992f8687d3fSJan Kneschke }
993f8687d3fSJan Kneschke 
9946afad87dSStefan Bühler 
buffer_to_upper(buffer * const b)995b04f0311SGlenn Strauss void buffer_to_upper(buffer * const b) {
996c58b95f2SGlenn Strauss     unsigned char * const restrict s = (unsigned char *)b->ptr;
99757c0859fSGlenn Strauss     const uint_fast32_t used = b->used;
99857c0859fSGlenn Strauss     for (uint_fast32_t i = 0; i < used; ++i) {
999c58b95f2SGlenn Strauss         if (light_islower(s[i])) s[i] &= 0xdf;
1000f8687d3fSJan Kneschke     }
1001f8687d3fSJan Kneschke }
1002