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