/* string.c: String-handling routines. * This code is not ANSI-conformant, because it allocates memory at the end * of String structure and references it with a one-element array. */ #define _POSIX_SOURCE #include <stdio.h> #include <string.h> #include "cmstring.h" #include "memory.h" #include "dbpack.h" #include "util.h" /* Note that we number string elements [0..(len - 1)] internally, while the * user sees string elements as numbered [1..len]. */ /* Many implementations of malloc() deal best with blocks eight or sixteen * bytes less than a power of two. MALLOC_DELTA and STRING_STARTING_SIZE take * this into account. We start with a string MALLOC_DELTA bytes less than * a power of two. When we enlarge a string, we double it and add MALLOC_DELTA * so that we're still MALLOC_DELTA less than a power of two. When we * allocate, we add in sizeof(String), leaving us 32 bytes short of a power of * two, as desired. */ #define MALLOC_DELTA (sizeof(String) + 32) #define STARTING_SIZE (128 - MALLOC_DELTA) static String *prepare_to_modify(String *str, int start, int len); String *string_new(int size_needed) { String *cnew; int size; size = STARTING_SIZE; while (size < size_needed) size = size * 2 + MALLOC_DELTA; cnew = (String *) emalloc(sizeof(String) + sizeof(char) * size); cnew->start = 0; cnew->len = 0; cnew->size = size; cnew->refs = 1; cnew->reg = NULL; *cnew->s = 0; return cnew; } String *string_from_chars(char *s, int len) { String *cnew = string_new(len); MEMCPY(cnew->s, s, len); cnew->s[len] = 0; cnew->len = len; return cnew; } String *string_of_char(int c, int len) { String *cnew = string_new(len); memset(cnew->s, c, len); cnew->s[len] = 0; cnew->len = len; return cnew; } String *string_dup(String *str) { str->refs++; return str; } int string_length(String *str) { return str->len; } char *string_chars(String *str) { return str->s + str->start; } void string_pack(String *str, FILE *fp) { if (str) { write_long(str->len, fp); fwrite(str->s + str->start, sizeof(char), str->len, fp); } else { write_long(-1, fp); } /*fprintf(stderr, "string_pack: %s len: %d @%d\n", str, str->len, ftell(fp));*/ } String *string_unpack(FILE *fp) { String *str; int len; int result; len = read_long(fp); if (len == -1) { /*fprintf(stderr, "string_unpack: NULL @%d\n", ftell(fp));*/ return NULL; } str = string_new(len); str->len = len; result = fread(str->s, sizeof(char), len, fp); str->s[len] = 0; /*fprintf(stderr, "string_unpack: %s %d result: %d @%d\n", str, len, result, ftell(fp));*/ return str; } int string_packed_size(String *str) { if (str) return size_long(str->len) + str->len * sizeof(char); else return size_long(-1); } int string_cmp(String *str1, String *str2) { return strcmp(str1->s + str1->start, str2->s + str2->start); } String *string_fread(String *str, int len, FILE *fp) { str = prepare_to_modify(str, str->start, str->len + len); fread(str->s + str->start + str->len - len, sizeof(char), len, fp); return str; } String *string_add(String *str1, String *str2) { str1 = prepare_to_modify(str1, str1->start, str1->len + str2->len); MEMCPY(str1->s + str1->start + str1->len - str2->len, str2->s + str2->start, str2->len); str1->s[str1->start + str1->len] = 0; return str1; } String *string_add_chars(String *str, char *s, int len) { str = prepare_to_modify(str, str->start, str->len + len); MEMCPY(str->s + str->start + str->len - len, s, len); /*str->s[str->len + str->start + len] = 0;*/ str->s[str->start + str->len] = 0; return str; } String *string_addc(String *str, int c) { str = prepare_to_modify(str, str->start, str->len + 1); str->s[str->start + str->len - 1] = c; str->s[str->start + str->len] = 0; return str; } String *string_add_padding(String *str, char *filler, int len, int padding) { str = prepare_to_modify(str, str->start, str->len + padding); if (len == 1) { /* Optimize this case using memset(). */ memset(str->s + str->start + str->len - padding, *filler, padding); return str; } while (padding > len) { MEMCPY(str->s + str->start + str->len - padding, filler, len); padding -= len; } MEMCPY(str->s + str->start + str->len - padding, filler, padding); return str; } String *string_truncate(String *str, int len) { str = prepare_to_modify(str, str->start, len); str->s[str->start + len] = 0; return str; } String *string_substring(String *str, int start, int len) { str = prepare_to_modify(str, str->start + start, len); str->s[str->start + str->len] = 0; return str; } String *string_uppercase(String *str) { char *s, *start, *end; str = prepare_to_modify(str, str->start, str->len); start = str->s + str->start; end = start + str->len; for (s = start; s < end; s++) *s = UCASE(*s); return str; } String *string_lowercase(String *str) { char *s, *start, *end; str = prepare_to_modify(str, str->start, str->len); start = str->s + str->start; end = start + str->len; for (s = start; s < end; s++) *s = LCASE(*s); return str; } /* Compile str's regexp, if it's not already compiled. If there is an error, * it will be placed in regexp_error, and the returned regexp will be NULL. */ regexp *string_regexp(String *str) { if (!str->reg) str->reg = regcomp(str->s + str->start); return str->reg; } void string_discard(String *str) { if (!--str->refs) { if (str->reg) free(str->reg); free(str); } } String *string_parse(char **sptr) { String *str; char *s = *sptr, *p; str = string_new(0); s++; while (1) { for (p = s; *p && *p != '"' && *p != '\\'; p++); str = string_add_chars(str, s, p - s); s = p + 1; if (!*p || *p == '"') break; if (*s) str = string_addc(str, *s++); } *sptr = s; return str; } String *string_add_unparsed(String *str, char *s, int len) { int i; str = string_addc(str, '"'); /* Add characters to string, escaping quotes and backslashes. */ while (1) { for (i = 0; i < len && s[i] != '"' && s[i] != '\\'; i++); str = string_add_chars(str, s, i); if (i < len) { str = string_addc(str, '\\'); str = string_addc(str, s[i]); s += i + 1; len -= i + 1; } else { break; } } return string_addc(str, '"'); } char *regerror(char *msg) { static char *regexp_error; if (msg) regexp_error = msg; return regexp_error; } /* Input to this routine should be a string you want to modify, a start, and a * length. The start gives the offset from str->s at which you start being * interested in characters; the length is the amount of characters there will * be in the string past that point after you finish modifying it. * * The return value of this routine is a string whose contents can be freely * modified, containing at least the information you claimed was interesting. * str->start will be set to the beginning of the interesting characters; * str->len will be set to len, even though this will make some characters * invalid if len > str->len upon input. Also, the returned string may not be * null-terminated. * * In general, modifying start and len is the responsibility of this routine; * modifying the contents is the responsibility of the calling routine. */ static String *prepare_to_modify(String *str, int start, int len) { String *cnew; int need_to_move, need_to_resize, size; /* Figure out if we need to resize the string or move its contents. Moving * contents takes precedence. */ need_to_resize = (len - start) * 4 < str->size; need_to_resize = need_to_resize && str->size > STARTING_SIZE; need_to_resize = need_to_resize || (str->size < len); need_to_move = (str->refs > 1) || (need_to_resize && start > 0); if (need_to_move) { /* Move the string's contents into a new list. */ cnew = string_new(len); MEMCPY(cnew->s, str->s + start, (len > str->len) ? str->len : len); cnew->len = len; string_discard(str); return cnew; } else if (need_to_resize) { /* Resize the list. We can assume that list->start == start == 0. */ str->len = len; size = STARTING_SIZE; while (size < len) size = size * 2 + MALLOC_DELTA; str = (String *)erealloc(str, sizeof(String) + (size * sizeof(char))); str->size = size; return str; } else { if (str->reg) { free(str->reg); str->reg = NULL; } str->start = start; str->len = len; return str; } }