Annotation of embedaddon/lighttpd/src/buffer.c, revision 1.1.1.3

1.1.1.3 ! misho       1: #include "first.h"
        !             2: 
1.1       misho       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));
1.1.1.2   misho      23:        force_assert(b);
1.1       misho      24: 
                     25:        b->ptr = NULL;
                     26:        b->size = 0;
                     27:        b->used = 0;
                     28: 
                     29:        return b;
                     30: }
                     31: 
1.1.1.3 ! misho      32: buffer *buffer_init_buffer(const buffer *src) {
1.1       misho      33:        buffer *b = buffer_init();
1.1.1.3 ! misho      34:        buffer_copy_buffer(b, src);
1.1       misho      35:        return b;
                     36: }
                     37: 
1.1.1.3 ! misho      38: buffer *buffer_init_string(const char *str) {
        !            39:        buffer *b = buffer_init();
        !            40:        buffer_copy_string(b, str);
        !            41:        return b;
        !            42: }
1.1       misho      43: 
                     44: void buffer_free(buffer *b) {
1.1.1.3 ! misho      45:        if (NULL == b) return;
1.1       misho      46: 
                     47:        free(b->ptr);
                     48:        free(b);
                     49: }
                     50: 
                     51: void buffer_reset(buffer *b) {
1.1.1.3 ! misho      52:        if (NULL == b) return;
1.1       misho      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;
1.1.1.3 ! misho      59:        } else if (b->size > 0) {
1.1       misho      60:                b->ptr[0] = '\0';
                     61:        }
                     62: 
                     63:        b->used = 0;
                     64: }
                     65: 
1.1.1.3 ! misho      66: void buffer_move(buffer *b, buffer *src) {
        !            67:        buffer tmp;
1.1       misho      68: 
1.1.1.3 ! misho      69:        if (NULL == b) {
        !            70:                buffer_reset(src);
        !            71:                return;
        !            72:        }
        !            73:        buffer_reset(b);
        !            74:        if (NULL == src) return;
1.1       misho      75: 
1.1.1.3 ! misho      76:        tmp = *src; *src = *b; *b = tmp;
        !            77: }
1.1       misho      78: 
1.1.1.3 ! misho      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: }
1.1       misho      86: 
1.1.1.3 ! misho      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;
1.1       misho      91: 
1.1.1.3 ! misho      92:        if (size <= b->size) return;
1.1       misho      93: 
1.1.1.3 ! misho      94:        if (NULL != b->ptr) free(b->ptr);
1.1       misho      95: 
                     96:        b->used = 0;
1.1.1.3 ! misho      97:        b->size = buffer_align_size(size);
        !            98:        b->ptr = malloc(b->size);
        !            99: 
        !           100:        force_assert(NULL != b->ptr);
1.1       misho     101: }
                    102: 
1.1.1.3 ! misho     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;
1.1       misho     107: 
1.1.1.3 ! misho     108:        if (size <= b->size) return;
1.1       misho     109: 
1.1.1.3 ! misho     110:        b->size = buffer_align_size(size);
        !           111:        b->ptr = realloc(b->ptr, b->size);
1.1       misho     112: 
1.1.1.3 ! misho     113:        force_assert(NULL != b->ptr);
        !           114: }
1.1       misho     115: 
                    116: 
1.1.1.3 ! misho     117: char* buffer_string_prepare_copy(buffer *b, size_t size) {
        !           118:        force_assert(NULL != b);
        !           119:        force_assert(size + 1 > size);
1.1       misho     120: 
1.1.1.3 ! misho     121:        buffer_alloc(b, size + 1);
1.1       misho     122: 
1.1.1.3 ! misho     123:        b->used = 1;
        !           124:        b->ptr[0] = '\0';
1.1       misho     125: 
1.1.1.3 ! misho     126:        return b->ptr;
        !           127: }
1.1       misho     128: 
1.1.1.3 ! misho     129: char* buffer_string_prepare_append(buffer *b, size_t size) {
        !           130:        force_assert(NULL !=  b);
1.1       misho     131: 
1.1.1.3 ! misho     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;
1.1       misho     136: 
1.1.1.3 ! misho     137:                /* not empty, b->used already includes a terminating 0 */
        !           138:                force_assert(req_size >= b->used);
1.1       misho     139: 
1.1.1.3 ! misho     140:                /* check for overflow: unsigned overflow is defined to wrap around */
        !           141:                force_assert(req_size >= b->used);
1.1       misho     142: 
1.1.1.3 ! misho     143:                buffer_realloc(b, req_size);
1.1       misho     144: 
1.1.1.3 ! misho     145:                return b->ptr + b->used - 1;
        !           146:        }
