/* String utility functions Copyright (C) 1999-2001, Joe Orton This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "ne_alloc.h" #include "string_utils.h" struct sbuffer_s { char *data; /* KEEP THIS HERE, to allow SBUFFER_CAST to work */ size_t used; /* used bytes in buffer */ size_t length; /* length of buffer */ }; /* TODO: These are both crap. Rewrite to be like strsep(). */ char **split_string(const char *str, const char separator, const char *quotes, const char *whitespace) { return split_string_c(str, separator, quotes, whitespace, NULL); } char **split_string_c(const char *str, const char separator, const char *quotes, const char *whitespace, int *give_count) { char **comps; const char *pnt, *quot = NULL, *start, *end; /* The start of the current component */ int count, /* The number of components */ iswhite, /* is it whitespace */ issep, /* is it the separator */ curr, /* current component index */ length, /* length of component */ leading_wspace; /* in leading whitespace still? */ /* Inefficient, but easier - first off, count the number of * components we have. */ count = 1; for (pnt = str; *pnt!='\0'; pnt++) { if (quotes != NULL) { quot = strchr(quotes, *pnt); } if (quot != NULL) { /* We found a quote, so skip till the next quote */ for (pnt++; (*pnt!=*quot) && (*pnt!='\0'); pnt++) /* nullop */; } else if (*pnt == separator) { count++; } } if (give_count) { /* Write the count */ *give_count = count; } /* Now, have got the number of components. * Allocate the comps array. +1 for the NULL */ comps = ne_malloc(sizeof(char *) * (count + 1)); comps[count] = NULL; quot = end = start = NULL; curr = 0; leading_wspace = 1; /* Now fill in the array */ for (pnt = str; *pnt != '\0'; pnt++) { /* What is the current character - quote, whitespace, separator? */ if (quotes != NULL) { quot = strchr(quotes, *pnt); } iswhite = (whitespace!=NULL) && (strchr(whitespace, *pnt) != NULL); issep = (*pnt == separator); /* What to do? */ if (leading_wspace) { if (quot!=NULL) { /* Quoted bit */ start = pnt; length = 1; leading_wspace = 0; } else if (issep) { /* Zero-length component */ comps[curr++] = ne_strdup(""); } else if (!iswhite) { start = end = pnt; length = 1; leading_wspace = 0; } } else { if (quot!=NULL) { /* Quoted bit */ length++; } else if (issep) { /* End of component - enter it into the array */ length = (end - start) + 1; comps[curr] = ne_malloc(length+1); memcpy(comps[curr], start, length); comps[curr][length] = '\0'; curr++; leading_wspace = 1; } else if (!iswhite) { /* Not whitespace - update end marker */ end = pnt; } } if (quot != NULL) { /* Skip to closing quote */ for (pnt++; *pnt!=*quot && *pnt != '\0'; ++pnt) /* nullop */; /* Last non-wspace char is closing quote */ end = pnt; } } /* Handle final component */ if (leading_wspace) { comps[curr] = ne_strdup(""); } else { /* End of component - enter it into the array */ length = (end - start) + 1; comps[curr] = ne_malloc(length+1); memcpy(comps[curr], start, length); comps[curr][length] = '\0'; } return comps; } char **pair_string(const char *str, const char compsep, const char kvsep, const char *quotes, const char *whitespace) { char **comps, **pairs, *split; int count = 0, n, length; comps = split_string_c(str, compsep, quotes, whitespace, &count); /* Allocate space for 2* as many components as split_string returned, * +2 for the NULLS. */ pairs = ne_malloc((2*count+2) * sizeof(char *)); if (pairs == NULL) { return NULL; } for (n = 0; n < count; n++) { /* Find the split */ split = strchr(comps[n], kvsep); if (split == NULL) { /* No seperator found */ length = strlen(comps[n]); } else { length = split-comps[n]; } /* Enter the key into the array */ pairs[2*n] = comps[n]; /* Null-terminate the key */ pairs[2*n][length] = '\0'; pairs[2*n+1] = split?(split + 1):NULL; } free(comps); pairs[2*count] = pairs[2*count+1] = NULL; return pairs; } void split_string_free(char **components) { char **pnt = components; while (*pnt != NULL) { free(*pnt); pnt++; } free(components); } void pair_string_free(char **pairs) { int n; for (n = 0; pairs[n] != NULL; n+=2) { free(pairs[n]); } free(pairs); } char *shave_string(const char *str, const char ch) { size_t len = strlen(str); char *ret; if (str[len-1] == ch) { len--; } if (str[0] == ch) { len--; str++; } ret = ne_malloc(len + 1); memcpy(ret, str, len); ret[len] = '\0'; return ret; } char *ne_concat(const char *str, ...) { va_list ap; sbuffer tmp = sbuffer_create(); sbuffer_zappend(tmp, str); va_start(ap, str); sbuffer_concat(tmp, ap); va_end(ap); return sbuffer_finish(tmp); } char *sbuffer_data(sbuffer buf) { return buf->data; } int sbuffer_size(sbuffer buf) { return buf->used - 1; } void sbuffer_clear(sbuffer buf) { memset(buf->data, 0, buf->length); buf->used = 1; } /* Grows for given size, returns 0 on success, -1 on error. */ int sbuffer_grow(sbuffer buf, size_t newsize) { size_t newlen, oldbuflen; #define SBUFFER_GROWTH 512 if (newsize <= buf->length) return 0; /* big enough already */ /* FIXME: ah, can't remember my maths... better way to do this? */ newlen = ((newsize / SBUFFER_GROWTH) + 1) * SBUFFER_GROWTH; oldbuflen = buf->length; /* Reallocate bigger buffer */ buf->data = realloc(buf->data, newlen); if (buf->data == NULL) return -1; buf->length = newlen; /* Zero-out the new bit of buffer */ memset(buf->data+oldbuflen, 0, newlen-oldbuflen); return 0; } int sbuffer_concat(sbuffer buf, ...) { va_list ap; char *next; size_t totallen = buf->used; /* Find out how much space we need for all the args */ va_start(ap, buf); do { next = va_arg(ap, char *); if (next != NULL) { totallen += strlen(next); } } while (next != NULL); va_end(ap); /* Grow the buffer */ if (sbuffer_grow(buf, totallen)) return -1; /* Now append the arguments to the buffer */ va_start(ap, buf); do { next = va_arg(ap, char *); if (next != NULL) { /* TODO: use stpcpy */ strcat(buf->data, next); } } while (next != NULL); va_end(ap); buf->used = totallen; return 0; } /* Append zero-terminated string... returns 0 on success or -1 on * realloc failure. */ int sbuffer_zappend(sbuffer buf, const char *str) { size_t len = strlen(str); if (sbuffer_grow(buf, buf->used + len)) { return -1; } strcat(buf->data, str); buf->used += len; return 0; } int sbuffer_append(sbuffer buf, const char *data, size_t len) { if (sbuffer_grow(buf, buf->used + len)) { return -1; } memcpy(buf->data + buf->used - 1, data, len); buf->used += len; buf->data[buf->used - 1] = '\0'; return 0; } sbuffer sbuffer_create(void) { return sbuffer_create_sized(512); } sbuffer sbuffer_create_sized(size_t s) { sbuffer buf = ne_malloc(sizeof(struct sbuffer_s)); buf->data = ne_calloc(s); buf->length = s; buf->used = 1; return buf; } void sbuffer_destroy(sbuffer buf) { if (buf->data) { free(buf->data); } free(buf); } char *sbuffer_finish(sbuffer buf) { char *ret = buf->data; free(buf); return ret; } void sbuffer_altered(sbuffer buf) { buf->used = strlen(buf->data) + 1; } /* Writes the ASCII representation of the MD5 digest into the * given buffer, which must be at least 33 characters long. */ void md5_to_ascii(const unsigned char md5_buf[16], char *buffer) { int count; for (count = 0; count<16; count++) { buffer[count*2] = HEX2ASC(md5_buf[count] >> 4); buffer[count*2+1] = HEX2ASC(md5_buf[count] & 0x0f); } buffer[32] = '\0'; } /* Reads the ASCII representation of an MD5 digest. The buffer must * be at least 32 characters long. */ void ascii_to_md5(const char *buffer, unsigned char md5_buf[16]) { int count; for (count = 0; count<16; count++) { md5_buf[count] = ((ASC2HEX(buffer[count*2])) << 4) | ASC2HEX(buffer[count*2+1]); } } char *ne_utf8_encode(const char *str) { char *buffer = ne_malloc(strlen(str) * 2 + 1); int in, len = strlen(str); char *out; for (in = 0, out = buffer; in < len; in++, out++) { if ((unsigned char)str[in] <= 0x7D) { *out = str[in]; } else { *out++ = 0xC0 | ((str[in] & 0xFC) >> 6); *out = str[in] & 0xBF; } } /* nul-terminate */ *out = '\0'; return buffer; } #ifdef SPLIT_STRING_TEST #include int main(int argc, char *argv[]) { char *str, sep, **comps, *wspace, *quotes; int count; if (argc < 3) { printf("Usage: split_string [whitespace] [quotes]\n"); return -1; } sep = *argv[1]; str = argv[2]; if (argc > 3) { wspace = argv[3]; } else { wspace = " "; } if (argc > 4) { quotes = argv[4]; } else { quotes = "\""; } printf("String: [%s] Separator: `%c' Whitespace: [%s] Quotes: [%s]\n", str, sep, wspace, quotes); comps = split_string(str, sep, quotes, wspace); count = 0; do { printf("Component #%d: [%s]\n", count, comps[count]); } while (comps[++count] != NULL); return 0; } #endif #ifdef PAIR_STRING_TEST #include int main(int argc, char *argv[]) { char *str, compsep, kvsep, **comps, *wspace, *quotes; int count; if (argc < 4) { printf("Usage: pair_string [whitespace] [quotes]\n"); return -1; } compsep = *argv[1]; kvsep = *argv[2]; str = argv[3]; if (argc > 4) { wspace = argv[4]; } else { wspace = " "; } if (argc > 5) { quotes = argv[5]; } else { quotes = "\""; } printf("String: [%s] CompSep: `%c' K/VSep: `%c'\nWhitespace: [%s] Quotes: [%s]\n", str, compsep, kvsep, wspace, quotes); comps = pair_string(str, compsep, kvsep, quotes, wspace); count = 0; do { printf("Component #%d: Key [%s] Value [%s]\n", count, comps[count], comps[count+1]); } while (comps[(count+=2)] != NULL); return 0; } #endif /* variables: * * Local variables: * compile-command: "gcc -g -O2 -Wall -I.. -ansi -DHAVE_CONFIG_H -DSPLIT_STRING_TEST -o split_string string_utils.c" * End: */