Coverage Report

Created: 2025-07-18 06:40

/src/h2o/lib/common/string.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Justin Zhu, Fastly, Inc.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#include <stdint.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <time.h>
27
#include "h2o/string_.h"
28
29
h2o_iovec_t h2o_strdup(h2o_mem_pool_t *pool, const char *s, size_t slen)
30
33.3k
{
31
    /* We do not need this check to be here, but it needs to be somewhere, see the definition of H2O_SIZE_T_LONGEST_STR */
32
33.3k
    H2O_BUILD_ASSERT(sizeof(size_t) <= sizeof(uint64_t));
33
34
33.3k
    h2o_iovec_t ret;
35
36
33.3k
    if (slen == SIZE_MAX)
37
1.15k
        slen = strlen(s);
38
39
33.3k
    if (pool != NULL) {
40
33.3k
        ret.base = h2o_mem_alloc_pool(pool, char, slen + 1);
41
33.3k
    } else {
42
11
        ret.base = h2o_mem_alloc(slen + 1);
43
11
    }
44
33.3k
    h2o_memcpy(ret.base, s, slen);
45
33.3k
    ret.base[slen] = '\0';
46
33.3k
    ret.len = slen;
47
33.3k
    return ret;
48
33.3k
}
49
50
h2o_iovec_t h2o_strdup_shared(h2o_mem_pool_t *pool, const char *s, size_t slen)
51
0
{
52
0
    h2o_iovec_t ret;
53
54
0
    if (slen == SIZE_MAX)
55
0
        slen = strlen(s);
56
57
0
    ret.base = h2o_mem_alloc_shared(pool, slen + 1, NULL);
58
0
    memcpy(ret.base, s, slen);
59
0
    ret.base[slen] = '\0';
60
0
    ret.len = slen;
61
0
    return ret;
62
0
}
63
64
h2o_iovec_t h2o_strdup_slashed(h2o_mem_pool_t *pool, const char *src, size_t len)
65
2
{
66
2
    h2o_iovec_t ret;
67
68
2
    ret.len = len != SIZE_MAX ? len : strlen(src);
69
2
    ret.base = pool != NULL ? h2o_mem_alloc_pool(pool, char, ret.len + 2) : h2o_mem_alloc(ret.len + 2);
70
2
    memcpy(ret.base, src, ret.len);
71
2
    if (ret.len != 0 && ret.base[ret.len - 1] != '/')
72
1
        ret.base[ret.len++] = '/';
73
2
    ret.base[ret.len] = '\0';
74
75
2
    return ret;
76
2
}
77
78
int h2o__lcstris_core(const char *target, const char *test, size_t test_len)
79
1.04k
{
80
3.23k
    for (; test_len != 0; --test_len)
81
2.96k
        if (h2o_tolower(*target++) != *test++)
82
774
            return 0;
83
268
    return 1;
84
1.04k
}
85
86
size_t h2o_strtosize(const char *s, size_t len)
87
6.17k
{
88
6.17k
    uint64_t v = 0, m = 1;
89
6.17k
    const char *p = s + len;
90
91
6.17k
    if (len == 0)
92
3
        goto Error;
93
94
10.3k
    while (1) {
95
10.3k
        int ch = *--p;
96
10.3k
        if (!('0' <= ch && ch <= '9'))
97
48
            goto Error;
98
10.3k
        v += (ch - '0') * m;
99
10.3k
        if (p == s)
100
6.12k
            break;
101
4.20k
        m *= 10;
102
        /* do not even try to overflow */
103
4.20k
        if (m == 10000000000000000000ULL)
104
2
            goto Error;
105
4.20k
    }
106
107
6.12k
    if (v >= SIZE_MAX)
108
0
        goto Error;
109
6.12k
    return v;
110
111
53
Error:
112
53
    return SIZE_MAX;
113
6.12k
}
114
115
size_t h2o_strtosizefwd(char **s, size_t len)
116
0
{
117
0
    uint64_t v, c;
118
0
    char *p = *s, *p_end = *s + len;
119
120
0
    if (len == 0)
121
0
        goto Error;
122
123
0
    int ch = *p++;
124
0
    if (!('0' <= ch && ch <= '9'))
125
0
        goto Error;
126
0
    v = ch - '0';
127
0
    c = 1;
128
129
0
    while (1) {
130
0
        ch = *p;
131
0
        if (!('0' <= ch && ch <= '9'))
132
0
            break;
133
0
        v *= 10;
134
0
        v += ch - '0';
135
0
        p++;
136
0
        c++;
137
0
        if (p == p_end)
138
0
            break;
139
        /* similar as above, do not even try to overflow */
140
0
        if (c == 20)
141
0
            goto Error;
142
0
    }
143
144
0
    if (v >= SIZE_MAX)
145
0
        goto Error;
146
0
    *s = p;
147
0
    return v;
148
149
0
Error:
150
0
    return SIZE_MAX;
151
0
}
152
153
static uint32_t decode_base64url_quad(const char *src)
154
0
{
155
0
    const char *src_end = src + 4;
156
0
    uint32_t decoded = 0;
157
158
0
    while (1) {
159
0
        if ('A' <= *src && *src <= 'Z') {
160
0
            decoded |= *src - 'A';
161
0
        } else if ('a' <= *src && *src <= 'z') {
162
0
            decoded |= *src - 'a' + 26;
163
0
        } else if ('0' <= *src && *src <= '9') {
164
0
            decoded |= *src - '0' + 52;
165
0
        } else if (*src == '-') {
166
0
            decoded |= 62;
167
0
        } else if (*src == '_') {
168
0
            decoded |= 63;
169
0
#if 1 /* curl uses normal base64 */
170
0
        } else if (*src == '+') {
171
0
            decoded |= 62;
172
0
        } else if (*src == '/') {
173
0
            decoded |= 63;
174
0
#endif
175
0
        } else {
176
0
            return UINT32_MAX;
177
0
        }
178
0
        if (++src == src_end)
179
0
            break;
180
0
        decoded <<= 6;
181
0
    }
182
183
0
    return decoded;
184
0
}
185
186
h2o_iovec_t h2o_decode_base64url(h2o_mem_pool_t *pool, const char *src, size_t len)
187
0
{
188
0
    h2o_iovec_t decoded;
189
0
    uint32_t t;
190
0
    uint8_t *dst;
191
0
    char remaining_input[4];
192
193
0
    decoded.len = len * 3 / 4;
194
0
    decoded.base = pool != NULL ? h2o_mem_alloc_pool(pool, char, decoded.len + 1) : h2o_mem_alloc(decoded.len + 1);
195
0
    dst = (uint8_t *)decoded.base;
196
197
0
    while (len >= 4) {
198
0
        if ((t = decode_base64url_quad(src)) == UINT32_MAX)
199
0
            goto Error;
200
0
        *dst++ = t >> 16;
201
0
        *dst++ = t >> 8;
202
0
        *dst++ = t;
203
0
        src += 4;
204
0
        len -= 4;
205
0
    }
206
0
    switch (len) {
207
0
    case 0:
208
0
        break;
209
0
    case 1:
210
0
        goto Error;
211
0
    case 2:
212
0
        remaining_input[0] = *src++;
213
0
        remaining_input[1] = *src++;
214
0
        remaining_input[2] = 'A';
215
0
        remaining_input[3] = 'A';
216
0
        if ((t = decode_base64url_quad(remaining_input)) == UINT32_MAX)
217
0
            goto Error;
218
0
        *dst++ = t >> 16;
219
0
        break;
220
0
    case 3:
221
0
        remaining_input[0] = *src++;
222
0
        remaining_input[1] = *src++;
223
0
        remaining_input[2] = *src++;
224
0
        remaining_input[3] = 'A';
225
0
        if ((t = decode_base64url_quad(remaining_input)) == UINT32_MAX)
226
0
            goto Error;
227
0
        *dst++ = t >> 16;
228
0
        *dst++ = t >> 8;
229
0
        break;
230
0
    }
231
232
0
    assert((char *)dst - decoded.base == decoded.len);
233
0
    decoded.base[decoded.len] = '\0';
234
235
0
    return decoded;
236
237
0
Error:
238
0
    if (pool == NULL)
239
0
        free(decoded.base);
240
0
    return h2o_iovec_init(NULL, 0);
241
0
}
242
243
size_t h2o_base64_encode(char *_dst, const void *_src, size_t len, int url_encoded)
244
0
{
245
0
    static const char *MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
246
0
                             "abcdefghijklmnopqrstuvwxyz"
247
0
                             "0123456789+/";
248
0
    static const char *MAP_URL_ENCODED = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
249
0
                                         "abcdefghijklmnopqrstuvwxyz"
250
0
                                         "0123456789-_";
251
252
0
    char *dst = _dst;
253
0
    const uint8_t *src = _src;
254
0
    const char *map = url_encoded ? MAP_URL_ENCODED : MAP;
255
0
    uint32_t quad;
256
257
0
    for (; len >= 3; src += 3, len -= 3) {
258
0
        quad = ((uint32_t)src[0] << 16) | ((uint32_t)src[1] << 8) | src[2];
259
0
        *dst++ = map[quad >> 18];
260
0
        *dst++ = map[(quad >> 12) & 63];
261
0
        *dst++ = map[(quad >> 6) & 63];
262
0
        *dst++ = map[quad & 63];
263
0
    }
264
0
    if (len != 0) {
265
0
        quad = (uint32_t)src[0] << 16;
266
0
        *dst++ = map[quad >> 18];
267
0
        if (len == 2) {
268
0
            quad |= (uint32_t)src[1] << 8;
269
0
            *dst++ = map[(quad >> 12) & 63];
270
0
            *dst++ = map[(quad >> 6) & 63];
271
0
            if (!url_encoded)
272
0
                *dst++ = '=';
273
0
        } else {
274
0
            *dst++ = map[(quad >> 12) & 63];
275
0
            if (!url_encoded) {
276
0
                *dst++ = '=';
277
0
                *dst++ = '=';
278
0
            }
279
0
        }
280
0
    }
281
282
0
    *dst = '\0';
283
0
    return dst - _dst;
284
0
}
285
286
static int decode_hex(int ch)
287
0
{
288
0
    if ('0' <= ch && ch <= '9')
289
0
        return ch - '0';
290
0
    if ('A' <= ch && ch <= 'F')
291
0
        return ch - 'A' + 0xa;
292
0
    if ('a' <= ch && ch <= 'f')
293
0
        return ch - 'a' + 0xa;
294
0
    return -1;
295
0
}
296
297
int h2o_hex_decode(void *_dst, const char *src, size_t src_len)
298
0
{
299
0
    unsigned char *dst = _dst;
300
301
0
    if (src_len % 2 != 0)
302
0
        return -1;
303
0
    for (; src_len != 0; src_len -= 2) {
304
0
        int hi, lo;
305
0
        if ((hi = decode_hex(*src++)) == -1 || (lo = decode_hex(*src++)) == -1)
306
0
            return -1;
307
0
        *dst++ = (hi << 4) | lo;
308
0
    }
309
0
    return 0;
310
0
}
311
312
void h2o_hex_encode(char *dst, const void *_src, size_t src_len)
313
0
{
314
0
    const unsigned char *src = _src, *src_end = src + src_len;
315
0
    for (; src != src_end; ++src) {
316
0
        *dst++ = "0123456789abcdef"[*src >> 4];
317
0
        *dst++ = "0123456789abcdef"[*src & 0xf];
318
0
    }
319
0
    *dst = '\0';
320
0
}
321
322
h2o_iovec_t h2o_uri_escape(h2o_mem_pool_t *pool, const char *s, size_t l, const char *preserve_chars)
323
0
{
324
0
    h2o_iovec_t encoded;
325
0
    size_t i, capacity = l * 3 + 1;
326
327
0
    encoded.base = pool != NULL ? h2o_mem_alloc_pool(pool, char, capacity) : h2o_mem_alloc(capacity);
328
0
    encoded.len = 0;
329
330
    /* RFC 3986:
331
        path-noscheme = segment-nz-nc *( "/" segment )
332
        segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
333
        unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
334
        sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
335
                     / "*" / "+" / "," / ";" / "="
336
    */
337
0
    for (i = 0; i != l; ++i) {
338
0
        int ch = s[i];
339
0
        if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9') || ch == '-' || ch == '.' ||
340
0
            ch == '_' || ch == '~' || ch == '!' || ch == '$' || ch == '&' || ch == '\'' || ch == '(' || ch == ')' || ch == '*' ||
341
0
            ch == '+' || ch == ',' || ch == ';' || ch == '=' ||
342
0
            (ch != '\0' && preserve_chars != NULL && strchr(preserve_chars, ch) != NULL)) {
343
0
            encoded.base[encoded.len++] = ch;
344
0
        } else {
345
0
            encoded.base[encoded.len++] = '%';
346
0
            encoded.base[encoded.len++] = "0123456789ABCDEF"[(ch >> 4) & 0xf];
347
0
            encoded.base[encoded.len++] = "0123456789ABCDEF"[ch & 0xf];
348
0
        }
349
0
    }
