/* * LibHTP (http://www.libhtp.org) * Copyright 2009,2010 Ivan Ristic * * LibHTP is an open source product, released under terms of the General Public Licence * version 2 (GPLv2). Please refer to the file LICENSE, which contains the complete text * of the license. * * In addition, there is a special exception that allows LibHTP to be freely * used with any OSI-approved open source licence. Please refer to the file * LIBHTP_LICENSING_EXCEPTION for the full text of the exception. * */ #include "bstr.h" #include /** * Allocate a zero-length bstring, but reserving space for at least len bytes. * * @param len * @return New string */ bstr *bstr_alloc(size_t len) { unsigned char *s = malloc(sizeof (bstr_t) + len); if (s == NULL) return NULL; bstr_t *b = (bstr_t *) s; b->len = 0; b->size = len; b->ptr = NULL; return (bstr *) s; } /** * Deallocate a bstring. Allows a NULL bstring on input. * * @param b */ void bstr_free(bstr *b) { if (b == NULL) return; free(b); } /** * Append source bstring to destination bstring, growing * destination if necessary. * * @param destination * @param source * @return destination, at a potentially different memory location */ bstr *bstr_add_str(bstr *destination, bstr *source) { return bstr_add_mem(destination, bstr_ptr(source), bstr_len(source)); } /** * Append a NUL-terminated source to destination, growing * destination if necessary. * * @param destination * @param source * @return destination, at a potentially different memory location */ bstr *bstr_add_cstr(bstr *destination, char *source) { return bstr_add_mem(destination, source, strlen(source)); } /** * Append a memory region to destination, growing destination * if necessary. * * @param destination * @param data * @param len * @return destination, at a potentially different memory location */ bstr *bstr_add_mem(bstr *destination, char *data, size_t len) { if (bstr_size(destination) < bstr_len(destination) + len) { destination = bstr_expand(destination, bstr_len(destination) + len); if (destination == NULL) return NULL; } bstr_t *b = (bstr_t *) destination; memcpy(bstr_ptr(destination) + b->len, data, len); b->len = b->len + len; return destination; } /** * Append source bstring to destination bstring, growing * destination if necessary. * * @param destination * @param source * @return destination, at a potentially different memory location */ bstr *bstr_add_str_noex(bstr *destination, bstr *source) { return bstr_add_mem_noex(destination, bstr_ptr(source), bstr_len(source)); } /** * Append a NUL-terminated source to destination, growing * destination if necessary. * * @param destination * @param source * @return destination, at a potentially different memory location */ bstr *bstr_add_cstr_noex(bstr *destination, char *source) { return bstr_add_mem_noex(destination, source, strlen(source)); } /** * Append a memory region to destination, growing destination * if necessary. * * @param destination * @param data * @param len * @return destination, at a potentially different memory location */ bstr *bstr_add_mem_noex(bstr *destination, char *data, size_t len) { size_t copylen = len; if (bstr_size(destination) < bstr_len(destination) + copylen) { copylen = bstr_size(destination) - bstr_len(destination); if (copylen <= 0) return destination; } bstr_t *b = (bstr_t *) destination; memcpy(bstr_ptr(destination) + b->len, data, copylen); b->len = b->len + copylen; return destination; } /** * Expand a string to support at least newsize bytes. The input bstring * is not changed if it is big enough to accommodate the desired size. If * the input bstring is smaller, however, it is expanded. The pointer to * the bstring may change. If the expansion fails, the original bstring * is left untouched (it is not freed). * * @param s * @param newsize * @return new bstring, or NULL if memory allocation failed */ bstr *bstr_expand(bstr *s, size_t newsize) { if (((bstr_t *) s)->ptr != NULL) { void * newblock = realloc(((bstr_t *) s)->ptr, newsize); if (newblock == NULL) { return NULL; } else { ((bstr_t *) s)->ptr = newblock; } } else { void *newblock = realloc(s, sizeof (bstr_t) + newsize); if (newblock == NULL) { return NULL; } else { s = newblock; } } ((bstr_t *) s)->size = newsize; return s; } /** * Create a new bstring by copying the provided NUL-terminated string. * * @param data * @return new bstring */ bstr *bstr_cstrdup(char *data) { return bstr_memdup(data, strlen(data)); } /** * Create a new bstring by copying the provided memory region. * * @param data * @param len * @return new bstring */ bstr *bstr_memdup(char *data, size_t len) { bstr *b = bstr_alloc(len); if (b == NULL) return NULL; memcpy(bstr_ptr(b), data, len); ((bstr_t *) b)->len = len; return b; } /** * Create a new bstring by copying the provided bstring. * * @param b * @return new bstring */ bstr *bstr_strdup(bstr *b) { return bstr_strdup_ex(b, 0, bstr_len(b)); } /** * Create a new bstring by copying a part of the provided * bstring. * * @param b * @param offset * @param len * @return new bstring */ bstr *bstr_strdup_ex(bstr *b, size_t offset, size_t len) { bstr *bnew = bstr_alloc(len); if (bnew == NULL) return NULL; memcpy(bstr_ptr(bnew), bstr_ptr(b) + offset, len); ((bstr_t *) bnew)->len = len; return bnew; } /** * Take the provided memory region and construct a NUL-terminated * string, replacing NUL bytes with "\0". * * @param data * @param len * @return new NUL-terminated string */ char *bstr_memtocstr(char *data, size_t len) { // Count how many NUL bytes we have in the string. size_t i, nulls = 0; for (i = 0; i < len; i++) { if (data[i] == '\0') { nulls++; } } // Now copy the string into a NUL-terminated buffer. char *r, *t; r = t = malloc(len + nulls + 1); if (t == NULL) return NULL; while (len--) { // Escape NUL bytes, but just copy everything else. if (*data == '\0') { data++; *t++ = '\\'; *t++ = '0'; } else { *t++ = *data++; } } // Terminate string. *t = '\0'; return r; } /** * Create a new NUL-terminated string out of the provided bstring. * * @param b * @return new NUL-terminated string */ char *bstr_tocstr(bstr *b) { if (b == NULL) return NULL; return bstr_memtocstr(bstr_ptr(b), bstr_len(b)); } /** * Return the first position of the provided character (byte). * * @param b * @param c * @return the first position of the character, or -1 if it could not be found */ int bstr_chr(bstr *b, int c) { char *data = bstr_ptr(b); size_t len = bstr_len(b); size_t i = 0; while (i < len) { if (data[i] == c) { return i; } i++; } return -1; } /** * Return the last position of a character (byte). * * @param b * @param c * @return the last position of the character, or -1 if it could not be found */ int bstr_rchr(bstr *b, int c) { char *data = bstr_ptr(b); size_t len = bstr_len(b); int i = len; while (i >= 0) { if (data[i] == c) { return i; } i--; } return -1; } /** * Compare two memory regions. * * @param s1 * @param l1 * @param s2 * @param l2 * @return 0 if the memory regions are identical, -1 or +1 if they're not */ int bstr_cmp_ex(char *s1, size_t l1, char *s2, size_t l2) { size_t p1 = 0, p2 = 0; while ((p1 < l1) && (p2 < l2)) { if (s1[p1] != s2[p2]) { // Difference return (s1[p1] < s2[p2]) ? -1 : 1; } p1++; p2++; } if ((p1 == l2) && (p2 == l1)) { // They're identical return 0; } else { // One string is shorter if (p1 == l1) return -1; else return 1; } } /** * Compare a bstring with a NUL-terminated string. * * @param b * @param c * @return 0, -1 or +1 */ int bstr_cmpc(bstr *b, char *c) { return bstr_cmp_ex(bstr_ptr(b), bstr_len(b), c, strlen(c)); } /** * Compare two bstrings. * * @param b1 * @param b2 * @return 0, -1 or +1 */ int bstr_cmp(bstr *b1, bstr *b2) { return bstr_cmp_ex(bstr_ptr(b1), bstr_len(b1), bstr_ptr(b2), bstr_len(b2)); } /** * Convert bstring to lowercase. * * @param b * @return b */ bstr *bstr_tolowercase(bstr *b) { if (b == NULL) return NULL; unsigned char *data = (unsigned char *)bstr_ptr(b); size_t len = bstr_len(b); size_t i = 0; while (i < len) { data[i] = tolower(data[i]); i++; } return b; } /** * Create a copy of the provided bstring, then convert it to lowercase. * * @param b * @return bstring copy */ bstr *bstr_dup_lower(bstr *b) { return bstr_tolowercase(bstr_strdup(b)); } /** * */ int bstr_util_memtoip(char *data, size_t len, int base, size_t *lastlen) { int rval = 0, tval = 0, tflag = 0; size_t i = *lastlen = 0; for (i = 0; i < len; i++) { int d = data[i]; *lastlen = i; // Convert character to digit. if ((d >= '0') && (d <= '9')) { d -= '0'; } else if ((d >= 'a') && (d <= 'z')) { d -= 'a' - 10; } else if ((d >= 'A') && (d <= 'Z')) { d -= 'A' - 10; } else { d = -1; } // Check that the digit makes sense with the base // we are using. if ((d == -1) || (d >= base)) { if (tflag) { // Return what we have so far; lastlen points // to the first non-digit position. return rval; } else { // We didn't see a single digit. return -1; } } if (tflag) { rval *= base; if (tval > rval) { // Overflow return -2; } rval += d; if (tval > rval) { // Overflow return -2; } tval = rval; } else { tval = rval = d; tflag = 1; } } *lastlen = i + 1; return rval; } /** * Find needle in a haystack. * * @param haystack * @param needle * @return */ int bstr_indexof(bstr *haystack, bstr *needle) { return bstr_indexofmem(haystack, bstr_ptr(needle), bstr_len(needle)); } /** * Find index in the haystack, with the needle being a NUL-terminated string. * * @param haystack * @param needle * @return */ int bstr_indexofc(bstr *haystack, char *needle) { return bstr_indexofmem(haystack, needle, strlen(needle)); } /** * Find index in the haystack. Ignore case differences. * * @param haystack * @param needle * @return */ int bstr_indexof_nocase(bstr *haystack, bstr *needle) { return bstr_indexofmem_nocase(haystack, bstr_ptr(needle), bstr_len(needle)); } /** * Find index in the haystack, with the needle being a NUL-terminated string. * Ignore case differences. * * @param haystack * @param needle * @return */ int bstr_indexofc_nocase(bstr *haystack, char *needle) { return bstr_indexofmem_nocase(haystack, needle, strlen(needle)); } /** * Find index in the haystack, with the needle being a memory region. * * @param haystack * @param data2 * @param len2 * @return */ int bstr_indexofmem(bstr *haystack, char *data2, size_t len2) { unsigned char *data = (unsigned char *)bstr_ptr(haystack); size_t len = bstr_len(haystack); size_t i, j; // TODO Is an optimisation here justified? // http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm for (i = 0; i < len; i++) { size_t k = i; for (j = 0; ((j < len2) && (k < len)); j++) { if (data[k++] != data2[j]) break; } if ((k - i) == len2) { return i; } } return -1; } /** * Find index in the haystack, with the needle being a memory region. * Ignore case differences. * * @param haystack * @param data2 * @param len2 * @return */ int bstr_indexofmem_nocase(bstr *haystack, char *data2, size_t len2) { unsigned char *data = (unsigned char *)bstr_ptr(haystack); size_t len = bstr_len(haystack); size_t i, j; // TODO No need to inspect the last len2 - 1 bytes for (i = 0; i < len; i++) { size_t k = i; for (j = 0; ((j < len2) && (k < len)); j++) { if (toupper(data[k++]) != toupper((unsigned char)data2[j])) break; } if ((k - i) == len2) { return i; } } return -1; } /** * Remove one byte from the end of the string. * * @param s */ void bstr_chop(bstr *s) { bstr_t *b = (bstr_t *) s; if (b->len > 0) { b->len--; } } /** * Adjust bstring length. You will need to use this method whenever * you work directly with the string contents, and you end up changing * its length. * * @param s * @param newlen */ void bstr_len_adjust(bstr *s, size_t newlen) { bstr_t *b = (bstr_t *) s; b->len = newlen; } /** * Return the character (byte) at the given position. * * @param s * @param pos * @return the character, or -1 if the bstring is too short */ char bstr_char_at(bstr *s, size_t pos) { unsigned char *data = (unsigned char *)bstr_ptr(s); size_t len = bstr_len(s); if (pos > len) return -1; return data[pos]; }