summaryrefslogtreecommitdiff
path: root/src/ne_string.c
diff options
context:
space:
mode:
authorjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2004-10-02 18:47:02 +0000
committerjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2004-10-02 18:47:02 +0000
commit0294ff3d3282d1b1c5497f00ea25e5e55e6f4338 (patch)
tree978af6f81c7b7715597871b1e89a9ad083907f1a /src/ne_string.c
downloadneon-0294ff3d3282d1b1c5497f00ea25e5e55e6f4338.tar.gz
Import neon 0.24.0 to begin 0.24.x branch.
git-svn-id: http://svn.webdav.org/repos/projects/neon/branches/0.24.x@243 61a7d7f5-40b7-0310-9c16-bb0ea8cb1845
Diffstat (limited to 'src/ne_string.c')
-rw-r--r--src/ne_string.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/src/ne_string.c b/src/ne_string.c
new file mode 100644
index 0000000..3cddad9
--- /dev/null
+++ b/src/ne_string.c
@@ -0,0 +1,519 @@
+/*
+ String utility functions
+ Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+
+ 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
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <ctype.h> /* for isprint() etc in ne_strclean() */
+
+#include "ne_alloc.h"
+#include "ne_string.h"
+
+char *ne_token(char **str, char separator)
+{
+ char *ret = *str, *pnt = strchr(*str, separator);
+
+ if (pnt) {
+ *pnt = '\0';
+ *str = pnt + 1;
+ } else {
+ /* no separator found: return end of string. */
+ *str = NULL;
+ }
+
+ return ret;
+}
+
+char *ne_qtoken(char **str, char separator, const char *quotes)
+{
+ char *pnt, *ret = NULL;
+
+ for (pnt = *str; *pnt != '\0'; pnt++) {
+ char *quot = strchr(quotes, *pnt);
+
+ if (quot) {
+ char *qclose = strchr(pnt+1, *quot);
+
+ if (!qclose) {
+ /* no closing quote: invalid string. */
+ return NULL;
+ }
+
+ pnt = qclose;
+ } else if (*pnt == separator) {
+ /* found end of token. */
+ *pnt = '\0';
+ ret = *str;
+ *str = pnt + 1;
+ return ret;
+ }
+ }
+
+ /* no separator found: return end of string. */
+ ret = *str;
+ *str = NULL;
+ return ret;
+}
+
+char *ne_shave(char *str, const char *whitespace)
+{
+ char *pnt, *ret = str;
+
+ while (*ret != '\0' && strchr(whitespace, *ret) != NULL) {
+ ret++;
+ }
+
+ /* pnt points at the NUL terminator. */
+ pnt = &ret[strlen(ret)];
+
+ while (pnt > ret && strchr(whitespace, *(pnt-1)) != NULL) {
+ pnt--;
+ }
+
+ *pnt = '\0';
+ return ret;
+}
+
+/* TODO: deprecate all these and use ne_token() instead. */
+
+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;
+ }
+ ne_free(comps);
+ pairs[2*count] = pairs[2*count+1] = NULL;
+ return pairs;
+}
+
+void split_string_free(char **components)
+{
+ char **pnt = components;
+ while (*pnt != NULL) {
+ ne_free(*pnt);
+ pnt++;
+ }
+ ne_free(components);
+}
+
+void pair_string_free(char **pairs)
+{
+ int n;
+ for (n = 0; pairs[n] != NULL; n+=2) {
+ ne_free(pairs[n]);
+ }
+ ne_free(pairs);
+}
+
+void ne_buffer_clear(ne_buffer *buf)
+{
+ memset(buf->data, 0, buf->length);
+ buf->used = 1;
+}
+
+/* Grows for given size, returns 0 on success, -1 on error. */
+void ne_buffer_grow(ne_buffer *buf, size_t newsize)
+{
+#define NE_BUFFER_GROWTH 512
+ if (newsize > buf->length) {
+ /* If it's not big enough already... */
+ buf->length = ((newsize / NE_BUFFER_GROWTH) + 1) * NE_BUFFER_GROWTH;
+
+ /* Reallocate bigger buffer */
+ buf->data = ne_realloc(buf->data, buf->length);
+ }
+}
+
+static size_t count_concat(va_list *ap)
+{
+ size_t total = 0;
+ char *next;
+
+ while ((next = va_arg(*ap, char *)) != NULL)
+ total += strlen(next);
+
+ return total;
+}
+
+static void do_concat(char *str, va_list *ap)
+{
+ char *next;
+
+ while ((next = va_arg(*ap, char *)) != NULL) {
+#ifdef HAVE_STPCPY
+ str = stpcpy(str, next);
+#else
+ size_t len = strlen(next);
+ memcpy(str, next, len);
+ str += len;
+#endif
+ }
+}
+
+void ne_buffer_concat(ne_buffer *buf, ...)
+{
+ va_list ap;
+ ssize_t total;
+
+ va_start(ap, buf);
+ total = buf->used + count_concat(&ap);
+ va_end(ap);
+
+ /* Grow the buffer */
+ ne_buffer_grow(buf, total);
+
+ va_start(ap, buf);
+ do_concat(buf->data + buf->used - 1, &ap);
+ va_end(ap);
+
+ buf->used = total;
+ buf->data[total - 1] = '\0';
+}
+
+char *ne_concat(const char *str, ...)
+{
+ va_list ap;
+ size_t total, slen = strlen(str);
+ char *ret;
+
+ va_start(ap, str);
+ total = slen + count_concat(&ap);
+ va_end(ap);
+
+ ret = memcpy(ne_malloc(total + 1), str, slen);
+
+ va_start(ap, str);
+ do_concat(ret + slen, &ap);
+ va_end(ap);
+
+ ret[total] = '\0';
+ return ret;
+}
+
+/* Append zero-terminated string... returns 0 on success or -1 on
+ * realloc failure. */
+void ne_buffer_zappend(ne_buffer *buf, const char *str)
+{
+ ne_buffer_append(buf, str, strlen(str));
+}
+
+void ne_buffer_append(ne_buffer *buf, const char *data, size_t len)
+{
+ ne_buffer_grow(buf, buf->used + len);
+ memcpy(buf->data + buf->used - 1, data, len);
+ buf->used += len;
+ buf->data[buf->used - 1] = '\0';
+}
+
+ne_buffer *ne_buffer_create(void)
+{
+ return ne_buffer_ncreate(512);
+}
+
+ne_buffer *ne_buffer_ncreate(size_t s)
+{
+ ne_buffer *buf = ne_malloc(sizeof(*buf));
+ buf->data = ne_malloc(s);
+ buf->data[0] = '\0';
+ buf->length = s;
+ buf->used = 1;
+ return buf;
+}
+
+void ne_buffer_destroy(ne_buffer *buf)
+{
+ ne_free(buf->data);
+ ne_free(buf);
+}
+
+char *ne_buffer_finish(ne_buffer *buf)
+{
+ char *ret = buf->data;
+ ne_free(buf);
+ return ret;
+}
+
+void ne_buffer_altered(ne_buffer *buf)
+{
+ buf->used = strlen(buf->data) + 1;
+}
+
+static const char *b64_alphabet =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/=";
+
+char *ne_base64(const unsigned char *text, size_t inlen)
+{
+ /* The tricky thing about this is doing the padding at the end,
+ * doing the bit manipulation requires a bit of concentration only */
+ char *buffer, *point;
+ size_t outlen;
+
+ /* Use 'buffer' to store the output. Work out how big it should be...
+ * This must be a multiple of 4 bytes */
+
+ outlen = (inlen*4)/3;
+ if ((inlen % 3) > 0) /* got to pad */
+ outlen += 4 - (inlen % 3);
+
+ buffer = ne_malloc(outlen + 1); /* +1 for the \0 */
+
+ /* now do the main stage of conversion, 3 bytes at a time,
+ * leave the trailing bytes (if there are any) for later */
+
+ for (point=buffer; inlen>=3; inlen-=3, text+=3) {
+ *(point++) = b64_alphabet[ (*text)>>2 ];
+ *(point++) = b64_alphabet[ ((*text)<<4 & 0x30) | (*(text+1))>>4 ];
+ *(point++) = b64_alphabet[ ((*(text+1))<<2 & 0x3c) | (*(text+2))>>6 ];
+ *(point++) = b64_alphabet[ (*(text+2)) & 0x3f ];
+ }
+
+ /* Now deal with the trailing bytes */
+ if (inlen > 0) {
+ /* We always have one trailing byte */
+ *(point++) = b64_alphabet[ (*text)>>2 ];
+ *(point++) = b64_alphabet[ (((*text)<<4 & 0x30) |
+ (inlen==2?(*(text+1))>>4:0)) ];
+ *(point++) = (inlen==1?'=':b64_alphabet[ (*(text+1))<<2 & 0x3c ]);
+ *(point++) = '=';
+ }
+
+ /* Null-terminate */
+ *point = '\0';
+
+ return buffer;
+}
+
+/* VALID_B64: fail if 'ch' is not a valid base64 character */
+#define VALID_B64(ch) (((ch) >= 'A' && (ch) <= 'Z') || \
+ ((ch) >= 'a' && (ch) <= 'z') || \
+ ((ch) >= '0' && (ch) <= '9') || \
+ (ch) == '/' || (ch) == '+' || (ch) == '=')
+
+/* DECODE_B64: decodes a valid base64 character. */
+#define DECODE_B64(ch) ((ch) >= 'a' ? ((ch) + 26 - 'a') : \
+ ((ch) >= 'A' ? ((ch) - 'A') : \
+ ((ch) >= '0' ? ((ch) + 52 - '0') : \
+ ((ch) == '+' ? 62 : 63))))
+
+size_t ne_unbase64(const char *data, unsigned char **out)
+{
+ size_t inlen = strlen(data);
+ unsigned char *outp;
+ const unsigned char *in;
+
+ if (inlen == 0 || (inlen % 4) != 0) return 0;
+
+ outp = *out = ne_malloc(inlen * 3 / 4);
+
+ for (in = (const unsigned char *)data; *in; in += 4) {
+ unsigned int tmp;
+ if (!VALID_B64(in[0]) || !VALID_B64(in[1]) || !VALID_B64(in[2]) ||
+ !VALID_B64(in[3]) || in[0] == '=' || in[1] == '=' ||
+ (in[2] == '=' && in[3] != '=')) {
+ ne_free(*out);
+ return 0;
+ }
+ tmp = (DECODE_B64(in[0]) & 0x3f) << 18 |
+ (DECODE_B64(in[1]) & 0x3f) << 12;
+ *outp++ = (tmp >> 16) & 0xff;
+ if (in[2] != '=') {
+ tmp |= (DECODE_B64(in[2]) & 0x3f) << 6;
+ *outp++ = (tmp >> 8) & 0xff;
+ if (in[3] != '=') {
+ tmp |= DECODE_B64(in[3]) & 0x3f;
+ *outp++ = tmp & 0xff;
+ }
+ }
+ }
+
+ return outp - *out;
+}
+
+char *ne_strclean(char *str)
+{
+ char *pnt;
+ for (pnt = str; *pnt; pnt++)
+ if (iscntrl(*pnt) || !isprint(*pnt)) *pnt = ' ';
+ return str;
+}
+
+char *ne_strerror(int errnum, char *buf, size_t buflen)
+{
+#ifdef HAVE_STRERROR_R
+#ifdef STRERROR_R_CHAR_P
+ /* glibc-style strerror_r which may-or-may-not use provided buffer. */
+ char *ret = strerror_r(errnum, buf, buflen);
+ if (ret != buf)
+ ne_strnzcpy(buf, ret, buflen);
+#else /* POSIX-style strerror_r: */
+ strerror_r(errnum, buf, buflen);
+#endif
+#else /* no strerror_r: */
+ ne_strnzcpy(buf, strerror(errnum), buflen);
+#endif
+ return buf;
+}