mirror of https://github.com/OISF/suricata
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			615 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
			
		
		
	
	
			615 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
/*
 | 
						|
 * LibHTP (http://www.libhtp.org)
 | 
						|
 * Copyright 2009,2010 Ivan Ristic <ivanr@webkreator.com>
 | 
						|
 *
 | 
						|
 * 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 <ctype.h>
 | 
						|
 | 
						|
/**
 | 
						|
 * 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];
 | 
						|
}
 | 
						|
 |