1 #include "first.h" 2 3 #include "buffer.h" 4 5 #include <stdlib.h> 6 #include <string.h> 7 #include <time.h> /* strftime() */ 8 9 static const char hex_chars_lc[] = "0123456789abcdef"; 10 static const char hex_chars_uc[] = "0123456789ABCDEF"; 11 12 /** 13 * init the buffer 14 * 15 */ 16 17 buffer* buffer_init(void) { 18 buffer *b; 19 20 b = malloc(sizeof(*b)); 21 force_assert(b); 22 23 b->ptr = NULL; 24 b->size = 0; 25 b->used = 0; 26 27 return b; 28 } 29 30 buffer *buffer_init_buffer(const buffer *src) { 31 buffer *b = buffer_init(); 32 buffer_copy_buffer(b, src); 33 return b; 34 } 35 36 buffer *buffer_init_string(const char *str) { 37 buffer *b = buffer_init(); 38 buffer_copy_string(b, str); 39 return b; 40 } 41 42 void buffer_free(buffer *b) { 43 if (NULL == b) return; 44 45 free(b->ptr); 46 free(b); 47 } 48 49 void buffer_free_ptr(buffer *b) { 50 free(b->ptr); 51 b->ptr = NULL; 52 b->used = 0; 53 b->size = 0; 54 } 55 56 void buffer_move(buffer * restrict b, buffer * restrict src) { 57 buffer tmp; 58 buffer_clear(b); 59 tmp = *src; *src = *b; *b = tmp; 60 } 61 62 /* make sure buffer is at least "size" big + 1 for '\0'. keep old data */ 63 __attribute_cold__ 64 __attribute_noinline__ 65 __attribute_nonnull__ 66 __attribute_returns_nonnull__ 67 static char* buffer_realloc(buffer * const restrict b, const size_t len) { 68 #define BUFFER_PIECE_SIZE 64uL /*(must be power-of-2)*/ 69 const size_t sz = (len + 1 + BUFFER_PIECE_SIZE-1) & ~(BUFFER_PIECE_SIZE-1); 70 force_assert(sz > len); 71 72 b->size = sz; 73 b->ptr = realloc(b->ptr, sz); 74 75 force_assert(NULL != b->ptr); 76 return b->ptr; 77 } 78 79 __attribute_cold__ 80 __attribute_noinline__ 81 __attribute_nonnull__ 82 __attribute_returns_nonnull__ 83 static char* buffer_alloc_replace(buffer * const restrict b, const size_t size) { 84 /*(discard old data so realloc() does not copy)*/ 85 if (NULL != b->ptr) { 86 free(b->ptr); 87 b->ptr = NULL; 88 } 89 /*(note: if size larger than one lshift, use size instead of power-2)*/ 90 return buffer_realloc(b, (b->size << 1) > size ? (b->size << 1)-1 : size); 91 } 92 93 char* buffer_string_prepare_copy(buffer * const b, const size_t size) { 94 b->used = 0; 95 return (size < b->size) 96 ? b->ptr 97 : buffer_alloc_replace(b, size); 98 } 99 100 __attribute_cold__ 101 __attribute_noinline__ 102 __attribute_nonnull__ 103 __attribute_returns_nonnull__ 104 static char* buffer_string_prepare_append_resize(buffer * const restrict b, const size_t size) { 105 if (b->used < 2) /* buffer_string_is_empty(b) */ 106 return buffer_string_prepare_copy(b, size); 107 108 /* not empty, b->used already includes a terminating 0 */ 109 /*(note: if size larger than one lshift, use size instead of power-2)*/ 110 const size_t req_size = ((b->size << 1) - b->used > size) 111 ? (b->size << 1)-1 112 : b->used + size; 113 114 /* check for overflow: unsigned overflow is defined to wrap around */ 115 force_assert(req_size >= b->used); 116 117 return buffer_realloc(b, req_size) + b->used - 1; 118 } 119 120 char* buffer_string_prepare_append(buffer * const b, const size_t size) { 121 const uint32_t len = b->used ? b->used-1 : 0; 122 return (b->size - len >= size + 1) 123 ? b->ptr + len 124 : buffer_string_prepare_append_resize(b, size); 125 } 126 127 /*(prefer smaller code than inlining buffer_extend in many places in buffer.c)*/ 128 __attribute_noinline__ 129 char* 130 buffer_extend (buffer * const b, const size_t x) 131 { 132 /* extend buffer to append x (reallocate by power-2 (or larger), if needed) 133 * (combine buffer_string_prepare_append() and buffer_commit()) 134 * (future: might make buffer.h static inline func for HTTP/1.1 performance) 135 * pre-sets '\0' byte and b->used (unlike buffer_string_prepare_append())*/ 136 const uint32_t len = b->used ? b->used-1 : 0; 137 char * const s = (b->size - len >= x + 1) 138 ? b->ptr + len 139 : buffer_string_prepare_append_resize(b, x); 140 b->used = len+x+1; 141 s[x] = '\0'; 142 return s; 143 } 144 145 void buffer_string_set_length(buffer *b, uint32_t len) { 146 /*(intended for string truncate; prefer buffer_extend() to extend string)*/ 147 /*(unlike others routines, this routine potentially extends string 148 * extra len + (up to) BUFFER_PIECE_SIZE without power-2 reallocation, 149 * but does not optimize realloc of empty string)*/ 150 b->used = len + 1; /*(not resizing for power-2)*/ 151 (len < b->size ? b->ptr : buffer_realloc(b, len))[len] = '\0'; 152 } 153 154 void buffer_commit(buffer *b, size_t size) 155 { 156 force_assert(b->size > 0); 157 158 if (0 == b->used) b->used = 1; 159 160 if (size > 0) { 161 /* check for overflow: unsigned overflow is defined to wrap around */ 162 size_t sz = b->used + size; 163 force_assert(sz > b->used); 164 force_assert(sz <= b->size); 165 b->used = sz; 166 } 167 168 b->ptr[b->used - 1] = '\0'; 169 } 170 171 void buffer_copy_string(buffer * restrict b, const char * restrict s) { 172 buffer_copy_string_len(b, s, NULL != s ? strlen(s) : 0); 173 } 174 175 void buffer_copy_string_len(buffer * const restrict b, const char * const restrict s, const size_t len) { 176 b->used = len + 1; 177 char * const restrict d = (len < b->size) 178 ? b->ptr 179 : buffer_alloc_replace(b, len); 180 d[len] = '\0'; 181 memcpy(d, s, len); 182 } 183 184 void buffer_append_string(buffer * restrict b, const char * restrict s) { 185 buffer_append_string_len(b, s, NULL != s ? strlen(s) : 0); 186 } 187 188 /** 189 * append a string to the end of the buffer 190 * 191 * the resulting buffer is terminated with a '\0' 192 * s is treated as a un-terminated string (a \0 is handled a normal character) 193 * 194 * @param b a buffer 195 * @param s the string 196 * @param s_len size of the string (without the terminating \0) 197 */ 198 199 void buffer_append_string_len(buffer * const restrict b, const char * const restrict s, const size_t len) { 200 memcpy(buffer_extend(b, len), s, len); 201 } 202 203 void buffer_append_str2(buffer * const restrict b, const char * const s1, const size_t len1, const char * const s2, const size_t len2) { 204 char * const restrict s = buffer_extend(b, len1+len2); 205 #ifdef HAVE_MEMPCPY 206 mempcpy(mempcpy(s, s1, len1), s2, len2); 207 #else 208 memcpy(s, s1, len1); 209 memcpy(s+len1, s2, len2); 210 #endif 211 } 212 213 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) { 214 char * restrict s = buffer_extend(b, len1+len2+len3); 215 #ifdef HAVE_MEMPCPY 216 mempcpy(mempcpy(mempcpy(s, s1, len1), s2, len2), s3, len3); 217 #else 218 memcpy(s, s1, len1); 219 memcpy((s+=len1), s2, len2); 220 memcpy((s+=len2), s3, len3); 221 #endif 222 } 223 224 void buffer_append_iovec(buffer * const restrict b, const struct const_iovec * const iov, const size_t n) { 225 size_t len = 0; 226 for (size_t i = 0; i < n; ++i) 227 len += iov[i].iov_len; 228 char *s = buffer_extend(b, len); 229 for (size_t i = 0; i < n; ++i) { 230 if (0 == iov[i].iov_len) continue; 231 #ifdef HAVE_MEMPCPY 232 s = mempcpy(s, iov[i].iov_base, iov[i].iov_len); 233 #else 234 memcpy(s, iov[i].iov_base, iov[i].iov_len); 235 s += iov[i].iov_len; 236 #endif 237 } 238 } 239 240 void buffer_append_path_len(buffer * restrict b, const char * restrict a, size_t alen) { 241 char * restrict s = buffer_string_prepare_append(b, alen+1); 242 const int aslash = (alen && a[0] == '/'); 243 if (b->used > 1 && s[-1] == '/') { 244 if (aslash) { 245 ++a; 246 --alen; 247 } 248 } 249 else { 250 if (0 == b->used) b->used = 1; 251 if (!aslash) { 252 *s++ = '/'; 253 ++b->used; 254 } 255 } 256 b->used += alen; 257 s[alen] = '\0'; 258 memcpy(s, a, alen); 259 } 260 261 void 262 buffer_copy_path_len2 (buffer * const restrict b, const char * const restrict s1, size_t len1, const char * const restrict s2, size_t len2) 263 { 264 /*(similar to buffer_copy_string_len(b, s1, len1) but combined allocation)*/ 265 memcpy(buffer_string_prepare_copy(b, len1+len2+1), s1, len1); 266 b->used = len1 + 1; /*('\0' byte will be written below)*/ 267 268 buffer_append_path_len(b, s2, len2);/*(choice: not inlined, special-cased)*/ 269 } 270 271 void buffer_append_uint_hex_lc(buffer *b, uintmax_t value) { 272 char *buf; 273 unsigned int shift = 0; 274 275 { 276 uintmax_t copy = value; 277 do { 278 copy >>= 8; 279 shift += 8; /* counting bits */ 280 } while (0 != copy); 281 } 282 283 buf = buffer_extend(b, shift >> 2); /*nibbles (4 bits)*/ 284 285 while (shift > 0) { 286 shift -= 4; 287 *(buf++) = hex_chars_lc[(value >> shift) & 0x0F]; 288 } 289 } 290 291 __attribute_nonnull__ 292 __attribute_returns_nonnull__ 293 static char* utostr(char buf[LI_ITOSTRING_LENGTH], uintmax_t val) { 294 char *cur = buf+LI_ITOSTRING_LENGTH; 295 do { 296 int mod = val % 10; 297 val /= 10; 298 /* prepend digit mod */ 299 *(--cur) = (char) ('0' + mod); 300 } while (0 != val); 301 return cur; 302 } 303 304 __attribute_nonnull__ 305 __attribute_returns_nonnull__ 306 static char* itostr(char buf[LI_ITOSTRING_LENGTH], intmax_t val) { 307 /* absolute value not defined for INTMAX_MIN, but can take absolute 308 * value of any negative number via twos complement cast to unsigned. 309 * negative sign is prepended after (now unsigned) value is converted 310 * to string */ 311 uintmax_t uval = val >= 0 ? (uintmax_t)val : ((uintmax_t)~val) + 1; 312 char *cur = utostr(buf, uval); 313 if (val < 0) *(--cur) = '-'; 314 315 return cur; 316 } 317 318 void buffer_append_int(buffer *b, intmax_t val) { 319 char buf[LI_ITOSTRING_LENGTH]; 320 const char * const str = itostr(buf, val); 321 buffer_append_string_len(b, str, buf+sizeof(buf) - str); 322 } 323 324 void buffer_append_strftime(buffer * const restrict b, const char * const restrict format, const struct tm * const restrict tm) { 325 force_assert(NULL != format); 326 /*(localtime_r() or gmtime_r() producing tm should not have failed)*/ 327 if (__builtin_expect( (NULL == tm), 0)) return; 328 329 /*(expecting typical format strings to result in < 64 bytes needed; 330 * skipping buffer_string_space() calculation and providing fixed size)*/ 331 size_t rv = strftime(buffer_string_prepare_append(b, 63), 64, format, tm); 332 333 /* 0 (in some apis) signals the string may have been too small; 334 * but the format could also just have lead to an empty string */ 335 if (__builtin_expect( (0 == rv), 0) || __builtin_expect( (rv > 63), 0)) { 336 /* unexpected; give it a second try with a larger string */ 337 rv = strftime(buffer_string_prepare_append(b, 4095), 4096, format, tm); 338 if (__builtin_expect( (rv > 4095), 0))/*(input format was ridiculous)*/ 339 return; 340 } 341 342 /*buffer_commit(b, rv);*/ 343 b->used += (uint32_t)rv + (0 == b->used); 344 } 345 346 347 size_t li_itostrn(char *buf, size_t buf_len, intmax_t val) { 348 char p_buf[LI_ITOSTRING_LENGTH]; 349 char* const str = itostr(p_buf, val); 350 size_t len = (size_t)(p_buf+sizeof(p_buf)-str); 351 force_assert(len <= buf_len); 352 memcpy(buf, str, len); 353 return len; 354 } 355 356 size_t li_utostrn(char *buf, size_t buf_len, uintmax_t val) { 357 char p_buf[LI_ITOSTRING_LENGTH]; 358 char* const str = utostr(p_buf, val); 359 size_t len = (size_t)(p_buf+sizeof(p_buf)-str); 360 force_assert(len <= buf_len); 361 memcpy(buf, str, len); 362 return len; 363 } 364 365 #define li_ntox_lc(n) ((n) <= 9 ? (n) + '0' : (n) + 'a' - 10) 366 367 /* c (char) and n (nibble) MUST be unsigned integer types */ 368 #define li_cton(c,n) \ 369 (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0)) 370 371 /* converts hex char (0-9, A-Z, a-z) to decimal. 372 * returns 0xFF on invalid input. 373 */ 374 char hex2int(unsigned char hex) { 375 unsigned char n; 376 return li_cton(hex,n) ? (char)n : 0xFF; 377 } 378 379 380 int buffer_eq_icase_ssn(const char * const a, const char * const b, const size_t len) { 381 for (size_t i = 0; i < len; ++i) { 382 unsigned int ca = ((unsigned char *)a)[i]; 383 unsigned int cb = ((unsigned char *)b)[i]; 384 if (ca != cb) { 385 ca |= 0x20; 386 cb |= 0x20; 387 if (ca != cb) return 0; 388 if (!light_islower(ca)) return 0; 389 if (!light_islower(cb)) return 0; 390 } 391 } 392 return 1; 393 } 394 395 int buffer_eq_icase_ss(const char * const a, const size_t alen, const char * const b, const size_t blen) { 396 /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */ 397 return (alen == blen && buffer_eq_icase_ssn(a, b, blen)); 398 } 399 400 int buffer_eq_icase_slen(const buffer * const b, const char * const s, const size_t slen) { 401 /* Note: b must be initialized, i.e. 0 != b->used; uninitialized is not eq*/ 402 /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */ 403 return (b->used == slen + 1 && buffer_eq_icase_ssn(b->ptr, s, slen)); 404 } 405 406 int buffer_eq_slen(const buffer * const b, const char * const s, const size_t slen) { 407 /* Note: b must be initialized, i.e. 0 != b->used; uninitialized is not eq*/ 408 /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */ 409 return (b->used == slen + 1 && 0 == memcmp(b->ptr, s, slen)); 410 } 411 412 413 /** 414 * check if two buffer contain the same data 415 */ 416 417 int buffer_is_equal(const buffer *a, const buffer *b) { 418 /* 1 = equal; 0 = not equal */ 419 return (a->used == b->used && 0 == memcmp(a->ptr, b->ptr, a->used)); 420 } 421 422 int buffer_is_equal_string(const buffer *a, const char *s, size_t b_len) { 423 force_assert(b_len + 1 > b_len); 424 425 /* 1 = equal; 0 = not equal */ 426 return (a->used == b_len + 1 && 0 == memcmp(a->ptr, s, b_len)); 427 } 428 429 int buffer_is_equal_right_len(const buffer *b1, const buffer *b2, size_t len) { 430 /* no len -> equal */ 431 if (len == 0) return 1; 432 433 /* len > 0, but empty buffers -> not equal */ 434 if (b1->used == 0 || b2->used == 0) return 0; 435 436 /* buffers too small -> not equal */ 437 if (b1->used - 1 < len || b2->used - 1 < len) return 0; 438 439 return 0 == memcmp(b1->ptr + b1->used - 1 - len, b2->ptr + b2->used - 1 - len, len); 440 } 441 442 443 void li_tohex_lc(char * const restrict buf, size_t buf_len, const char * const restrict s, size_t s_len) { 444 force_assert(2 * s_len > s_len); 445 force_assert(2 * s_len < buf_len); 446 447 for (size_t i = 0; i < s_len; ++i) { 448 buf[2*i] = hex_chars_lc[(s[i] >> 4) & 0x0F]; 449 buf[2*i+1] = hex_chars_lc[s[i] & 0x0F]; 450 } 451 buf[2*s_len] = '\0'; 452 } 453 454 void li_tohex_uc(char * const restrict buf, size_t buf_len, const char * const restrict s, size_t s_len) { 455 force_assert(2 * s_len > s_len); 456 force_assert(2 * s_len < buf_len); 457 458 for (size_t i = 0; i < s_len; ++i) { 459 buf[2*i] = hex_chars_uc[(s[i] >> 4) & 0x0F]; 460 buf[2*i+1] = hex_chars_uc[s[i] & 0x0F]; 461 } 462 buf[2*s_len] = '\0'; 463 } 464 465 466 void buffer_substr_replace (buffer * const restrict b, const size_t offset, 467 const size_t len, const buffer * const restrict replace) 468 { 469 const size_t blen = buffer_string_length(b); 470 const size_t rlen = buffer_string_length(replace); 471 472 if (rlen > len) { 473 buffer_string_set_length(b, blen-len+rlen); 474 memmove(b->ptr+offset+rlen, b->ptr+offset+len, blen-offset-len); 475 } 476 477 memcpy(b->ptr+offset, replace->ptr, rlen); 478 479 if (rlen < len) { 480 memmove(b->ptr+offset+rlen, b->ptr+offset+len, blen-offset-len); 481 buffer_string_set_length(b, blen-len+rlen); 482 } 483 } 484 485 486 void buffer_append_string_encoded_hex_lc(buffer * const restrict b, const char * const restrict s, size_t len) { 487 unsigned char * const p = (unsigned char *)buffer_extend(b, len*2); 488 for (size_t i = 0; i < len; ++i) { 489 p[(i<<1)] = hex_chars_lc[(s[i] >> 4) & 0x0F]; 490 p[(i<<1)+1] = hex_chars_lc[(s[i]) & 0x0F]; 491 } 492 } 493 494 void buffer_append_string_encoded_hex_uc(buffer * const restrict b, const char * const restrict s, size_t len) { 495 unsigned char * const p = (unsigned char *)buffer_extend(b, len*2); 496 for (size_t i = 0; i < len; ++i) { 497 p[(i<<1)] = hex_chars_uc[(s[i] >> 4) & 0x0F]; 498 p[(i<<1)+1] = hex_chars_uc[(s[i]) & 0x0F]; 499 } 500 } 501 502 503 /* everything except: ! ( ) * - . 0-9 A-Z _ a-z */ 504 static const char encoded_chars_rel_uri_part[] = { 505 /* 506 0 1 2 3 4 5 6 7 8 9 A B C D E F 507 */ 508 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 509 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 510 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, /* 20 - 2F space " # $ % & ' + , / */ 511 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; < = > ? */ 512 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ 513 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ 514 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ 515 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } DEL */ 516 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 517 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 518 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ 519 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ 520 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ 521 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ 522 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ 523 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ 524 }; 525 526 /* everything except: ! ( ) * - . / 0-9 A-Z _ a-z */ 527 static const char encoded_chars_rel_uri[] = { 528 /* 529 0 1 2 3 4 5 6 7 8 9 A B C D E F 530 */ 531 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 532 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 533 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, /* 20 - 2F space " # $ % & ' + , */ 534 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; < = > ? */ 535 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ 536 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ 537 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ 538 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } DEL */ 539 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 540 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 541 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ 542 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ 543 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ 544 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ 545 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ 546 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ 547 }; 548 549 static const char encoded_chars_html[] = { 550 /* 551 0 1 2 3 4 5 6 7 8 9 A B C D E F 552 */ 553 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 554 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 555 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F " & ' */ 556 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ 557 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ 558 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ 559 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ 560 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ 561 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 562 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 563 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ 564 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ 565 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ 566 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ 567 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ 568 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ 569 }; 570 571 static const char encoded_chars_minimal_xml[] = { 572 /* 573 0 1 2 3 4 5 6 7 8 9 A B C D E F 574 */ 575 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 576 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 577 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F " & ' */ 578 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ 579 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ 580 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ 581 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ 582 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ 583 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 584 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 585 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 586 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 587 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 588 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 589 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 590 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ 591 }; 592 593 594 595 void buffer_append_string_encoded(buffer * const restrict b, const char * const restrict s, size_t s_len, buffer_encoding_t encoding) { 596 unsigned char *ds, *d; 597 size_t d_len, ndx; 598 const char *map = NULL; 599 600 if (0 == s_len) return; 601 602 force_assert(NULL != s); 603 604 switch(encoding) { 605 case ENCODING_REL_URI: 606 map = encoded_chars_rel_uri; 607 break; 608 case ENCODING_REL_URI_PART: 609 map = encoded_chars_rel_uri_part; 610 break; 611 case ENCODING_HTML: 612 map = encoded_chars_html; 613 break; 614 case ENCODING_MINIMAL_XML: 615 map = encoded_chars_minimal_xml; 616 break; 617 } 618 619 force_assert(NULL != map); 620 621 /* count to-be-encoded-characters */ 622 for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { 623 if (map[*ds & 0xFF]) { 624 switch(encoding) { 625 case ENCODING_REL_URI: 626 case ENCODING_REL_URI_PART: 627 d_len += 3; 628 break; 629 case ENCODING_HTML: 630 case ENCODING_MINIMAL_XML: 631 d_len += 6; 632 break; 633 } 634 } else { 635 d_len++; 636 } 637 } 638 639 d = (unsigned char*) buffer_extend(b, d_len); 640 641 if (d_len == s_len) { /*(short-circuit; nothing to encoded)*/ 642 memcpy(d, s, s_len); 643 return; 644 } 645 646 for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { 647 if (map[*ds & 0xFF]) { 648 switch(encoding) { 649 case ENCODING_REL_URI: 650 case ENCODING_REL_URI_PART: 651 d[d_len++] = '%'; 652 d[d_len++] = hex_chars_uc[((*ds) >> 4) & 0x0F]; 653 d[d_len++] = hex_chars_uc[(*ds) & 0x0F]; 654 break; 655 case ENCODING_HTML: 656 case ENCODING_MINIMAL_XML: 657 d[d_len++] = '&'; 658 d[d_len++] = '#'; 659 d[d_len++] = 'x'; 660 d[d_len++] = hex_chars_uc[((*ds) >> 4) & 0x0F]; 661 d[d_len++] = hex_chars_uc[(*ds) & 0x0F]; 662 d[d_len++] = ';'; 663 break; 664 } 665 } else { 666 d[d_len++] = *ds; 667 } 668 } 669 } 670 671 void buffer_append_string_c_escaped(buffer * const restrict b, const char * const restrict s, size_t s_len) { 672 unsigned char *ds, *d; 673 size_t d_len, ndx; 674 675 if (0 == s_len) return; 676 677 /* count to-be-encoded-characters */ 678 for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { 679 if ((*ds < 0x20) /* control character */ 680 || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ 681 switch (*ds) { 682 case '\t': 683 case '\r': 684 case '\n': 685 d_len += 2; 686 break; 687 default: 688 d_len += 4; /* \xCC */ 689 break; 690 } 691 } else { 692 d_len++; 693 } 694 } 695 696 d = (unsigned char*) buffer_extend(b, d_len); 697 698 if (d_len == s_len) { /*(short-circuit; nothing to encoded)*/ 699 memcpy(d, s, s_len); 700 return; 701 } 702 703 for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { 704 if ((*ds < 0x20) /* control character */ 705 || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ 706 d[d_len++] = '\\'; 707 switch (*ds) { 708 case '\t': 709 d[d_len++] = 't'; 710 break; 711 case '\r': 712 d[d_len++] = 'r'; 713 break; 714 case '\n': 715 d[d_len++] = 'n'; 716 break; 717 default: 718 d[d_len++] = 'x'; 719 d[d_len++] = hex_chars_lc[((*ds) >> 4) & 0x0F]; 720 d[d_len++] = hex_chars_lc[(*ds) & 0x0F]; 721 break; 722 } 723 } else { 724 d[d_len++] = *ds; 725 } 726 } 727 } 728 729 730 /* decodes url-special-chars inplace. 731 * replaces non-printable characters with '_' 732 * (If this is used on a portion of query string, then query string should be 733 * split on '&', and '+' replaced with ' ' before calling this routine) 734 */ 735 736 void buffer_urldecode_path(buffer * const b) { 737 const size_t len = buffer_string_length(b); 738 char *src = len ? memchr(b->ptr, '%', len) : NULL; 739 if (NULL == src) return; 740 741 char *dst = src; 742 do { 743 /* *src == '%' */ 744 unsigned char high = hex2int(*(src + 1)); 745 unsigned char low = hex2int(*(src + 2)); 746 if (0xFF != high && 0xFF != low) { 747 high = (high << 4) | low; /* map ctrls to '_' */ 748 *dst = (high >= 32 && high != 127) ? high : '_'; 749 src += 2; 750 } /* else ignore this '%'; leave as-is and move on */ 751 752 while ((*++dst = *++src) != '%' && *src) ; 753 } while (*src); 754 b->used = (dst - b->ptr) + 1; 755 } 756 757 int buffer_is_valid_UTF8(const buffer *b) { 758 /* https://www.w3.org/International/questions/qa-forms-utf-8 */ 759 const unsigned char *c = (unsigned char *)b->ptr; 760 while (*c) { 761 762 /*(note: includes ctrls)*/ 763 if ( c[0] < 0x80 ) { ++c; continue; } 764 765 if ( 0xc2 <= c[0] && c[0] <= 0xdf 766 && 0x80 <= c[1] && c[1] <= 0xbf ) { c+=2; continue; } 767 768 if ( ( ( 0xe0 == c[0] 769 && 0xa0 <= c[1] && c[1] <= 0xbf) 770 || ( 0xe1 <= c[0] && c[0] <= 0xef && c[0] != 0xed 771 && 0x80 <= c[1] && c[1] <= 0xbf) 772 || ( 0xed == c[0] 773 && 0x80 <= c[1] && c[1] <= 0x9f) ) 774 && 0x80 <= c[2] && c[2] <= 0xbf ) { c+=3; continue; } 775 776 if ( ( ( 0xf0 == c[0] 777 && 0x90 <= c[1] && c[1] <= 0xbf) 778 || ( 0xf1 <= c[0] && c[0] <= 0xf3 779 && 0x80 <= c[1] && c[1] <= 0xbf) 780 || ( 0xf4 == c[0] 781 && 0x80 <= c[1] && c[1] <= 0x8f) ) 782 && 0x80 <= c[2] && c[2] <= 0xbf 783 && 0x80 <= c[3] && c[3] <= 0xbf ) { c+=4; continue; } 784 785 return 0; /* invalid */ 786 } 787 return 1; /* valid */ 788 } 789 790 /* - special case: empty string returns empty string 791 * - on windows or cygwin: replace \ with / 792 * - strip leading spaces 793 * - prepends "/" if not present already 794 * - resolve "/../", "//" and "/./" the usual way: 795 * the first one removes a preceding component, the other two 796 * get compressed to "/". 797 * - "/." and "/.." at the end are similar, but always leave a trailing 798 * "/" 799 * 800 * /blah/.. gets / 801 * /blah/../foo gets /foo 802 * /abc/./xyz gets /abc/xyz 803 * /abc//xyz gets /abc/xyz 804 * 805 * NOTE: src and dest can point to the same buffer, in which case, 806 * the operation is performed in-place. 807 */ 808 809 void buffer_path_simplify(buffer *dest, buffer *src) 810 { 811 /* current character, the one before, and the one before that from input */ 812 char c, pre1, pre2; 813 char *start, *slash, *walk, *out; 814 815 if (buffer_string_is_empty(src)) { 816 buffer_copy_string_len(dest, CONST_STR_LEN("")); 817 return; 818 } 819 820 force_assert('\0' == src->ptr[src->used-1]); 821 822 #if defined(__WIN32) || defined(__CYGWIN__) 823 /* cygwin is treating \ and / the same, so we have to that too */ 824 { 825 char *p; 826 for (p = src->ptr; *p; p++) { 827 if (*p == '\\') *p = '/'; 828 } 829 } 830 #endif 831 832 walk = src->ptr; 833 start = dest->ptr; 834 out = dest->ptr; 835 slash = dest->ptr; 836 837 /* skip leading spaces */ 838 while (*walk == ' ') { 839 walk++; 840 } 841 if (*walk == '.') { 842 if (walk[1] == '/' || walk[1] == '\0') 843 ++walk; 844 else if (walk[1] == '.' && (walk[2] == '/' || walk[2] == '\0')) 845 walk+=2; 846 } 847 848 pre1 = 0; 849 c = *(walk++); 850 851 while (c != '\0') { 852 /* assert((src != dest || out <= walk) && slash <= out); */ 853 /* the following comments about out and walk are only interesting if 854 * src == dest; otherwise the memory areas don't overlap anyway. 855 */ 856 pre2 = pre1; 857 pre1 = c; 858 859 /* possibly: out == walk - need to read first */ 860 c = *walk; 861 *out = pre1; 862 863 out++; 864 walk++; 865 /* (out <= walk) still true; also now (slash < out) */ 866 867 if (c == '/' || c == '\0') { 868 const size_t toklen = out - slash; 869 if (toklen == 3 && pre2 == '.' && pre1 == '.' && *slash == '/') { 870 /* "/../" or ("/.." at end of string) */ 871 out = slash; 872 /* if there is something before "/..", there is at least one 873 * component, which needs to be removed */ 874 if (out > start) { 875 out--; 876 while (out > start && *out != '/') out--; 877 } 878 879 /* don't kill trailing '/' at end of path */ 880 if (c == '\0') out++; 881 /* slash < out before, so out_new <= slash + 1 <= out_before <= walk */ 882 } else if (toklen == 1 || (pre2 == '/' && pre1 == '.')) { 883 /* "//" or "/./" or (("/" or "/.") at end of string) */ 884 out = slash; 885 /* don't kill trailing '/' at end of path */ 886 if (c == '\0') out++; 887 /* slash < out before, so out_new <= slash + 1 <= out_before <= walk */ 888 } 889 890 slash = out; 891 } 892 } 893 894 buffer_string_set_length(dest, out - start); 895 } 896 897 void buffer_to_lower(buffer * const b) { 898 unsigned char * const restrict s = (unsigned char *)b->ptr; 899 for (uint32_t i = 0; i < b->used; ++i) { 900 if (light_isupper(s[i])) s[i] |= 0x20; 901 } 902 } 903 904 905 void buffer_to_upper(buffer * const b) { 906 unsigned char * const restrict s = (unsigned char *)b->ptr; 907 for (uint32_t i = 0; i < b->used; ++i) { 908 if (light_islower(s[i])) s[i] &= 0xdf; 909 } 910 } 911 912 913 #include <stdio.h> 914 915 #ifdef HAVE_LIBUNWIND 916 # define UNW_LOCAL_ONLY 917 # include <libunwind.h> 918 919 static void print_backtrace(FILE *file) { 920 unw_cursor_t cursor; 921 unw_context_t context; 922 int ret; 923 unsigned int frame = 0; 924 925 if (0 != (ret = unw_getcontext(&context))) goto error; 926 if (0 != (ret = unw_init_local(&cursor, &context))) goto error; 927 928 fprintf(file, "Backtrace:\n"); 929 930 while (0 < (ret = unw_step(&cursor))) { 931 unw_word_t proc_ip = 0; 932 unw_proc_info_t procinfo; 933 char procname[256]; 934 unw_word_t proc_offset = 0; 935 936 if (0 != (ret = unw_get_reg(&cursor, UNW_REG_IP, &proc_ip))) goto error; 937 938 if (0 == proc_ip) { 939 /* without an IP the other functions are useless; unw_get_proc_name would return UNW_EUNSPEC */ 940 ++frame; 941 fprintf(file, "%u: (nil)\n", frame); 942 continue; 943 } 944 945 if (0 != (ret = unw_get_proc_info(&cursor, &procinfo))) goto error; 946 947 if (0 != (ret = unw_get_proc_name(&cursor, procname, sizeof(procname), &proc_offset))) { 948 switch (-ret) { 949 case UNW_ENOMEM: 950 memset(procname + sizeof(procname) - 4, '.', 3); 951 procname[sizeof(procname) - 1] = '\0'; 952 break; 953 case UNW_ENOINFO: 954 procname[0] = '?'; 955 procname[1] = '\0'; 956 proc_offset = 0; 957 break; 958 default: 959 snprintf(procname, sizeof(procname), "?? (unw_get_proc_name error %d)", -ret); 960 break; 961 } 962 } 963 964 ++frame; 965 fprintf(file, "%u: %s (+0x%x) [%p]\n", 966 frame, 967 procname, 968 (unsigned int) proc_offset, 969 (void*)(uintptr_t)proc_ip); 970 } 971 972 if (0 != ret) goto error; 973 974 return; 975 976 error: 977 fprintf(file, "Error while generating backtrace: unwind error %i\n", (int) -ret); 978 } 979 #else 980 static void print_backtrace(FILE *file) { 981 UNUSED(file); 982 } 983 #endif 984 985 void log_failed_assert(const char *filename, unsigned int line, const char *msg) { 986 /* can't use buffer here; could lead to recursive assertions */ 987 fprintf(stderr, "%s.%u: %s\n", filename, line, msg); 988 print_backtrace(stderr); 989 fflush(stderr); 990 abort(); 991 } 992