1 #include "buffer.h" 2 3 #include <stdlib.h> 4 #include <string.h> 5 6 #include <stdio.h> 7 #include <assert.h> 8 #include <ctype.h> 9 10 static const char hex_chars[] = "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_reset(buffer *b) { 50 if (NULL == b) return; 51 52 /* limit don't reuse buffer larger than ... bytes */ 53 if (b->size > BUFFER_MAX_REUSE_SIZE) { 54 free(b->ptr); 55 b->ptr = NULL; 56 b->size = 0; 57 } else if (b->size > 0) { 58 b->ptr[0] = '\0'; 59 } 60 61 b->used = 0; 62 } 63 64 void buffer_move(buffer *b, buffer *src) { 65 buffer tmp; 66 67 if (NULL == b) { 68 buffer_reset(src); 69 return; 70 } 71 buffer_reset(b); 72 if (NULL == src) return; 73 74 tmp = *src; *src = *b; *b = tmp; 75 } 76 77 #define BUFFER_PIECE_SIZE 64 78 static size_t buffer_align_size(size_t size) { 79 size_t align = BUFFER_PIECE_SIZE - (size % BUFFER_PIECE_SIZE); 80 /* overflow on unsinged size_t is defined to wrap around */ 81 if (size + align < size) return size; 82 return size + align; 83 } 84 85 /* make sure buffer is at least "size" big. discard old data */ 86 static void buffer_alloc(buffer *b, size_t size) { 87 force_assert(NULL != b); 88 if (0 == size) size = 1; 89 90 if (size <= b->size) return; 91 92 if (NULL != b->ptr) free(b->ptr); 93 94 b->used = 0; 95 b->size = buffer_align_size(size); 96 b->ptr = malloc(b->size); 97 98 force_assert(NULL != b->ptr); 99 } 100 101 /* make sure buffer is at least "size" big. keep old data */ 102 static void buffer_realloc(buffer *b, size_t size) { 103 force_assert(NULL != b); 104 if (0 == size) size = 1; 105 106 if (size <= b->size) return; 107 108 b->size = buffer_align_size(size); 109 b->ptr = realloc(b->ptr, b->size); 110 111 force_assert(NULL != b->ptr); 112 } 113 114 115 char* buffer_string_prepare_copy(buffer *b, size_t size) { 116 force_assert(NULL != b); 117 force_assert(size + 1 > size); 118 119 buffer_alloc(b, size + 1); 120 121 b->used = 1; 122 b->ptr[0] = '\0'; 123 124 return b->ptr; 125 } 126 127 char* buffer_string_prepare_append(buffer *b, size_t size) { 128 force_assert(NULL != b); 129 130 if (buffer_string_is_empty(b)) { 131 return buffer_string_prepare_copy(b, size); 132 } else { 133 size_t req_size = b->used + size; 134 135 /* not empty, b->used already includes a terminating 0 */ 136 force_assert(req_size >= b->used); 137 138 /* check for overflow: unsigned overflow is defined to wrap around */ 139 force_assert(req_size >= b->used); 140 141 buffer_realloc(b, req_size); 142 143 return b->ptr + b->used - 1; 144 } 145 } 146 147 void buffer_string_set_length(buffer *b, size_t len) { 148 force_assert(NULL != b); 149 force_assert(len + 1 > len); 150 151 buffer_realloc(b, len + 1); 152 153 b->used = len + 1; 154 b->ptr[len] = '\0'; 155 } 156 157 void buffer_commit(buffer *b, size_t size) 158 { 159 force_assert(NULL != b); 160 force_assert(b->size > 0); 161 162 if (0 == b->used) b->used = 1; 163 164 if (size > 0) { 165 /* check for overflow: unsigned overflow is defined to wrap around */ 166 force_assert(b->used + size > b->used); 167 168 force_assert(b->used + size <= b->size); 169 b->used += size; 170 } 171 172 b->ptr[b->used - 1] = '\0'; 173 } 174 175 void buffer_copy_string(buffer *b, const char *s) { 176 buffer_copy_string_len(b, s, NULL != s ? strlen(s) : 0); 177 } 178 179 void buffer_copy_string_len(buffer *b, const char *s, size_t s_len) { 180 force_assert(NULL != b); 181 force_assert(NULL != s || s_len == 0); 182 183 buffer_string_prepare_copy(b, s_len); 184 185 if (0 != s_len) memcpy(b->ptr, s, s_len); 186 187 buffer_commit(b, s_len); 188 } 189 190 void buffer_copy_buffer(buffer *b, const buffer *src) { 191 if (NULL == src || 0 == src->used) { 192 buffer_string_prepare_copy(b, 0); 193 b->used = 0; /* keep special empty state for now */ 194 } else { 195 buffer_copy_string_len(b, src->ptr, buffer_string_length(src)); 196 } 197 } 198 199 void buffer_append_string(buffer *b, const char *s) { 200 buffer_append_string_len(b, s, NULL != s ? strlen(s) : 0); 201 } 202 203 /** 204 * append a string to the end of the buffer 205 * 206 * the resulting buffer is terminated with a '\0' 207 * s is treated as a un-terminated string (a \0 is handled a normal character) 208 * 209 * @param b a buffer 210 * @param s the string 211 * @param s_len size of the string (without the terminating \0) 212 */ 213 214 void buffer_append_string_len(buffer *b, const char *s, size_t s_len) { 215 char *target_buf; 216 217 force_assert(NULL != b); 218 force_assert(NULL != s || s_len == 0); 219 220 target_buf = buffer_string_prepare_append(b, s_len); 221 222 if (0 == s_len) return; /* nothing to append */ 223 224 memcpy(target_buf, s, s_len); 225 226 buffer_commit(b, s_len); 227 } 228 229 void buffer_append_string_buffer(buffer *b, const buffer *src) { 230 if (NULL == src) { 231 buffer_append_string_len(b, NULL, 0); 232 } else { 233 buffer_append_string_len(b, src->ptr, buffer_string_length(src)); 234 } 235 } 236 237 void buffer_append_uint_hex(buffer *b, uintmax_t value) { 238 char *buf; 239 int shift = 0; 240 241 { 242 uintmax_t copy = value; 243 do { 244 copy >>= 8; 245 shift += 2; /* counting nibbles (4 bits) */ 246 } while (0 != copy); 247 } 248 249 buf = buffer_string_prepare_append(b, shift); 250 buffer_commit(b, shift); /* will fill below */ 251 252 shift <<= 2; /* count bits now */ 253 while (shift > 0) { 254 shift -= 4; 255 *(buf++) = hex_chars[(value >> shift) & 0x0F]; 256 } 257 } 258 259 static char* utostr(char * const buf_end, uintmax_t val) { 260 char *cur = buf_end; 261 do { 262 int mod = val % 10; 263 val /= 10; 264 /* prepend digit mod */ 265 *(--cur) = (char) ('0' + mod); 266 } while (0 != val); 267 return cur; 268 } 269 270 static char* itostr(char * const buf_end, intmax_t val) { 271 char *cur = buf_end; 272 if (val >= 0) return utostr(buf_end, (uintmax_t) val); 273 274 /* can't take absolute value, as it isn't defined for INTMAX_MIN */ 275 do { 276 int mod = val % 10; 277 val /= 10; 278 /* val * 10 + mod == orig val, -10 < mod < 10 */ 279 /* we want a negative mod */ 280 if (mod > 0) { 281 mod -= 10; 282 val += 1; 283 } 284 /* prepend digit abs(mod) */ 285 *(--cur) = (char) ('0' + (-mod)); 286 } while (0 != val); 287 *(--cur) = '-'; 288 289 return cur; 290 } 291 292 void buffer_append_int(buffer *b, intmax_t val) { 293 char buf[LI_ITOSTRING_LENGTH]; 294 char* const buf_end = buf + sizeof(buf); 295 char *str; 296 297 force_assert(NULL != b); 298 299 str = itostr(buf_end, val); 300 force_assert(buf_end > str && str >= buf); 301 302 buffer_append_string_len(b, str, buf_end - str); 303 } 304 305 void buffer_copy_int(buffer *b, intmax_t val) { 306 force_assert(NULL != b); 307 308 b->used = 0; 309 buffer_append_int(b, val); 310 } 311 312 void buffer_append_strftime(buffer *b, const char *format, const struct tm *tm) { 313 size_t r; 314 char* buf; 315 force_assert(NULL != b); 316 force_assert(NULL != tm); 317 318 if (NULL == format || '\0' == format[0]) { 319 /* empty format */ 320 buffer_string_prepare_append(b, 0); 321 return; 322 } 323 324 buf = buffer_string_prepare_append(b, 255); 325 r = strftime(buf, buffer_string_space(b), format, tm); 326 327 /* 0 (in some apis buffer_string_space(b)) signals the string may have 328 * been too small; but the format could also just have lead to an empty 329 * string 330 */ 331 if (0 == r || r >= buffer_string_space(b)) { 332 /* give it a second try with a larger string */ 333 buf = buffer_string_prepare_append(b, 4095); 334 r = strftime(buf, buffer_string_space(b), format, tm); 335 } 336 337 if (r >= buffer_string_space(b)) r = 0; 338 339 buffer_commit(b, r); 340 } 341 342 343 void li_itostrn(char *buf, size_t buf_len, intmax_t val) { 344 char p_buf[LI_ITOSTRING_LENGTH]; 345 char* const p_buf_end = p_buf + sizeof(p_buf); 346 char* str = p_buf_end - 1; 347 *str = '\0'; 348 349 str = itostr(str, val); 350 force_assert(p_buf_end > str && str >= p_buf); 351 352 force_assert(buf_len >= (size_t) (p_buf_end - str)); 353 memcpy(buf, str, p_buf_end - str); 354 } 355 356 void li_itostr(char *buf, intmax_t val) { 357 li_itostrn(buf, LI_ITOSTRING_LENGTH, val); 358 } 359 360 void li_utostrn(char *buf, size_t buf_len, uintmax_t val) { 361 char p_buf[LI_ITOSTRING_LENGTH]; 362 char* const p_buf_end = p_buf + sizeof(p_buf); 363 char* str = p_buf_end - 1; 364 *str = '\0'; 365 366 str = utostr(str, val); 367 force_assert(p_buf_end > str && str >= p_buf); 368 369 force_assert(buf_len >= (size_t) (p_buf_end - str)); 370 memcpy(buf, str, p_buf_end - str); 371 } 372 373 void li_utostr(char *buf, uintmax_t val) { 374 li_utostrn(buf, LI_ITOSTRING_LENGTH, val); 375 } 376 377 char int2hex(char c) { 378 return hex_chars[(c & 0x0F)]; 379 } 380 381 /* converts hex char (0-9, A-Z, a-z) to decimal. 382 * returns 0xFF on invalid input. 383 */ 384 char hex2int(unsigned char hex) { 385 unsigned char value = hex - '0'; 386 if (value > 9) { 387 hex |= 0x20; /* to lower case */ 388 value = hex - 'a' + 10; 389 if (value < 10) value = 0xff; 390 } 391 if (value > 15) value = 0xff; 392 393 return value; 394 } 395 396 char * buffer_search_string_len(buffer *b, const char *needle, size_t len) { 397 size_t i; 398 force_assert(NULL != b); 399 force_assert(0 != len && NULL != needle); /* empty needles not allowed */ 400 401 if (b->used < len) return NULL; 402 403 for(i = 0; i < b->used - len; i++) { 404 if (0 == memcmp(b->ptr + i, needle, len)) { 405 return b->ptr + i; 406 } 407 } 408 409 return NULL; 410 } 411 412 int buffer_is_empty(const buffer *b) { 413 return NULL == b || 0 == b->used; 414 } 415 416 int buffer_string_is_empty(const buffer *b) { 417 return 0 == buffer_string_length(b); 418 } 419 420 /** 421 * check if two buffer contain the same data 422 * 423 * HISTORY: this function was pretty much optimized, but didn't handled 424 * alignment properly. 425 */ 426 427 int buffer_is_equal(const buffer *a, const buffer *b) { 428 force_assert(NULL != a && NULL != b); 429 430 if (a->used != b->used) return 0; 431 if (a->used == 0) return 1; 432 433 return (0 == memcmp(a->ptr, b->ptr, a->used)); 434 } 435 436 int buffer_is_equal_string(const buffer *a, const char *s, size_t b_len) { 437 force_assert(NULL != a && NULL != s); 438 force_assert(b_len + 1 > b_len); 439 440 if (a->used != b_len + 1) return 0; 441 if (0 != memcmp(a->ptr, s, b_len)) return 0; 442 if ('\0' != a->ptr[a->used-1]) return 0; 443 444 return 1; 445 } 446 447 /* buffer_is_equal_caseless_string(b, CONST_STR_LEN("value")) */ 448 int buffer_is_equal_caseless_string(const buffer *a, const char *s, size_t b_len) { 449 force_assert(NULL != a); 450 if (a->used != b_len + 1) return 0; 451 force_assert('\0' == a->ptr[a->used - 1]); 452 453 return (0 == strcasecmp(a->ptr, s)); 454 } 455 456 int buffer_caseless_compare(const char *a, size_t a_len, const char *b, size_t b_len) { 457 size_t const len = (a_len < b_len) ? a_len : b_len; 458 size_t i; 459 460 for (i = 0; i < len; ++i) { 461 unsigned char ca = a[i], cb = b[i]; 462 if (ca == cb) continue; 463 464 /* always lowercase for transitive results */ 465 if (ca >= 'A' && ca <= 'Z') ca |= 32; 466 if (cb >= 'A' && cb <= 'Z') cb |= 32; 467 468 if (ca == cb) continue; 469 return ca - cb; 470 } 471 if (a_len == b_len) return 0; 472 return a_len < b_len ? -1 : 1; 473 } 474 475 int buffer_is_equal_right_len(const buffer *b1, const buffer *b2, size_t len) { 476 /* no len -> equal */ 477 if (len == 0) return 1; 478 479 /* len > 0, but empty buffers -> not equal */ 480 if (b1->used == 0 || b2->used == 0) return 0; 481 482 /* buffers too small -> not equal */ 483 if (b1->used - 1 < len || b2->used - 1 < len) return 0; 484 485 return 0 == memcmp(b1->ptr + b1->used - 1 - len, b2->ptr + b2->used - 1 - len, len); 486 } 487 488 void li_tohex(char *buf, const char *s, size_t s_len) { 489 size_t i; 490 491 for (i = 0; i < s_len; i++) { 492 buf[2*i] = hex_chars[(s[i] >> 4) & 0x0F]; 493 buf[2*i+1] = hex_chars[s[i] & 0x0F]; 494 } 495 buf[2*s_len] = '\0'; 496 } 497 498 void buffer_copy_string_hex(buffer *b, const char *in, size_t in_len) { 499 /* overflow protection */ 500 force_assert(in_len * 2 > in_len); 501 502 buffer_string_set_length(b, 2 * in_len); 503 li_tohex(b->ptr, in, in_len); 504 } 505 506 /* everything except: ! ( ) * - . 0-9 A-Z _ a-z ~ */ 507 static const char encoded_chars_rel_uri_part[] = { 508 /* 509 0 1 2 3 4 5 6 7 8 9 A B C D E F 510 */ 511 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 512 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 513 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, /* 20 - 2F space " # $ % & ' + , / */ 514 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; < = > ? */ 515 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ 516 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ 517 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ 518 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } ~ DEL */ 519 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 520 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 521 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ 522 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ 523 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ 524 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ 525 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ 526 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ 527 }; 528 529 /* everything except: ! ( ) * - . / 0-9 A-Z _ a-z ~ */ 530 static const char encoded_chars_rel_uri[] = { 531 /* 532 0 1 2 3 4 5 6 7 8 9 A B C D E F 533 */ 534 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 535 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 536 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, /* 20 - 2F space " # $ % & ' + , */ 537 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; < = > ? */ 538 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ 539 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ 540 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ 541 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } ~ DEL */ 542 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 543 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 544 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ 545 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ 546 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ 547 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ 548 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ 549 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ 550 }; 551 552 static const char encoded_chars_html[] = { 553 /* 554 0 1 2 3 4 5 6 7 8 9 A B C D E F 555 */ 556 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 557 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 558 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F & */ 559 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ 560 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ 561 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ 562 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ 563 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ 564 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 565 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 566 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ 567 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ 568 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ 569 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ 570 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ 571 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ 572 }; 573 574 static const char encoded_chars_minimal_xml[] = { 575 /* 576 0 1 2 3 4 5 6 7 8 9 A B C D E F 577 */ 578 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 579 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 580 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F & */ 581 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ 582 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ 583 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ 584 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ 585 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ 586 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 587 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 588 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 589 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 590 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 591 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 592 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 593 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ 594 }; 595 596 static const char encoded_chars_hex[] = { 597 /* 598 0 1 2 3 4 5 6 7 8 9 A B C D E F 599 */ 600 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ 601 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ 602 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ 603 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */ 604 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ 605 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ 606 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ 607 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 70 - 7F */ 608 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ 609 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ 610 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ 611 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ 612 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ 613 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ 614 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ 615 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ 616 }; 617 618 static const char encoded_chars_http_header[] = { 619 /* 620 0 1 2 3 4 5 6 7 8 9 A B C D E F 621 */ 622 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 00 - 0F */ 623 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ 624 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F */ 625 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 30 - 3F */ 626 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ 627 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ 628 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ 629 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */ 630 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 631 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 632 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 633 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 634 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 635 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 636 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 637 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ 638 }; 639 640 641 642 void buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding) { 643 unsigned char *ds, *d; 644 size_t d_len, ndx; 645 const char *map = NULL; 646 647 force_assert(NULL != b); 648 force_assert(NULL != s || 0 == s_len); 649 650 if (0 == s_len) return; 651 652 switch(encoding) { 653 case ENCODING_REL_URI: 654 map = encoded_chars_rel_uri; 655 break; 656 case ENCODING_REL_URI_PART: 657 map = encoded_chars_rel_uri_part; 658 break; 659 case ENCODING_HTML: 660 map = encoded_chars_html; 661 break; 662 case ENCODING_MINIMAL_XML: 663 map = encoded_chars_minimal_xml; 664 break; 665 case ENCODING_HEX: 666 map = encoded_chars_hex; 667 break; 668 case ENCODING_HTTP_HEADER: 669 map = encoded_chars_http_header; 670 break; 671 } 672 673 force_assert(NULL != map); 674 675 /* count to-be-encoded-characters */ 676 for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { 677 if (map[*ds]) { 678 switch(encoding) { 679 case ENCODING_REL_URI: 680 case ENCODING_REL_URI_PART: 681 d_len += 3; 682 break; 683 case ENCODING_HTML: 684 case ENCODING_MINIMAL_XML: 685 d_len += 6; 686 break; 687 case ENCODING_HTTP_HEADER: 688 case ENCODING_HEX: 689 d_len += 2; 690 break; 691 } 692 } else { 693 d_len++; 694 } 695 } 696 697 d = (unsigned char*) buffer_string_prepare_append(b, d_len); 698 buffer_commit(b, d_len); /* fill below */ 699 force_assert('\0' == *d); 700 701 for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { 702 if (map[*ds]) { 703 switch(encoding) { 704 case ENCODING_REL_URI: 705 case ENCODING_REL_URI_PART: 706 d[d_len++] = '%'; 707 d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; 708 d[d_len++] = hex_chars[(*ds) & 0x0F]; 709 break; 710 case ENCODING_HTML: 711 case ENCODING_MINIMAL_XML: 712 d[d_len++] = '&'; 713 d[d_len++] = '#'; 714 d[d_len++] = 'x'; 715 d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; 716 d[d_len++] = hex_chars[(*ds) & 0x0F]; 717 d[d_len++] = ';'; 718 break; 719 case ENCODING_HEX: 720 d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; 721 d[d_len++] = hex_chars[(*ds) & 0x0F]; 722 break; 723 case ENCODING_HTTP_HEADER: 724 d[d_len++] = *ds; 725 d[d_len++] = '\t'; 726 break; 727 } 728 } else { 729 d[d_len++] = *ds; 730 } 731 } 732 } 733 734 void buffer_append_string_c_escaped(buffer *b, const char *s, size_t s_len) { 735 unsigned char *ds, *d; 736 size_t d_len, ndx; 737 738 force_assert(NULL != b); 739 force_assert(NULL != s || 0 == s_len); 740 741 if (0 == s_len) return; 742 743 /* count to-be-encoded-characters */ 744 for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { 745 if ((*ds < 0x20) /* control character */ 746 || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ 747 switch (*ds) { 748 case '\t': 749 case '\r': 750 case '\n': 751 d_len += 2; 752 break; 753 default: 754 d_len += 4; /* \xCC */ 755 break; 756 } 757 } else { 758 d_len++; 759 } 760 } 761 762 d = (unsigned char*) buffer_string_prepare_append(b, d_len); 763 buffer_commit(b, d_len); /* fill below */ 764 force_assert('\0' == *d); 765 766 for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { 767 if ((*ds < 0x20) /* control character */ 768 || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ 769 d[d_len++] = '\\'; 770 switch (*ds) { 771 case '\t': 772 d[d_len++] = 't'; 773 break; 774 case '\r': 775 d[d_len++] = 'r'; 776 break; 777 case '\n': 778 d[d_len++] = 'n'; 779 break; 780 default: 781 d[d_len++] = 'x'; 782 d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; 783 d[d_len++] = hex_chars[(*ds) & 0x0F]; 784 break; 785 } 786 } else { 787 d[d_len++] = *ds; 788 } 789 } 790 } 791 792 793 void buffer_copy_string_encoded_cgi_varnames(buffer *b, const char *s, size_t s_len, int is_http_header) { 794 size_t i, j; 795 796 force_assert(NULL != b); 797 force_assert(NULL != s || 0 == s_len); 798 799 buffer_reset(b); 800 801 if (is_http_header && NULL != s && 0 != strcasecmp(s, "CONTENT-TYPE")) { 802 buffer_string_prepare_append(b, s_len + 5); 803 buffer_copy_string_len(b, CONST_STR_LEN("HTTP_")); 804 } else { 805 buffer_string_prepare_append(b, s_len); 806 } 807 808 j = buffer_string_length(b); 809 for (i = 0; i < s_len; ++i) { 810 unsigned char cr = s[i]; 811 if (light_isalpha(cr)) { 812 /* upper-case */ 813 cr &= ~32; 814 } else if (!light_isdigit(cr)) { 815 cr = '_'; 816 } 817 b->ptr[j++] = cr; 818 } 819 b->used = j; 820 b->ptr[b->used++] = '\0'; 821 } 822 823 /* decodes url-special-chars inplace. 824 * replaces non-printable characters with '_' 825 */ 826 827 static void buffer_urldecode_internal(buffer *url, int is_query) { 828 unsigned char high, low; 829 char *src; 830 char *dst; 831 832 force_assert(NULL != url); 833 if (buffer_string_is_empty(url)) return; 834 835 force_assert('\0' == url->ptr[url->used-1]); 836 837 src = (char*) url->ptr; 838 839 while ('\0' != *src) { 840 if ('%' == *src) break; 841 if (is_query && '+' == *src) *src = ' '; 842 src++; 843 } 844 dst = src; 845 846 while ('\0' != *src) { 847 if (is_query && *src == '+') { 848 *dst = ' '; 849 } else if (*src == '%') { 850 *dst = '%'; 851 852 high = hex2int(*(src + 1)); 853 if (0xFF != high) { 854 low = hex2int(*(src + 2)); 855 if (0xFF != low) { 856 high = (high << 4) | low; 857 858 /* map control-characters out */ 859 if (high < 32 || high == 127) high = '_'; 860 861 *dst = high; 862 src += 2; 863 } 864 } 865 } else { 866 *dst = *src; 867 } 868 869 dst++; 870 src++; 871 } 872 873 *dst = '\0'; 874 url->used = (dst - url->ptr) + 1; 875 } 876 877 void buffer_urldecode_path(buffer *url) { 878 buffer_urldecode_internal(url, 0); 879 } 880 881 void buffer_urldecode_query(buffer *url) { 882 buffer_urldecode_internal(url, 1); 883 } 884 885 /* Remove "/../", "//", "/./" parts from path, 886 * strips leading spaces, 887 * prepends "/" if not present already 888 * 889 * /blah/.. gets / 890 * /blah/../foo gets /foo 891 * /abc/./xyz gets /abc/xyz 892 * /abc//xyz gets /abc/xyz 893 * 894 * NOTE: src and dest can point to the same buffer, in which case, 895 * the operation is performed in-place. 896 */ 897 898 void buffer_path_simplify(buffer *dest, buffer *src) 899 { 900 int toklen; 901 char c, pre1; 902 char *start, *slash, *walk, *out; 903 unsigned short pre; 904 905 force_assert(NULL != dest && NULL != src); 906 907 if (buffer_string_is_empty(src)) { 908 buffer_string_prepare_copy(dest, 0); 909 return; 910 } 911 912 force_assert('\0' == src->ptr[src->used-1]); 913 914 /* might need one character more for the '/' prefix */ 915 if (src == dest) { 916 buffer_string_prepare_append(dest, 1); 917 } else { 918 buffer_string_prepare_copy(dest, buffer_string_length(src) + 1); 919 } 920 921 #if defined(__WIN32) || defined(__CYGWIN__) 922 /* cygwin is treating \ and / the same, so we have to that too */ 923 { 924 char *p; 925 for (p = src->ptr; *p; p++) { 926 if (*p == '\\') *p = '/'; 927 } 928 } 929 #endif 930 931 walk = src->ptr; 932 start = dest->ptr; 933 out = dest->ptr; 934 slash = dest->ptr; 935 936 937 while (*walk == ' ') { 938 walk++; 939 } 940 941 pre1 = *(walk++); 942 c = *(walk++); 943 pre = pre1; 944 if (pre1 != '/') { 945 pre = ('/' << 8) | pre1; 946 *(out++) = '/'; 947 } 948 *(out++) = pre1; 949 950 if (pre1 == '\0') { 951 dest->used = (out - start) + 1; 952 return; 953 } 954 955 for (;;) { 956 if (c == '/' || c == '\0') { 957 toklen = out - slash; 958 if (toklen == 3 && pre == (('.' << 8) | '.')) { 959 out = slash; 960 if (out > start) { 961 out--; 962 while (out > start && *out != '/') out--; 963 } 964 965 if (c == '\0') out++; 966 } else if (toklen == 1 || pre == (('/' << 8) | '.')) { 967 out = slash; 968 if (c == '\0') out++; 969 } 970 971 slash = out; 972 } 973 974 if (c == '\0') break; 975 976 pre1 = c; 977 pre = (pre << 8) | pre1; 978 c = *walk; 979 *out = pre1; 980 981 out++; 982 walk++; 983 } 984 985 buffer_string_set_length(dest, out - start); 986 } 987 988 int light_isdigit(int c) { 989 return (c >= '0' && c <= '9'); 990 } 991 992 int light_isxdigit(int c) { 993 if (light_isdigit(c)) return 1; 994 995 c |= 32; 996 return (c >= 'a' && c <= 'f'); 997 } 998 999 int light_isalpha(int c) { 1000 c |= 32; 1001 return (c >= 'a' && c <= 'z'); 1002 } 1003 1004 int light_isalnum(int c) { 1005 return light_isdigit(c) || light_isalpha(c); 1006 } 1007 1008 void buffer_to_lower(buffer *b) { 1009 size_t i; 1010 1011 for (i = 0; i < b->used; ++i) { 1012 char c = b->ptr[i]; 1013 if (c >= 'A' && c <= 'Z') b->ptr[i] |= 0x20; 1014 } 1015 } 1016 1017 1018 void buffer_to_upper(buffer *b) { 1019 size_t i; 1020 1021 for (i = 0; i < b->used; ++i) { 1022 char c = b->ptr[i]; 1023 if (c >= 'A' && c <= 'Z') b->ptr[i] &= ~0x20; 1024 } 1025 } 1026 1027 #ifdef HAVE_LIBUNWIND 1028 # define UNW_LOCAL_ONLY 1029 # include <libunwind.h> 1030 1031 void print_backtrace(FILE *file) { 1032 unw_cursor_t cursor; 1033 unw_context_t context; 1034 int ret; 1035 unsigned int frame = 0; 1036 1037 if (0 != (ret = unw_getcontext(&context))) goto error; 1038 if (0 != (ret = unw_init_local(&cursor, &context))) goto error; 1039 1040 fprintf(file, "Backtrace:\n"); 1041 1042 while (0 < (ret = unw_step(&cursor))) { 1043 unw_word_t proc_ip = 0; 1044 unw_proc_info_t procinfo; 1045 char procname[256]; 1046 unw_word_t proc_offset = 0; 1047 1048 if (0 != (ret = unw_get_reg(&cursor, UNW_REG_IP, &proc_ip))) goto error; 1049 1050 if (0 == proc_ip) { 1051 /* without an IP the other functions are useless; unw_get_proc_name would return UNW_EUNSPEC */ 1052 ++frame; 1053 fprintf(file, "%u: (nil)\n", frame); 1054 continue; 1055 } 1056 1057 if (0 != (ret = unw_get_proc_info(&cursor, &procinfo))) goto error; 1058 1059 if (0 != (ret = unw_get_proc_name(&cursor, procname, sizeof(procname), &proc_offset))) { 1060 switch (-ret) { 1061 case UNW_ENOMEM: 1062 memset(procname + sizeof(procname) - 4, '.', 3); 1063 procname[sizeof(procname) - 1] = '\0'; 1064 break; 1065 case UNW_ENOINFO: 1066 procname[0] = '?'; 1067 procname[1] = '\0'; 1068 proc_offset = 0; 1069 break; 1070 default: 1071 snprintf(procname, sizeof(procname), "?? (unw_get_proc_name error %d)", -ret); 1072 break; 1073 } 1074 } 1075 1076 ++frame; 1077 fprintf(file, "%u: %s (+0x%x) [%p]\n", 1078 frame, 1079 procname, 1080 (unsigned int) proc_offset, 1081 (void*)(uintptr_t)proc_ip); 1082 } 1083 1084 if (0 != ret) goto error; 1085 1086 return; 1087 1088 error: 1089 fprintf(file, "Error while generating backtrace: unwind error %i\n", (int) -ret); 1090 } 1091 #else 1092 void print_backtrace(FILE *file) { 1093 UNUSED(file); 1094 } 1095 #endif 1096 1097 void log_failed_assert(const char *filename, unsigned int line, const char *msg) { 1098 /* can't use buffer here; could lead to recursive assertions */ 1099 fprintf(stderr, "%s.%d: %s\n", filename, line, msg); 1100 print_backtrace(stderr); 1101 fflush(stderr); 1102 abort(); 1103 } 1104