#define _GNU_SOURCE #include #include #include #include #include #include "dynstr.h" struct Dynstr { const unsigned char *mem; size_t size; bool dynamic; }; static dynstr_error_handler *error_handler = dynstr_malloc_error_indicate; static void *(*current_malloc)(size_t) = malloc; void dynstr_init(void) { error_handler = dynstr_malloc_error_indicate; current_malloc = malloc; } void *(*dynstr_get_malloc(void))(size_t) { return current_malloc; } void dynstr_set_malloc(void *(*new_malloc)(size_t)) { current_malloc = new_malloc; } dynstr_error_handler *dynstr_get_malloc_error_handler(void) { return error_handler; } void dynstr_set_malloc_error_handler(dynstr_error_handler *handler) { error_handler = handler; } void dynstr_malloc_error_indicate(int error, size_t size, void *oldptr) { } void dynstr_malloc_error_abort(int error, size_t size, void *oldptr) { abort(); } /* Dynamically allocate a number of bytes. */ static void *alloc(size_t size) { void *p; p = current_malloc(size); if (p == NULL) error_handler(errno, size, NULL); return p; } /* Allocate a new dynamic string. If dynamic is true, the contents of the * new string is copied from mem. Otherwise the new string just uses mem * directly. */ static Dynstr *new(const void *mem, size_t size, bool dynamic) { Dynstr *dynstr; dynstr = alloc(sizeof(Dynstr)); if (dynstr == NULL) return NULL; if (dynamic) { void *newmem = alloc(size); if (newmem == NULL) { free(dynstr); return NULL; } memcpy(newmem, mem, size); dynstr->mem = newmem; dynstr->size = size; dynstr->dynamic = true; } else { dynstr->mem = mem; dynstr->size = size; dynstr->dynamic = false; } return dynstr; } Dynstr *dynstr_new_empty(void) { return new(NULL, 0, true); } Dynstr *dynstr_new_from_cstring(const char *cstring) { return new(cstring, strlen(cstring), true); } Dynstr *dynstr_new_from_memory(const void *mem, size_t size) { return new(mem, size, true); } Dynstr *dynstr_new_from_constant_cstring(const char *cstring) { return new(cstring, strlen(cstring), false); } Dynstr *dynstr_new_from_constant_memory(const void *mem, size_t size) { return new(mem, size, false); } void dynstr_free(Dynstr *dynstr) { if (dynstr) { if (dynstr->dynamic) free((void *) dynstr->mem); free(dynstr); } } size_t dynstr_len(Dynstr *dynstr) { return dynstr->size; } bool dynstr_is_empty(Dynstr *dynstr) { return dynstr_len(dynstr) == 0; } size_t dynstr_memcpy(void *mem, Dynstr *dynstr, size_t offset, size_t size) { size_t len; len = dynstr_len(dynstr); if (offset >= len) return 0; if (size > len - offset) size = len - offset; memcpy(mem, dynstr->mem + offset, size); return size; } Dynstr *dynstr_substr(Dynstr *dynstr, size_t offset, size_t size) { if (offset >= dynstr->size) { offset = 0; size = 0; } else if (size > dynstr->size - offset) { size = dynstr->size - offset; } return new(dynstr->mem + offset, size, true); } Dynstr *dynstr_cat(Dynstr *dynstr, ...) { va_list args; Dynstr *result; size_t size; void *mem; size_t offset; va_start(args, dynstr); size = 0; for (Dynstr *p = dynstr; p != NULL; p = va_arg(args, Dynstr *)) size += p->size; va_end(args); mem = alloc(size); if (mem == NULL) return NULL; va_start(args, dynstr); offset = 0; for (Dynstr *p = dynstr; p != NULL; p = va_arg(args, Dynstr *)) { memcpy(mem + offset, p->mem, p->size); offset += p->size; } va_end(args); result = new(mem, size, false); if (result == NULL) { free(mem); return NULL; } result->dynamic = true; return result; } int dynstr_byte_at(Dynstr *dynstr, size_t offset) { if (offset >= dynstr->size) return -1; return dynstr->mem[offset]; } size_t dynstr_first_byte(Dynstr *dynstr, size_t offset, int byte) { unsigned char *p; if (offset >= dynstr->size) return DYNSTR_NOT_FOUND; p = memchr(dynstr->mem + offset, byte, dynstr->size - offset); if (p == NULL) return DYNSTR_NOT_FOUND; return (size_t) (p - dynstr->mem); } size_t dynstr_last_byte(Dynstr *dynstr, size_t offset, int byte) { unsigned char *p; if (offset >= dynstr->size) return DYNSTR_NOT_FOUND; p = memrchr(dynstr->mem + offset, byte, dynstr->size - offset); if (p == NULL) return DYNSTR_NOT_FOUND; return (size_t) (p - dynstr->mem); } size_t dynstr_first_string(Dynstr *dynstr, size_t offset, Dynstr *pattern) { const unsigned char *p; if (offset >= dynstr->size) return DYNSTR_NOT_FOUND; if (pattern->size == 0) return DYNSTR_NOT_FOUND; p = memmem(dynstr->mem + offset, dynstr->size - offset, pattern->mem, pattern->size); if (p == NULL) return DYNSTR_NOT_FOUND; return (size_t) (p - dynstr->mem); } size_t dynstr_last_string(Dynstr *dynstr, size_t offset, Dynstr *pattern) { size_t result; size_t new_result; /* GNU libc lacks a memrmem function, so we loop to the last * dynstr_first_string match. */ result = DYNSTR_NOT_FOUND; for (;;) { new_result = dynstr_first_string(dynstr, offset, pattern); if (new_result == DYNSTR_NOT_FOUND) break; result = new_result; offset = result + 1; } return result; } int dynstr_cmp(Dynstr *dynstr1, Dynstr *dynstr2) { int result; size_t min_size; if (dynstr1->size < dynstr2->size) min_size = dynstr1->size; else min_size = dynstr2->size; result = memcmp(dynstr1->mem, dynstr2->mem, min_size); if (result == 0) { if (dynstr1->size < dynstr2->size) return -1; else if (dynstr1->size == dynstr2->size) return 0; else return 1; } return result; } size_t dynstr_fwrite(FILE *file, Dynstr *dynstr) { return fwrite(dynstr->mem, 1, dynstr->size, file); }