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