xref: /lighttpd1.4/src/buffer.c (revision 673923da)
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, 1, 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, 1, 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_copy_string_encoded_cgi_varnames(buffer *b, const char *s, size_t s_len, int is_http_header) {
735 	size_t i, j;
736 
737 	force_assert(NULL != b);
738 	force_assert(NULL != s || 0 == s_len);
739 
740 	buffer_reset(b);
741 
742 	if (is_http_header && NULL != s && 0 != strcasecmp(s, "CONTENT-TYPE")) {
743 		buffer_string_prepare_append(b, s_len + 5);
744 		buffer_copy_string_len(b, CONST_STR_LEN("HTTP_"));
745 	} else {
746 		buffer_string_prepare_append(b, s_len);
747 	}
748 
749 	j = buffer_string_length(b);
750 	for (i = 0; i < s_len; ++i) {
751 		unsigned char cr = s[i];
752 		if (light_isalpha(cr)) {
753 			/* upper-case */
754 			cr &= ~32;
755 		} else if (!light_isdigit(cr)) {
756 			cr = '_';
757 		}
758 		b->ptr[j++] = cr;
759 	}
760 	b->used = j;
761 	b->ptr[b->used++] = '\0';
762 }
763 
764 /* decodes url-special-chars inplace.
765  * replaces non-printable characters with '_'
766  */
767 
768 static void buffer_urldecode_internal(buffer *url, int is_query) {
769 	unsigned char high, low;
770 	char *src;
771 	char *dst;
772 
773 	force_assert(NULL != url);
774 	if (buffer_string_is_empty(url)) return;
775 
776 	force_assert('\0' == url->ptr[url->used-1]);
777 
778 	src = (char*) url->ptr;
779 
780 	while ('\0' != *src) {
781 		if ('%' == *src) break;
782 		if (is_query && '+' == *src) *src = ' ';
783 		src++;
784 	}
785 	dst = src;
786 
787 	while ('\0' != *src) {
788 		if (is_query && *src == '+') {
789 			*dst = ' ';
790 		} else if (*src == '%') {
791 			*dst = '%';
792 
793 			high = hex2int(*(src + 1));
794 			if (0xFF != high) {
795 				low = hex2int(*(src + 2));
796 				if (0xFF != low) {
797 					high = (high << 4) | low;
798 
799 					/* map control-characters out */
800 					if (high < 32 || high == 127) high = '_';
801 
802 					*dst = high;
803 					src += 2;
804 				}
805 			}
806 		} else {
807 			*dst = *src;
808 		}
809 
810 		dst++;
811 		src++;
812 	}
813 
814 	*dst = '\0';
815 	url->used = (dst - url->ptr) + 1;
816 }
817 
818 void buffer_urldecode_path(buffer *url) {
819 	buffer_urldecode_internal(url, 0);
820 }
821 
822 void buffer_urldecode_query(buffer *url) {
823 	buffer_urldecode_internal(url, 1);
824 }
825 
826 /* Remove "/../", "//", "/./" parts from path,
827  * strips leading spaces,
828  * prepends "/" if not present already
829  *
830  * /blah/..         gets  /
831  * /blah/../foo     gets  /foo
832  * /abc/./xyz       gets  /abc/xyz
833  * /abc//xyz        gets  /abc/xyz
834  *
835  * NOTE: src and dest can point to the same buffer, in which case,
836  *       the operation is performed in-place.
837  */
838 
839 void buffer_path_simplify(buffer *dest, buffer *src)
840 {
841 	int toklen;
842 	char c, pre1;
843 	char *start, *slash, *walk, *out;
844 	unsigned short pre;
845 
846 	force_assert(NULL != dest && NULL != src);
847 
848 	if (buffer_string_is_empty(src)) {
849 		buffer_string_prepare_copy(dest, 0);
850 		return;
851 	}
852 
853 	force_assert('\0' == src->ptr[src->used-1]);
854 
855 	/* might need one character more for the '/' prefix */
856 	if (src == dest) {
857 		buffer_string_prepare_append(dest, 1);
858 	} else {
859 		buffer_string_prepare_copy(dest, buffer_string_length(src) + 1);
860 	}
861 
862 #if defined(__WIN32) || defined(__CYGWIN__)
863 	/* cygwin is treating \ and / the same, so we have to that too */
864 	{
865 		char *p;
866 		for (p = src->ptr; *p; p++) {
867 			if (*p == '\\') *p = '/';
868 		}
869 	}
870 #endif
871 
872 	walk  = src->ptr;
873 	start = dest->ptr;
874 	out   = dest->ptr;
875 	slash = dest->ptr;
876 
877 
878 	while (*walk == ' ') {
879 		walk++;
880 	}
881 
882 	pre1 = *(walk++);
883 	c    = *(walk++);
884 	pre  = pre1;
885 	if (pre1 != '/') {
886 		pre = ('/' << 8) | pre1;
887 		*(out++) = '/';
888 	}
889 	*(out++) = pre1;
890 
891 	if (pre1 == '\0') {
892 		dest->used = (out - start) + 1;
893 		return;
894 	}
895 
896 	for (;;) {
897 		if (c == '/' || c == '\0') {
898 			toklen = out - slash;
899 			if (toklen == 3 && pre == (('.' << 8) | '.')) {
900 				out = slash;
901 				if (out > start) {
902 					out--;
903 					while (out > start && *out != '/') out--;
904 				}
905 
906 				if (c == '\0') out++;
907 			} else if (toklen == 1 || pre == (('/' << 8) | '.')) {
908 				out = slash;
909 				if (c == '\0') out++;
910 			}
911 
912 			slash = out;
913 		}
914 
915 		if (c == '\0') break;
916 
917 		pre1 = c;
918 		pre  = (pre << 8) | pre1;
919 		c    = *walk;
920 		*out = pre1;
921 
922 		out++;
923 		walk++;
924 	}
925 
926 	buffer_string_set_length(dest, out - start);
927 }
928 
929 int light_isdigit(int c) {
930 	return (c >= '0' && c <= '9');
931 }
932 
933 int light_isxdigit(int c) {
934 	if (light_isdigit(c)) return 1;
935 
936 	c |= 32;
937 	return (c >= 'a' && c <= 'f');
938 }
939 
940 int light_isalpha(int c) {
941 	c |= 32;
942 	return (c >= 'a' && c <= 'z');
943 }
944 
945 int light_isalnum(int c) {
946 	return light_isdigit(c) || light_isalpha(c);
947 }
948 
949 void buffer_to_lower(buffer *b) {
950 	size_t i;
951 
952 	for (i = 0; i < b->used; ++i) {
953 		char c = b->ptr[i];
954 		if (c >= 'A' && c <= 'Z') b->ptr[i] |= 0x20;
955 	}
956 }
957 
958 
959 void buffer_to_upper(buffer *b) {
960 	size_t i;
961 
962 	for (i = 0; i < b->used; ++i) {
963 		char c = b->ptr[i];
964 		if (c >= 'A' && c <= 'Z') b->ptr[i] &= ~0x20;
965 	}
966 }
967 
968 #ifdef HAVE_LIBUNWIND
969 # define UNW_LOCAL_ONLY
970 # include <libunwind.h>
971 
972 void print_backtrace(FILE *file) {
973 	unw_cursor_t cursor;
974 	unw_context_t context;
975 	int ret;
976 	unsigned int frame = 0;
977 
978 	if (0 != (ret = unw_getcontext(&context))) goto error;
979 	if (0 != (ret = unw_init_local(&cursor, &context))) goto error;
980 
981 	fprintf(file, "Backtrace:\n");
982 
983 	while (0 < (ret = unw_step(&cursor))) {
984 		unw_word_t proc_ip = 0;
985 		unw_proc_info_t procinfo;
986 		char procname[256];
987 		unw_word_t proc_offset = 0;
988 
989 		if (0 != (ret = unw_get_reg(&cursor, UNW_REG_IP, &proc_ip))) goto error;
990 
991 		if (0 == proc_ip) {
992 			/* without an IP the other functions are useless; unw_get_proc_name would return UNW_EUNSPEC */
993 			++frame;
994 			fprintf(file, "%u: (nil)\n", frame);
995 			continue;
996 		}
997 
998 		if (0 != (ret = unw_get_proc_info(&cursor, &procinfo))) goto error;
999 
1000 		if (0 != (ret = unw_get_proc_name(&cursor, procname, sizeof(procname), &proc_offset))) {
1001 			switch (-ret) {
1002 			case UNW_ENOMEM:
1003 				memset(procname + sizeof(procname) - 4, '.', 3);
1004 				procname[sizeof(procname) - 1] = '\0';
1005 				break;
1006 			case UNW_ENOINFO:
1007 				procname[0] = '?';
1008 				procname[1] = '\0';
1009 				proc_offset = 0;
1010 				break;
1011 			default:
1012 				snprintf(procname, sizeof(procname), "?? (unw_get_proc_name error %d)", -ret);
1013 				break;
1014 			}
1015 		}
1016 
1017 		++frame;
1018 		fprintf(file, "%u: %s (+0x%x) [%p]\n",
1019 			frame,
1020 			procname,
1021 			(unsigned int) proc_offset,
1022 			(void*)(uintptr_t)proc_ip);
1023 	}
1024 
1025 	if (0 != ret) goto error;
1026 
1027 	return;
1028 
1029 error:
1030 	fprintf(file, "Error while generating backtrace: unwind error %i\n", (int) -ret);
1031 }
1032 #else
1033 void print_backtrace(FILE *file) {
1034 	UNUSED(file);
1035 }
1036 #endif
1037 
1038 void log_failed_assert(const char *filename, unsigned int line, const char *msg) {
1039 	/* can't use buffer here; could lead to recursive assertions */
1040 	fprintf(stderr, "%s.%d: %s\n", filename, line, msg);
1041 	print_backtrace(stderr);
1042 	fflush(stderr);
1043 	abort();
1044 }
1045