1.1       misho     147: }
                    148: 
1.1.1.3 ! misho     149: void buffer_string_set_length(buffer *b, size_t len) {
        !           150:        force_assert(NULL != b);
        !           151:        force_assert(len + 1 > len);
1.1       misho     152: 
1.1.1.3 ! misho     153:        buffer_realloc(b, len + 1);
        !           154: 
        !           155:        b->used = len + 1;
        !           156:        b->ptr[len] = '\0';
1.1       misho     157: }
                    158: 
1.1.1.3 ! misho     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;
1.1       misho     165: 
1.1.1.3 ! misho     166:        if (size > 0) {
        !           167:                /* check for overflow: unsigned overflow is defined to wrap around */
        !           168:                force_assert(b->used + size > b->used);
1.1       misho     169: 
1.1.1.3 ! misho     170:                force_assert(b->used + size <= b->size);
        !           171:                b->used += size;
        !           172:        }
1.1       misho     173: 
1.1.1.3 ! misho     174:        b->ptr[b->used - 1] = '\0';
        !           175: }
1.1       misho     176: 
1.1.1.3 ! misho     177: void buffer_copy_string(buffer *b, const char *s) {
        !           178:        buffer_copy_string_len(b, s, NULL != s ? strlen(s) : 0);
1.1       misho     179: }
                    180: 
1.1.1.3 ! misho     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);
1.1       misho     184: 
1.1.1.3 ! misho     185:        buffer_string_prepare_copy(b, s_len);
1.1       misho     186: 
1.1.1.3 ! misho     187:        if (0 != s_len) memcpy(b->ptr, s, s_len);
        !           188: 
        !           189:        buffer_commit(b, s_len);
        !           190: }
1.1       misho     191: 
1.1.1.3 ! misho     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));
1.1       misho     198:        }
1.1.1.3 ! misho     199: }
1.1       misho     200: 
1.1.1.3 ! misho     201: void buffer_append_string(buffer *b, const char *s) {
        !           202:        buffer_append_string_len(b, s, NULL != s ? strlen(s) : 0);
1.1       misho     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: 
1.1.1.3 ! misho     216: void buffer_append_string_len(buffer *b, const char *s, size_t s_len) {
        !           217:        char *target_buf;
1.1       misho     218: 
1.1.1.3 ! misho     219:        force_assert(NULL != b);
        !           220:        force_assert(NULL != s || s_len == 0);
1.1       misho     221: 
1.1.1.3 ! misho     222:        target_buf = buffer_string_prepare_append(b, s_len);
1.1       misho     223: 
1.1.1.3 ! misho     224:        if (0 == s_len) return; /* nothing to append */
1.1       misho     225: 
1.1.1.3 ! misho     226:        memcpy(target_buf, s, s_len);
1.1       misho     227: 
1.1.1.3 ! misho     228:        buffer_commit(b, s_len);
1.1       misho     229: }
                    230: 
1.1.1.3 ! misho     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:        }
1.1       misho     237: }
                    238: 
1.1.1.3 ! misho     239: void buffer_append_uint_hex(buffer *b, uintmax_t value) {
1.1       misho     240:        char *buf;
                    241:        int shift = 0;
                    242: 
1.1.1.3 ! misho     243:        {
        !           244:                uintmax_t copy = value;
        !           245:                do {
        !           246:                        copy >>= 8;
        !           247:                        shift += 2; /* counting nibbles (4 bits) */
        !           248:                } while (0 != copy);
        !           249:        }
1.1       misho     250: 
1.1.1.3 ! misho     251:        buf = buffer_string_prepare_append(b, shift);
        !           252:        buffer_commit(b, shift); /* will fill below */
        !           253: 
        !           254:        shift <<= 2; /* count bits now */
1.1       misho     255:        while (shift > 0) {
                    256:                shift -= 4;
                    257:                *(buf++) = hex_chars[(value >> shift) & 0x0F];
                    258:        }
                    259: }
                    260: 
1.1.1.3 ! misho     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: }
1.1       misho     271: 
1.1.1.3 ! misho     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) = '-';
1.1       misho     280: 
1.1.1.3 ! misho     281:        return cur;
1.1       misho     282: }
                    283: 
1.1.1.3 ! misho     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;
1.1       misho     288: 
1.1.1.3 ! misho     289:        force_assert(NULL != b);
1.1       misho     290: 
1.1.1.3 ! misho     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);
1.1       misho     295: }
                    296: 