350
0
    encoded.base[encoded.len] = '\0';
351
352
0
    return encoded;
353
0
}
354
355
h2o_iovec_t h2o_uri_unescape(h2o_mem_pool_t *pool, const char *str, size_t len)
356
0
{
357
0
    h2o_iovec_t decoded;
358
359
0
    decoded.base = pool != NULL ? h2o_mem_alloc_pool(pool, char, len + 1) : h2o_mem_alloc(len + 1);
360
0
    decoded.len = 0;
361
362
0
    for (size_t i = 0; i < len; ++i) {
363
0
        if (str[i] == '%') {
364
0
            if (i + 2 >= len)
365
0
                goto Fail;
366
0
            int hi = decode_hex(str[i + 1]);
367
0
            int lo = decode_hex(str[i + 2]);
368
0
            if (hi < 0 || lo < 0 || (hi == 0 && lo == 0))
369
0
                goto Fail;
370
0
            decoded.base[decoded.len++] = (hi << 4) | lo;
371
0
            i += 2;
372
0
        } else {
373
0
            decoded.base[decoded.len++] = str[i];
374
0
        }
375
0
    }
376
0
    decoded.base[decoded.len] = '\0';
377
0
    return decoded;
378
379
0
Fail:
380
0
    if (pool == NULL)
381
0
        free(decoded.base);
382
0
    return h2o_iovec_init(NULL, 0);
383
0
}
384
385
h2o_iovec_t h2o_get_filext(const char *path, size_t len)
386
0
{
387
0
    const char *end = path + len, *p = end;
388
389
0
    while (--p != path) {
390
0
        if (*p == '.') {
391
0
            return h2o_iovec_init(p + 1, end - (p + 1));
392
0
        } else if (*p == '/') {
393
0
            break;
394
0
        }
395
0
    }
396
0
    return h2o_iovec_init(NULL, 0);
397
0
}
398
399
static int is_ws(int ch)
400
0
{
401
0
    return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
402
0
}
403
404
h2o_iovec_t h2o_str_stripws(const char *s, size_t len)
405
0
{
406
0
    const char *end = s + len;
407
408
0
    while (s != end) {
409
0
        if (!is_ws(*s))
410
0
            break;
411
0
        ++s;
412
0
    }
413
0
    while (s != end) {
414
0
        if (!is_ws(end[-1]))
415
0
            break;
416
0
        --end;
417
0
    }
418
0
    return h2o_iovec_init(s, end - s);
419
0
}
420
421
size_t h2o_strstr(const char *haysack, size_t haysack_len, const char *needle, size_t needle_len)
422
1.43k
{
423
    /* TODO optimize */
424
1.43k
    if (haysack_len >= needle_len) {
425
1.28k
        size_t off, max = haysack_len - needle_len + 1;
426
1.28k
        if (needle_len == 0)
427
0
            return 0;
428
428k
        for (off = 0; off != max; ++off)
429
427k
            if (haysack[off] == needle[0] && memcmp(haysack + off + 1, needle + 1, needle_len - 1) == 0)
430
0
                return off;
431
1.28k
    }
432
1.43k
    return SIZE_MAX;
433
1.43k
}
434
435
/* note: returns a zero-width match as well */
436
const char *h2o_next_token(h2o_iovec_t *iter, int inner, int outer, size_t *element_len, h2o_iovec_t *value)
437
9.56k
{
438
9.56k
    const char *cur = iter->base, *end = iter->base + iter->len, *token_start, *token_end;
439
440
    /* find start */
441
11.2k
    for (;; ++cur) {
442
11.2k
        if (cur == end)
443
874
            return NULL;
444
10.4k
        if (!(*cur == ' ' || *cur == '\t'))
445
8.69k
            break;
446
10.4k
    }
447
8.69k
    token_start = cur;
448
8.69k
    token_end = cur;
449
450
    /* find last */
451
65.4k
    for (;; ++cur) {
452
65.4k
        if (cur == end)
453
822
            break;
454
64.6k
        if (*cur == inner) {
455
5.51k
            ++cur;
456
5.51k
            break;
457
5.51k
        }
458
59.1k
        if (*cur == outer) {
459
0
            if (token_start == cur) {
460
0
                ++cur;
461
0
                token_end = cur;
462
0
            }
463
0
            break;
464
0
        }
465
59.1k
        if (value != NULL && *cur == '=') {
466
2.35k
            ++cur;
467
2.35k
            goto FindValue;
468
2.35k
        }
469
56.7k
        if (!(*cur == ' ' || *cur == '\t'))
470
55.3k
            token_end = cur + 1;
471
56.7k
    }
472
473
    /* found */
474
6.33k
    *iter = h2o_iovec_init(cur, end - cur);
475
6.33k
    *element_len = token_end - token_start;
476
6.33k
    if (value != NULL)
477
3.42k
        *value = (h2o_iovec_t){NULL};
478
6.33k
    return token_start;
479
480
2.35k
FindValue:
481
2.35k
    *iter = h2o_iovec_init(cur, end - cur);
482
2.35k
    *element_len = token_end - token_start;
483
2.35k
    if ((value->base = (char *)h2o_next_token(iter, inner, outer, &value->len, NULL)) == NULL) {
484
12
        *value = (h2o_iovec_t){"", 0};
485
2.34k
    } else if (h2o_memis(value->base, value->len, H2O_STRLIT(","))) {
486
0
        *value = (h2o_iovec_t){"", 0};
487
0
        iter->base -= 1;
488
0
        iter->len += 1;
489
0
    }
490
2.35k
    return token_start;
491
8.69k
}
492
493
int h2o_contains_token(const char *haysack, size_t haysack_len, const char *needle, size_t needle_len, int separator)
494
565
{
495
565
    h2o_iovec_t iter = h2o_iovec_init(haysack, haysack_len);
496
565
    const char *token = NULL;
497
565
    size_t token_len = 0;
498
499
1.13k
    while ((token = h2o_next_token(&iter, separator, ',', &token_len, NULL)) != NULL) {
500
565
        if (h2o_lcstris(token, token_len, needle, needle_len)) {
501
0
            return 1;
502
0
        }
503
565
    }
504
565
    return 0;
505
565
}
506
507
h2o_iovec_t h2o_htmlescape(h2o_mem_pool_t *pool, const char *src, size_t len)
508
0
{
509
0
    const char *s, *end = src + len;
510
0
    size_t add_size = 0;
511
512
0
#define ENTITY_MAP()                                                                                                               \
513
0
    ENTITY('"', "&quot;");                                                                                                         \
514
0
    ENTITY('&', "&amp;");                                                                                                          \
515
0
    ENTITY('\'', "&#39;");                                                                                                         \
516
0
    ENTITY('<', "&lt;");                                                                                                           \
517
0
    ENTITY('>', "&gt;");
518
519
0
    for (s = src; s != end; ++s) {
520
0
        if ((unsigned)(unsigned char)*s - '"' <= '>' - '"') {
521
0
            switch (*s) {
522
0
#define ENTITY(code, quoted)                                                                                                       \
523
0
    case code:                                                                                                                     \
524
0
        add_size += sizeof(quoted) - 2;                                                                                            \
525
0
        break
526
0
                ENTITY_MAP()
527
0
#undef ENTITY
528
0
            }
529
0
        }
530
0
    }
531
532
    /* escape and return the result if necessary */
533
0
    if (add_size != 0) {
534
        /* allocate buffer and fill in the chars that are known not to require escaping */
535
0
        h2o_iovec_t escaped = {h2o_mem_alloc_pool(pool, char, len + add_size + 1), 0};
536
        /* fill-in the rest */
537
0
        for (s = src; s != end; ++s) {
538
0
            switch (*s) {
539
0
#define ENTITY(code, quoted)                                                                                                       \
540
0
    case code:                                                                                                                     \
541
0
        memcpy(escaped.base + escaped.len, quoted, sizeof(quoted) - 1);                                                            \
542
0
        escaped.len += sizeof(quoted) - 1;                                                                                         \
543
0
        break
544
0
                ENTITY_MAP()
545
0
#undef ENTITY
546
0
            default:
547
0
                escaped.base[escaped.len++] = *s;
548
0
                break;
549
0
            }
550
0
        }
551
0
        assert(escaped.len == len + add_size);
552
0
        escaped.base[escaped.len] = '\0';
553
554
0
        return escaped;
555
0
    }
556
557
0
#undef ENTITY_MAP
558
559
    /* no need not escape; return the original */
560
0
    return h2o_iovec_init(src, len);
561
0
}
562
563
h2o_iovec_t h2o_concat_list(h2o_mem_pool_t *pool, h2o_iovec_t *list, size_t count)
564
1.39k
{
565
1.39k
    h2o_iovec_t ret = {NULL, 0};
566
1.39k
    size_t i;
567
568
    /* calc the length */
569
481k
    for (i = 0; i != count; ++i) {
570
480k
        ret.len += list[i].len;
571
480k
    }
572
573
    /* allocate memory */
574
1.39k
    if (pool != NULL)
575
1.39k
        ret.base = h2o_mem_alloc_pool(pool, char, ret.len + 1);
576
0
    else
577
0
        ret.base = h2o_mem_alloc(ret.len + 1);
578
579
    /* concatenate */
580
1.39k
    ret.len = 0;
581
481k
    for (i = 0; i != count; ++i) {
582
480k
        h2o_memcpy(ret.base + ret.len, list[i].base, list[i].len);
583
480k
        ret.len += list[i].len;
584
480k
    }
585
1.39k
    ret.base[ret.len] = '\0';
586
587
1.39k
    return ret;
588
1.39k
}
589
590
h2o_iovec_t h2o_join_list(h2o_mem_pool_t *pool, h2o_iovec_t *list, size_t count, h2o_iovec_t delimiter)
591
257
{
592
257
    if (count == 0) {
593
0
        return h2o_iovec_init(NULL, 0);
594
0
    }
595
596
257
    size_t joined_len = 0;
597
257
    h2o_iovec_t *joined = alloca(sizeof(*joined) * (count * 2 - 1));
598
599
257
    size_t i;
600
239k
    for (i = 0; i != count; ++i) {
601
239k
        if (i != 0) {
602
239k
            joined[joined_len++] = delimiter;
603
239k
        }
604
239k
        joined[joined_len++] = list[i];
605
239k
    }
606
257
    return h2o_concat_list(pool, joined, joined_len);
607
257
}
608
609
void h2o_split(h2o_mem_pool_t *pool, h2o_iovec_vector_t *list, h2o_iovec_t str, const char needle)
610
0
{
611
0
    const char *p = str.base, *end = str.base + str.len, *found;
612
613
0
    while (p < end && (found = memchr(p, needle, end - p)) != NULL) {
614
0
        h2o_vector_reserve(pool, list, list->size + 1);
615
0
        list->entries[list->size++] = h2o_strdup(pool, p, found - p);
616
0
        p = found + 1;
617
0
    }
618
0
    h2o_vector_reserve(pool, list, list->size + 1);
619
0
    list->entries[list->size++] = h2o_strdup(pool, p, end - p);
620
0
}
621
622
int h2o_str_at_position(char *buf, const char *src, size_t src_len, int lineno, int column)
623
0
{
624
0
    const char *src_end = src + src_len;
625
0
    int i;
626
627
    /* find the line */
628
0
    if (lineno <= 0 || column <= 0)
629
0
        return -1;
630
0
    for (--lineno; lineno != 0; --lineno) {
631
0
        do {
632
0
            if (src == src_end)
633
0
                return -1;
634
0
        } while (*src++ != '\n');
635
0
    }
636
637
    /* adjust the starting column */
638
0
    while (column > 40) {
639
0
        if (src != src_end)
640
0
            ++src;
641
0
        --column;
642
0
    }
643
644
    /* emit */
645
0
    for (i = 1; i <= 76; ++i) {
646
0
        if (src == src_end || *src == '\n')
647
0
            break;
648
0
        *buf++ = *src++;
649
0
    }
650
0
    if (i < column)
651
0
        column = i;
652
0
    *buf++ = '\n';
653
0
    for (i = 1; i < column; ++i)
654
0
        *buf++ = ' ';
655
0
    *buf++ = '^';
656
0
    *buf++ = '\n';
657
0
    *buf = '\0';
658
0
    return 0;
659
0
}
660
661
h2o_iovec_t h2o_encode_sf_string(h2o_mem_pool_t *pool, const char *s, size_t slen)
662
0
{
663
0
    if (slen == SIZE_MAX)
664
0
        slen = strlen(s);
665
666
    /* https://tools.ietf.org/html/rfc8941#section-3.3.3 */
667
0
    size_t to_escape = 0;
668
0
    for (size_t i = 0; i < slen; ++i) {
669
0
        if (s[i] == '\\' || s[i] == '"')
670
0
            ++to_escape;
671
0
    }
672
673
0
    h2o_iovec_t ret;
674
0
    ret.len = slen + to_escape + 2;
675
0
    if (pool != NULL) {
676
0
        ret.base = h2o_mem_alloc_pool(pool, char, ret.len + 1);
677
0
    } else {
678
0
        ret.base = h2o_mem_alloc(ret.len + 1);
679
0
    }
680
0
    char *dst = ret.base;
681
0
    *dst++ = '"';
682
0
    for (size_t i = 0; i < slen; ++i) {
683
0
        if (s[i] == '\\' || s[i] == '"')
684
0
            *dst++ = '\\';
685
0
        *dst++ = s[i];
686
0
    }
687
0
    *dst++ = '"';
688
0
    *dst++ = '\0';
689
0
    return ret;
690
0
}