/* // Full copyright information is available in the file ../doc/CREDITS // // This code is not ANSI-conformant, because it allocates memory at the end // of String structure and references it with a one-element array. */ #include "defs.h" #include <string.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(cStr), leaving us 32 bytes short of a power of * two, as desired. */ #define MALLOC_DELTA (sizeof(cStr) + 32) #define STARTING_SIZE (128 - MALLOC_DELTA) cStr *string_new(Int size_needed) { cStr *cnew; Int size; /* plus one for NULL */ size = size_needed + 1; cnew = (cStr *) emalloc(sizeof(cStr) + sizeof(char) * size); cnew->start = 0; cnew->len = 0; cnew->size = size; cnew->refs = 1; cnew->reg = NULL; *cnew->s = 0; return cnew; } cStr *string_from_chars(char *s, Int len) { cStr *cnew = string_new(len); MEMCPY(cnew->s, s, len); cnew->s[len] = (char) NULL; cnew->len = len; return cnew; } cStr *string_of_char(Int c, Int len) { cStr *cnew = string_new(len); memset(cnew->s, c, len); cnew->s[len] = 0; cnew->len = len; return cnew; } cStr *string_dup(cStr *str) { str->refs++; return str; } void string_pack(cStr *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); } } cStr *string_unpack(FILE *fp) { cStr *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; return str; } Int string_packed_size(cStr *str) { if (str) return size_long(str->len) + str->len * sizeof(char); else return size_long(-1); } Int string_cmp(cStr *str1, cStr *str2) { return strcmp(str1->s + str1->start, str2->s + str2->start); } cStr *string_fread(cStr *str, Int len, FILE *fp) { str = string_prep(str, str->start, str->len + len); fread(str->s + str->start + str->len - len, sizeof(char), len, fp); return str; } cStr *string_add(cStr *str1, cStr *str2) { str1 = string_prep(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; } /* calling this with len == 0 can be a problem */ cStr *string_add_chars(cStr *str, char *s, Int len) { str = string_prep(str, str->start, str->len + len); MEMCPY(str->s + str->start + str->len - len, s, len); str->s[str->start + str->len] = 0; return str; } cStr *string_addc(cStr *str, Int c) { str = string_prep(str, str->start, str->len + 1); str->s[str->start + str->len - 1] = c; str->s[str->start + str->len] = 0; return str; } cStr *string_add_padding(cStr *str, char *filler, Int len, Int padding) { str = string_prep(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; } cStr *string_truncate(cStr *str, Int len) { str = string_prep(str, str->start, len); str->s[str->start + len] = 0; return str; } cStr *string_substring(cStr *str, Int start, Int len) { str = string_prep(str, str->start + start, len); str->s[str->start + str->len] = 0; return str; } cStr * string_uppercase(cStr *str) { char *s, *end; str = string_prep(str, str->start, str->len); s = string_chars(str); end = s + str->len; for (; s < end; s++) *s = (char) UCASE(*s); return str; } cStr * string_lowercase(cStr *str) { char *s, *end; str = string_prep(str, str->start, str->len); s = string_chars(str); end = s + str->len; for (; s < end; s++) *s = (char) 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(cStr *str) { if (!str->reg) str->reg = regcomp(str->s + str->start); return str->reg; } void string_discard(cStr *str) { if (!--str->refs) { if (str->reg) efree(str->reg); efree(str); } } cStr * string_parse(char **sptr) { cStr * str; char *p, *s, *start = *sptr; start++; /* compress escaped, trust me, it works this time (BJG) */ for (p=s=start; *p && *p != '"'; p++, s++) { if (*p == '\\' && *(p+1) && (*(p+1) == '"' || *(p+1) == '\\')) p++; *s = *p; } /* make it into a string */ str = string_from_chars(start, s - start); /* push *sptr to its new position */ *sptr = *p ? p+1 : p; /* give them the string */ return str; } cStr *string_add_unparsed(cStr *str, char *s, Int len) { Int i; str = string_addc(str, '"'); /* Add characters to string, escaping quotes and backslashes. */ forever { for (i = 0; i < len && s[i] != '"' && s[i] != '\\'; i++); str = string_add_chars(str, s, i); if (i < len) { if (s[i] == '"' || /* it is a slash, by default */ (s[i + 1] == '\\' || s[i + 1] == '"' || s[i + 1] == (char) NULL)) 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; } /* // ------------------------------------------------------------- // index() and company */ INTERNAL int str_rindexs(char * str, int len, char * sub, int slen, int origin){ register char * s; if (origin < slen) origin = slen; len -= origin; if (len < 0) return 0; s = &str[len]; while (len-- >= 0) { if (*s == *sub) { if (!strncmp(s, sub, slen)) return (s - str) + 1; } s--; } return 0; } INTERNAL int str_rindexc(char * str, int len, char sub, int origin) { register char * s; len -= origin; if (len < 0) return 0; s = &str[len]; while (len-- >= 0) { if (*s == sub) return (s - str) + 1; s--; } return 0; } /* // returns 1..$ if item is found, 0 if it is not or -1 if an error is thrown */ int string_index(cStr * str, cStr * sub, int origin) { int len, slen; char * s, * p, * ss; Bool reverse = NO; s = string_chars(str); len = string_length(str); ss = string_chars(sub); slen = string_length(sub); if (origin < 0) { reverse = YES; origin = -origin; } if (origin > len || !origin) return F_FAILURE; if (reverse) { if (slen == 1) return str_rindexc(s, len, *ss, origin); return str_rindexs(s, len, ss, slen, origin); } else if (origin > 0) { origin--; if (len - origin < slen) return 0; p = s + origin; if ((p = strcstr(p, ss))) return (p - s)+1; } return 0; } /* // 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. */ cStr * string_prep(cStr *str, Int start, Int len) { cStr *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. */ #if DISABLED 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); #endif need_to_resize = str->size <= len + start; need_to_move = (str->refs > 1) || (need_to_resize && start > 0); if (need_to_move) { /* Move the string's contents into a new string. */ cnew = string_new(len); MEMCPY(cnew->s, str->s + start, (len > str->len) ? str->len : len); cnew->s[len] = (char) NULL; cnew->len = len; string_discard(str); return cnew; } else if (need_to_resize) { /* Resize the string. We can assume that string->start == start == 0 */ str->len = len; size = len + 1; /* plus one for NULL */ str = (cStr *)erealloc(str, sizeof(cStr)+(size * sizeof(char))); str->s[start+len] = (char) NULL; str->size = size; return str; } else { if (str->reg) { efree(str->reg); str->reg = NULL; } str->start = start; str->len = len; str->s[start+len] = (char) NULL; return str; } }