1.1.1.3 ! misho     297: void buffer_copy_int(buffer *b, intmax_t val) {
        !           298:        force_assert(NULL != b);
1.1       misho     299: 
                    300:        b->used = 0;
1.1.1.3 ! misho     301:        buffer_append_int(b, val);
1.1       misho     302: }
                    303: 
1.1.1.3 ! misho     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);
1.1       misho     309: 
1.1.1.3 ! misho     310:        if (NULL == format || '\0' == format[0]) {
        !           311:                /* empty format */
        !           312:                buffer_string_prepare_append(b, 0);
        !           313:                return;
        !           314:        }
1.1       misho     315: 
1.1.1.3 ! misho     316:        buf = buffer_string_prepare_append(b, 255);
        !           317:        r = strftime(buf, buffer_string_space(b), format, tm);
1.1       misho     318: 
1.1.1.3 ! misho     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);
1.1       misho     327:        }
                    328: 
1.1.1.3 ! misho     329:        if (r >= buffer_string_space(b)) r = 0;
1.1       misho     330: 
1.1.1.3 ! misho     331:        buffer_commit(b, r);
        !           332: }
1.1       misho     333: 
                    334: 
1.1.1.3 ! misho     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);
1.1       misho     346: }
                    347: 
1.1.1.3 ! misho     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';
1.1       misho     353: 
1.1.1.3 ! misho     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);
1.1       misho     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) {
1.1.1.3 ! misho     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;
1.1       misho     374:        }
1.1.1.3 ! misho     375:        if (value > 15) value = 0xff;
1.1       misho     376: 
1.1.1.3 ! misho     377:        return value;
1.1       misho     378: }
                    379: 
                    380: char * buffer_search_string_len(buffer *b, const char *needle, size_t len) {
                    381:        size_t i;
1.1.1.3 ! misho     382:        force_assert(NULL != b);
        !           383:        force_assert(0 != len && NULL != needle); /* empty needles not allowed */
1.1       misho     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: 
1.1.1.3 ! misho     396: int buffer_is_empty(const buffer *b) {
        !           397:        return NULL == b || 0 == b->used;
1.1       misho     398: }
                    399: 
1.1.1.3 ! misho     400: int buffer_string_is_empty(const buffer *b) {
        !           401:        return 0 == buffer_string_length(b);
1.1       misho     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: 
1.1.1.3 ! misho     411: int buffer_is_equal(const buffer *a, const buffer *b) {
        !           412:        force_assert(NULL != a && NULL != b);
        !           413: 
1.1       misho     414:        if (a->used != b->used) return 0;
                    415:        if (a->used == 0) return 1;
                    416: 
1.1.1.3 ! misho     417:        return (0 == memcmp(a->ptr, b->ptr, a->used));
1.1       misho     418: }
                    419: 
1.1.1.3 ! misho     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);
1.1       misho     423: 
1.1.1.3 ! misho     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;
1.1       misho     427: 
1.1.1.3 ! misho     428:        return 1;
1.1       misho     429: }
                    430: 
                    431: /* buffer_is_equal_caseless_string(b, CONST_STR_LEN("value")) */
1.1.1.3 ! misho     432: int buffer_is_equal_caseless_string(const buffer *a, const char *s, size_t b_len) {
        !           433:        force_assert(NULL != a);
1.1       misho     434:        if (a->used != b_len + 1) return 0;
1.1.1.3 ! misho     435:        force_assert('\0' == a->ptr[a->used - 1]);
1.1       misho     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;
1.1.1.3 ! misho     453:                return ((int)ca) - ((int)cb);
1.1       misho     454:        }
                    455:        if (a_len == b_len) return 0;
1.1.1.3 ! misho     456:        return a_len < b_len ? -1 : 1;
1.1       misho     457: }
                    458: 
1.1.1.3 ! misho     459: int buffer_is_equal_right_len(const buffer *b1, const buffer *b2, size_t len) {
        !           460:        /* no len -> equal */
1.1       misho     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 */
1.1.1.2   misho     467:        if (b1->used - 1 < len || b2->used - 1 < len) return 0;
1.1       misho     468: 
1.1.1.3 ! misho     469:        return 0 == memcmp(b1->ptr + b1->used - 1 - len, b2->ptr + b2->used - 1 - len, len);
1.1       misho     470: }
                    471: 
