/* dstring and dsarray * data structures and functions * related to creating and manipulating * 'safe' dynamic strings. the safe or secure * attribute of this library come from the manipulation * of strings and arrays using dynamic memory allocation instead * of a fixed size buffer thus preventing buffer overflows. * of course this does not prevent DOS attacks if the attacker * continually feeds memory into the dynamic string or array. this would * have to be prevented on the interface side of the application * and not this library. * * * Copyright: 2004 by Michael Siley * * TODO: * - more unit tests using assertions. WIP * - bring in code for BSD strlcpy and strlcat to make code even more secure. * - have the dstring.length be a unsigned long int for maximum length of strings * possible on the system. * - create a ds_compact method to make the buffer size equal the string size. * - dstrings leaving functions should be of the compact form. since most of the time * they are usually not added to like buffers. * - need the option of buffer and compact types for dstrings. * * a buffer dstring would be designed for speed and therefore * more space would be allocated then necessary. * * a compact or normal dstring would be allocated just enough * space for the string value it holds. * * ? should there be an upper limit size? * - create a generic dynamic array library. * */ #include "dstring.h" /* utility functions. */ // at this point if the system is out of memory // we get scared and bail. the chances of this // happening on a modern system are small. and if // it does there's not much hope of recovery anyway. static void memchk(void *ptr) { if (ptr == NULL) { fprintf(stderr, "ERROR: Out Of Memory!\n"); exit(1); } } /* * dsarray functions * */ // creates an array of a given size. dsarray *dsa_init(unsigned int size) { dstring **ds_ptr; dsarray *dsa = (dsarray *)malloc(sizeof(dsarray)); memchk(dsa); ds_ptr = (dstring **)malloc(size * sizeof(dstring)); memchk(ds_ptr); dsa->dstrings = ds_ptr; dsa->dstrings[0] = NULL; // should i be doing this? dsa->length = 0; dsa->max_size = size; return dsa; } // frees a dsarray void dsa_free(dsarray *dsa) { int i; if (dsa != NULL) { for (i = 0; i < dsa->length; i++) ds_free(dsa->dstrings[i]); free(dsa); dsa = NULL; } } // resizes a dsarray with the given new_size. static void dsa_resize(dsarray *dsa, unsigned int new_size) { dstring **tmp = NULL; tmp = (dstring **)realloc(dsa->dstrings, new_size * sizeof(dstring)); memchk(tmp); dsa->dstrings = tmp; dsa->max_size = new_size; } // appends a dstring unto the end of a given dsarray. void dsa_append(dsarray *dsa, dstring *ds) { if (dsa->length >= dsa->max_size) // array too small increase in size by ARRAY_RESIZE_FACTOR dsa_resize(dsa, (int)(dsa->max_size * ARRAY_RESIZE_FACTOR)); dsa->dstrings[dsa->length] = ds_copy(ds); dsa->length++; } // concats dsa1 to the back of dsa2. void dsa_cat(dsarray *dsa1, dsarray *dsa2) { int i; for (i = 0; i < dsa2->length; i++) dsa_append(dsa1, dsa2->dstrings[i]); } /* * string functions * * some of the functions are specifc * to dstrings and some to ordinary cstrings. * the differences arise when applying the following rules. * 1. make the functions as generic as possible for use by dstrings and cstrings. * 2. capitalize on dstring's dynamic memory capabilities. * 3. whenever we create a new string we prefer a dstring. * */ /* dstring centric functions. */ // resize dstring to given size. static void ds_resize(dstring *ds, unsigned int size) { char *new_str; new_str = (char *)realloc(ds->string, size * sizeof(char)); memchk(new_str); ds->string = new_str; ds->max_size = size; } void ds_compact(dstring *ds) { ds_resize(ds, ds->length+1); ds->is_buffer = FALSE; // we assume no longer a buffer. } // create a dstring with allocated memory of given size. dstring *ds_buffer_init(unsigned int size) { char *new_str; dstring *ds = (dstring *)malloc(sizeof(dstring)); memchk(ds); //size = INIT_STR_SIZE > size ? INIT_STR_SIZE : size; new_str = (char *)calloc(size, sizeof(char)); memchk(new_str); ds->string = new_str; ds->string[0] = '\0'; ds->length = 0; ds->max_size = size; ds->is_buffer = TRUE; return ds; } // initialize a new dstring with a c string. dstring *ds_init(char *s) { int size = strlen(s); dstring *ds; ds = ds_buffer_init(size+1); strcpy(ds->string, s); ds->length = size; ds->is_buffer = FALSE; return ds; } // trunc a dstring to a null dstring // leaving memory allocation intact. void ds_clear(dstring *ds) { ds->string[0] = '\0'; ds->length = 0; } // returns a new copy of the passed in dstring. dstring *ds_copy(dstring *ds) { return ds_init(ds->string); } // copies the string and creates a new // memory allocated string. char *cs_copy(char *s) { int size = strlen(s); char *new_s; new_s = (char *)calloc(size+1, sizeof(char)); memchk(new_s); strcpy(new_s, s); return new_s; } // free up a dynamic string from memory. // XXX there's a bug here on double free situations. void ds_free(dstring *ds) { if (ds != NULL) { if (ds->string != NULL) { free(ds->string); ds->string = NULL; } free(ds); ds = NULL; } } // appends a cstring to a dstring. void ds_append(dstring *ds, char *s) { register int slen = strlen(s); register int size = ds->length + slen; register int resize_size = size+1; if (resize_size > ds->max_size) { if (ds->is_buffer) resize_size = ds->max_size * 2; ds_resize(ds, (int)resize_size); } strcat(ds->string, s); ds->length = size; } // append a char to a dstring. void ds_append_char(dstring *ds, char c) { register int len = ds->length; char buf[2]; if (len < ds->max_size) { ds->string[len] = c; ds->string[++ds->length] = 0; } else { buf[0] = c; buf[1] = 0; ds_append(ds, buf); } } // return a substring of a cstring // starting at index start with length of len. dstring *cs_substr(char *s, int start, int len) { int i, widx, last_n; int size = strlen(s); dstring *ds; char *sub_word = (char *)calloc(size+1, sizeof(char)); memchk(sub_word); last_n = start+len; last_n = last_n > size ? size : last_n; widx = 0; for (i = start; i < last_n; i++) sub_word[widx++] = s[i]; sub_word[widx] = '\0'; ds = ds_init(sub_word); free(sub_word); return ds; } // same as cs_substr but with a dstring argument. dstring *ds_substr(dstring *ds, int start, int len) { return cs_substr(ds->string, start, len); } // lowers the case of a cstring void cs_lower(char *s) { int size = strlen(s); int i; for (i = 0; i < size; i++) s[i] = tolower(s[i]); } // ups the case of a cstring void cs_upper(char *s) { int size = strlen(s); int i; for (i = 0; i < size; i++) s[i] = toupper(s[i]); } // lowers the case of a dstring void ds_lower(dstring *ds) { cs_lower(ds->string); } // ups the case of a dstring void ds_upper(dstring *ds) { cs_upper(ds->string); } // reverses a cstring. void cs_reverse(char *s) { int len = strlen(s); int i, j; int stop; char tmp; if (len > 0) { stop = len-1; i = 0; j = stop; while (i < j) { tmp = s[i]; s[i] = s[j]; s[j] = tmp; i++; j--; } } } // reverses a dstring. void ds_reverse(dstring *ds) { cs_reverse(ds->string); } // strip whitespace from right side of cstring return new string size. int cs_rstrip(char *s) { int size = strlen(s); int i, idx; char c; char *new_s; int new_size = 0; int in_word = FALSE; new_s = (char *)calloc(size+1, sizeof(char)); memchk(new_s); idx = 0; for (i = 0; i < size; i++) { c = s[i]; if (!isspace(c)) in_word = TRUE; if (in_word) new_s[idx++] = c; } new_s[idx] = '\0'; strcpy(s, new_s); new_size = strlen(new_s); free(new_s); return new_size; } void ds_rstrip(dstring *ds) { int size = cs_rstrip(ds->string); ds->length = size; } int cs_lstrip(char *s) { int size = 0; cs_reverse(s); size = cs_rstrip(s); cs_reverse(s); return size; } int cs_strip(char *s) { cs_rstrip(s); cs_lstrip(s); return strlen(s); } // strip whitespace from left side of dstring. void ds_lstrip(dstring *ds) { int size = cs_lstrip(ds->string); ds->length = size; } // strip whitespace from both sides of dstring. void ds_strip(dstring *ds) { ds_rstrip(ds); ds_lstrip(ds); } // finds the first occurence of subds in ds. // if not found returns -1 else returns the index of // the first char of string sub found. int cs_index(char *s, char *sub) { char c, sub_c; int i; int index, sub_idx; int sub_len = strlen(sub); int s_len = strlen(s); index = -1; if (s_len == 0 || sub_len == 0) return index; if (sub_len > s_len) return index; sub_idx = 0; for (i = 0; i < s_len; i++) { c = s[i]; sub_c = sub[sub_idx]; if (c == sub_c) sub_idx++; else sub_idx = 0; if (sub_len == sub_idx) { index = i - sub_len + 1; break; } } return index; } // same as cs_index except using a dstring. int ds_index(dstring *ds, char *sub) { return cs_index(ds->string, sub); } // returns a dsarray of dstrings that were between the cstring delim. dsarray *ds_split(dstring *ds, char *delim) { int start_idx; int stop_idx; int delim_len = strlen(delim); dstring *ds_field; dstring *ds_p = NULL; dstring *dscpy = ds_copy(ds); dsarray *dsa = dsa_init(10); start_idx = 0; while ((stop_idx = ds_index(dscpy, delim)) != -1) { ds_field = ds_substr(dscpy, start_idx, stop_idx-start_idx); dsa_append(dsa, ds_field); ds_free(ds_field); ds_p = ds_substr(dscpy, stop_idx+delim_len, ds->length); ds_free(dscpy); dscpy = ds_p; } if (dscpy->length > 0) dsa_append(dsa, dscpy); if (dscpy != NULL) ds_free(dscpy); return dsa; } // joins the dstrings together in dsa with the dstring delim. dstring *ds_join(dsarray *dsa, char *delim) { int i; dstring *ds = ds_buffer_init(100); for (i = 0; i < dsa->length; i++) { ds_append(ds, dsa->dstrings[i]->string); if (i != dsa->length-1) ds_append(ds, delim); } return ds; } // repalces old string with new string in ds in place. void ds_replace(dstring *ds, char *oldds, char *newds) { dsarray *dsa; dstring *new_ds; dsa = ds_split(ds, oldds); new_ds = ds_join(dsa, newds); free(ds->string); ds->string = new_ds->string; ds->length = new_ds->length; ds->max_size = new_ds->max_size; ds->is_buffer = new_ds->is_buffer; free(new_ds); }