xref: /lighttpd1.4/src/buffer.c (revision bfaa4826)
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