1.1.1.3 ! misho     472: void li_tohex(char *buf, size_t buf_len, const char *s, size_t s_len) {
1.1       misho     473:        size_t i;
1.1.1.3 ! misho     474:        force_assert(2 * s_len > s_len);
        !           475:        force_assert(2 * s_len < buf_len);
1.1       misho     476: 
1.1.1.3 ! misho     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];
1.1       misho     480:        }
1.1.1.3 ! misho     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);
1.1       misho     487: 
1.1.1.3 ! misho     488:        buffer_string_set_length(b, 2 * in_len);
        !           489:        li_tohex(b->ptr, buffer_string_length(b)+1, in, in_len);
1.1       misho     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 ` */
1.1.1.3 ! misho     504:        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  70 -  7F { | } DEL */
1.1       misho     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 ` */
1.1.1.3 ! misho     527:        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  70 -  7F { | } DEL */
1.1       misho     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 */
1.1.1.3 ! misho     544:        0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,  /*  20 -  2F " & ' */
1.1       misho     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 */
1.1.1.3 ! misho     548:        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
1.1       misho     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 */
1.1.1.3 ! misho     566:        0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,  /*  20 -  2F " & ' */
1.1       misho     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 */
1.1.1.3 ! misho     570:        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
1.1       misho     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: 
1.1.1.3 ! misho     628: void buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding) {
1.1       misho     629:        unsigned char *ds, *d;
                    630:        size_t d_len, ndx;
                    631:        const char *map = NULL;
                    632: 
1.1.1.3 ! misho     633:        force_assert(NULL != b);
        !           634:        force_assert(NULL != s || 0 == s_len);
1.1       misho     635: 
1.1.1.3 ! misho     636:        if (0 == s_len) return;
1.1       misho     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: 
1.1.1.3 ! misho     659:        force_assert(NULL != map);
1.1       misho     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 {
1.1.1.3 ! misho     679:                        d_len++;
1.1       misho     680:                }
                    681:        }
                    682: 
1.1.1.3 ! misho     683:        d = (unsigned char*) buffer_string_prepare_append(b, d_len);
        !           684:        buffer_commit(b, d_len); /* fill below */
        !           685:        force_assert('\0' == *d);
1.1       misho     686: 
1.1.1.3 ! misho     687:        for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
1.1       misho     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:        }
1.1.1.3 ! misho     718: }
1.1       misho     719: 
1.1.1.3 ! misho     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:        }
1.1       misho     747: 
1.1.1.3 ! misho     748:        d = (unsigned char*) buffer_string_prepare_append(b, d_len);
        !           749:        buffer_commit(b, d_len); /* fill below */
        !           750:        force_assert('\0' == *d);
1.1       misho     751: 
1.1.1.3 ! misho     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:        }
1.1       misho     776: }
                    777: 
                    778: 
1.1.1.3 ! misho     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: 
1.1       misho     809: /* decodes url-special-chars inplace.
                    810:  * replaces non-printable characters with '_'
                    811:  */
                    812: 
1.1.1.3 ! misho     813: static void buffer_urldecode_internal(buffer *url, int is_query) {
1.1       misho     814:        unsigned char high, low;
1.1.1.3 ! misho     815:        char *src;
1.1       misho     816:        char *dst;
                    817: 
1.1.1.3 ! misho     818:        force_assert(NULL != url);
        !           819:        if (buffer_string_is_empty(url)) return;
        !           820: 
        !           821:        force_assert('\0' == url->ptr[url->used-1]);
1.1       misho     822: 
1.1.1.3 ! misho     823:        src = (char*) url->ptr;
1.1       misho     824: 
1.1.1.3 ! misho     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) {
1.1       misho     833:                if (is_query && *src == '+') {
                    834:                        *dst = ' ';
                    835:                } else if (*src == '%') {
                    836:                        *dst = '%';
                    837: 
                    838:                        high = hex2int(*(src + 1));
1.1.1.3 ! misho     839:                        if (0xFF != high) {
1.1       misho     840:                                low = hex2int(*(src + 2));
1.1.1.3 ! misho     841:                                if (0xFF != low) {
1.1       misho     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: 
1.1.1.3 ! misho     863: void buffer_urldecode_path(buffer *url) {
        !           864:        buffer_urldecode_internal(url, 0);
1.1       misho     865: }
                    866: 
1.1.1.3 ! misho     867: void buffer_urldecode_query(buffer *url) {
        !           868:        buffer_urldecode_internal(url, 1);
1.1       misho     869: }
                    870: 
1.1.1.3 ! misho     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:  *   "/"
1.1       misho     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: 
1.1.1.3 ! misho     890: void buffer_path_simplify(buffer *dest, buffer *src)
1.1       misho     891: {
1.1.1.3 ! misho     892:        /* current character, the one before, and the one before that from input */
        !           893:        char c, pre1, pre2;
1.1       misho     894:        char *start, *slash, *walk, *out;
                    895: 
1.1.1.3 ! misho     896:        force_assert(NULL != dest && NULL != src);
1.1       misho     897: 
1.1.1.3 ! misho     898:        if (buffer_string_is_empty(src)) {
        !           899:                buffer_string_prepare_copy(dest, 0);
        !           900:                return;
        !           901:        }
1.1       misho     902: 
1.1.1.3 ! misho     903:        force_assert('\0' == src->ptr[src->used-1]);
1.1       misho     904: 
1.1.1.3 ! misho     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:        }
1.1       misho     911: 
                    912: #if defined(__WIN32) || defined(__CYGWIN__)
1.1.1.3 ! misho     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:                }
1.1       misho     919:        }
                    920: #endif
                    921: 
1.1.1.3 ! misho     922:        walk  = src->ptr;
        !           923:        start = dest->ptr;
        !           924:        out   = dest->ptr;
        !           925:        slash = dest->ptr;
        !           926: 
        !           927:        /* skip leading spaces */
1.1       misho     928:        while (*walk == ' ') {
                    929:                walk++;
                    930:        }
                    931: 
1.1.1.3 ! misho     932:        pre2 = pre1 = 0;
        !           933:        c = *(walk++);
        !           934:        /* prefix with '/' if not already present */
        !           935:        if (c != '/') {
        !           936:                pre1 = '/';
1.1       misho     937:                *(out++) = '/';
                    938:        }
                    939: 
1.1.1.3 ! misho     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) */
1.1       misho     955: 
                    956:                if (c == '/' || c == '\0') {
1.1.1.3 ! misho     957:                        const size_t toklen = out - slash;
        !           958:                        if (toklen == 3 && pre2 == '.' && pre1 == '.') {
        !           959:                                /* "/../" or ("/.." at end of string) */
1.1       misho     960:                                out = slash;
1.1.1.3 ! misho     961:                                /* if there is something before "/..", there is at least one
        !           962:                                 * component, which needs to be removed */
1.1       misho     963:                                if (out > start) {
                    964:                                        out--;
1.1.1.3 ! misho     965:                                        while (out > start && *out != '/') out--;
1.1       misho     966:                                }
                    967: 
1.1.1.3 ! misho     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) */
1.1       misho     973:                                out = slash;
1.1.1.3 ! misho     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 */
1.1       misho     977:                        }
                    978: 
                    979:                        slash = out;
                    980:                }
                    981:        }
                    982: 
1.1.1.3 ! misho     983:        buffer_string_set_length(dest, out - start);
1.1       misho     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: 
1.1.1.3 ! misho    1006: void buffer_to_lower(buffer *b) {
        !          1007:        size_t i;
1.1       misho    1008: 
1.1.1.3 ! misho    1009:        for (i = 0; i < b->used; ++i) {
        !          1010:                char c = b->ptr[i];
        !          1011:                if (c >= 'A' && c <= 'Z') b->ptr[i] |= 0x20;
1.1       misho    1012:        }
1.1.1.3 ! misho    1013: }
        !          1014: 
1.1       misho    1015: 
1.1.1.3 ! misho    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:        }
1.1       misho    1023: }
                   1024: 
1.1.1.3 ! misho    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;
1.1       misho    1034: 
1.1.1.3 ! misho    1035:        if (0 != (ret = unw_getcontext(&context))) goto error;
        !          1036:        if (0 != (ret = unw_init_local(&cursor, &context))) goto error;
1.1       misho    1037: 
1.1.1.3 ! misho    1038:        fprintf(file, "Backtrace:\n");
1.1       misho    1039: 
1.1.1.3 ! misho    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;
1.1       misho    1053:                }
1.1.1.3 ! misho    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);
1.1       misho    1080:        }
                   1081: 
1.1.1.3 ! misho    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);
1.1       misho    1088: }
1.1.1.3 ! misho    1089: #else
        !          1090: void print_backtrace(FILE *file) {
        !          1091:        UNUSED(file);
        !          1092: }
        !          1093: #endif
1.1.1.2   misho    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 */
1.1.1.3 ! misho    1097:        fprintf(stderr, "%s.%u: %s\n", filename, line, msg);
        !          1098:        print_backtrace(stderr);
1.1.1.2   misho    1099:        fflush(stderr);
                   1100:        abort();
                   1101: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>