blob: 02b796cdec360390313d55271fe15a4f1204070c [file] [log] [blame]
/*
* svn_string.h: routines to manipulate counted-length strings
* (svn_stringbuf_t and svn_string_t) and C strings.
*
*
* ====================================================================
* Copyright (c) 2000-2002 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <string.h> /* for memcpy(), memcmp(), strlen() */
#include <apr_lib.h> /* for apr_isspace() */
#include "svn_string.h" /* loads "svn_types.h" and <apr_pools.h> */
/* Our own realloc, since APR doesn't have one. Note: this is a
generic realloc for memory pools, *not* for strings. */
static void *
my__realloc (char *data, const apr_size_t oldsize, const apr_size_t request,
apr_pool_t *pool)
{
void *new_area;
/* kff todo: it's a pity APR doesn't give us this -- sometimes it
could realloc the block merely by extending in place, sparing us
a memcpy(), but only the pool would know enough to be able to do
this. We should add a realloc() to APR if someone hasn't
already. */
/* malloc new area */
new_area = apr_palloc (pool, request);
/* copy data to new area */
memcpy (new_area, data, oldsize);
/* I'm NOT freeing old area here -- cuz we're using pools, ugh. */
/* return new area */
return new_area;
}
/* svn_string functions */
static svn_string_t *
create_string (const char *data, apr_size_t size,
apr_pool_t *pool)
{
svn_string_t *new_string;
new_string = (svn_string_t *) apr_palloc (pool, sizeof (*new_string));
new_string->data = data;
new_string->len = size;
return new_string;
}
svn_string_t *
svn_string_ncreate (const char *bytes, const apr_size_t size,
apr_pool_t *pool)
{
char *data;
data = apr_palloc (pool, size + 1);
memcpy (data, bytes, size);
/* Null termination is the convention -- even if we suspect the data
to be binary, it's not up to us to decide, it's the caller's
call. Heck, that's why they call it the caller! */
data[size] = '\0';
/* wrap an svn_string_t around the new data */
return create_string (data, size, pool);
}
svn_string_t *
svn_string_create (const char *cstring, apr_pool_t *pool)
{
return svn_string_ncreate (cstring, strlen (cstring), pool);
}
svn_string_t *
svn_string_create_from_buf (const svn_stringbuf_t *strbuf, apr_pool_t *pool)
{
return svn_string_ncreate (strbuf->data, strbuf->len, pool);
}
svn_string_t *
svn_string_createv (apr_pool_t *pool, const char *fmt, va_list ap)
{
char *data = apr_pvsprintf (pool, fmt, ap);
/* wrap an svn_string_t around the new data */
return create_string (data, strlen (data), pool);
}
svn_string_t *
svn_string_createf (apr_pool_t *pool, const char *fmt, ...)
{
svn_string_t *str;
va_list ap;
va_start (ap, fmt);
str = svn_string_createv (pool, fmt, ap);
va_end (ap);
return str;
}
svn_boolean_t
svn_string_isempty (const svn_string_t *str)
{
return (str->len == 0);
}
svn_string_t *
svn_string_dup (const svn_string_t *original_string, apr_pool_t *pool)
{
return (svn_string_ncreate (original_string->data,
original_string->len, pool));
}
svn_boolean_t
svn_string_compare (const svn_string_t *str1, const svn_string_t *str2)
{
/* easy way out :) */
if (str1->len != str2->len)
return FALSE;
/* now that we know they have identical lengths... */
if (memcmp (str1->data, str2->data, str1->len))
return FALSE;
else
return TRUE;
}
apr_size_t
svn_string_first_non_whitespace (const svn_string_t *str)
{
apr_size_t i;
for (i = 0; i < str->len; i++)
{
if (! apr_isspace (str->data[i]))
{
return i;
}
}
/* if we get here, then the string must be entirely whitespace */
return (-1);
}
void
svn_string_strip_whitespace (svn_string_t *str)
{
apr_size_t i;
/* Find first non-whitespace character */
apr_size_t offset = svn_string_first_non_whitespace (str);
/* Go ahead! Waste some RAM, we've got pools! :) */
str->data += offset;
str->len -= offset;
/* Can't adjust `blocksize' here, because svn_string_t does not
record a blocksize independent of len, thus can't be
realloc'd. */
/* Now that we've chomped whitespace off the front, search backwards
from the end for the first non-whitespace. */
for (i = (str->len - 1); i >= 0; i--)
{
if (! apr_isspace (str->data[i]))
{
break;
}
}
/* Mmm, waste some more RAM */
str->len = i + 1;
/* ### In svn_stringbuf_strip_whitespace, we reset the null
terminator here. But svn_string_t can have const data, so I
don't think we can do that, unfortunately. */
}
apr_size_t
svn_string_find_char_backward (const svn_string_t *str, char ch)
{
int i; /* signed! */
for (i = (str->len - 1); i >= 0; i--)
{
if (str->data[i] == ch)
return i;
}
return str->len;
}
/* svn_stringbuf functions */
static svn_stringbuf_t *
create_stringbuf (char *data, apr_size_t size, apr_pool_t *pool)
{
svn_stringbuf_t *new_string;
new_string = (svn_stringbuf_t *) apr_palloc (pool, sizeof (*new_string));
new_string->data = data;
new_string->len = size;
new_string->blocksize = size + 1; /* we know there is a null-term */
new_string->pool = pool;
return new_string;
}
svn_stringbuf_t *
svn_stringbuf_ncreate (const char *bytes, const apr_size_t size,
apr_pool_t *pool)
{
char *data;
data = apr_palloc (pool, size + 1);
memcpy (data, bytes, size);
/* Null termination is the convention -- even if we suspect the data
to be binary, it's not up to us to decide, it's the caller's
call. Heck, that's why they call it the caller! */
data[size] = '\0';
/* wrap an svn_stringbuf_t around the new data */
return create_stringbuf (data, size, pool);
}
svn_stringbuf_t *
svn_stringbuf_create (const char *cstring, apr_pool_t *pool)
{
return svn_stringbuf_ncreate (cstring, strlen (cstring), pool);
}
svn_stringbuf_t *
svn_stringbuf_create_from_string (const svn_string_t *str, apr_pool_t *pool)
{
return svn_stringbuf_ncreate (str->data, str->len, pool);
}
svn_stringbuf_t *
svn_stringbuf_createv (apr_pool_t *pool, const char *fmt, va_list ap)
{
char *data = apr_pvsprintf (pool, fmt, ap);
/* wrap an svn_stringbuf_t around the new data */
return create_stringbuf (data, strlen (data), pool);
}
svn_stringbuf_t *
svn_stringbuf_createf (apr_pool_t *pool, const char *fmt, ...)
{
svn_stringbuf_t *str;
va_list ap;
va_start (ap, fmt);
str = svn_stringbuf_createv (pool, fmt, ap);
va_end (ap);
return str;
}
void
svn_stringbuf_fillchar (svn_stringbuf_t *str, const unsigned char c)
{
memset (str->data, c, str->len);
}
void
svn_stringbuf_set (svn_stringbuf_t *str, const char *value)
{
apr_size_t amt = strlen (value);
svn_stringbuf_ensure (str, amt + 1);
memcpy (str->data, value, amt + 1);
str->len = amt;
}
void
svn_stringbuf_setempty (svn_stringbuf_t *str)
{
if (str->len > 0)
str->data[0] = '\0';
str->len = 0;
}
void
svn_stringbuf_chop (svn_stringbuf_t *str, apr_size_t nbytes)
{
if (nbytes > str->len)
str->len = 0;
else
str->len -= nbytes;
str->data[str->len] = '\0';
}
svn_boolean_t
svn_stringbuf_isempty (const svn_stringbuf_t *str)
{
return (str->len == 0);
}
void
svn_stringbuf_ensure (svn_stringbuf_t *str, apr_size_t minimum_size)
{
/* Keep doubling capacity until have enough. */
if (str->blocksize < minimum_size)
{
if (str->blocksize == 0)
str->blocksize = minimum_size;
else
while (str->blocksize < minimum_size)
str->blocksize *= 2;
str->data = (char *) my__realloc (str->data,
str->len,
str->blocksize,
str->pool);
}
}
void
svn_stringbuf_appendbytes (svn_stringbuf_t *str, const char *bytes,
const apr_size_t count)
{
apr_size_t total_len;
void *start_address;
total_len = str->len + count; /* total size needed */
/* +1 for null terminator. */
svn_stringbuf_ensure (str, (total_len + 1));
/* get address 1 byte beyond end of original bytestring */
start_address = (str->data + str->len);
memcpy (start_address, (void *) bytes, count);
str->len = total_len;
str->data[str->len] = '\0'; /* We don't know if this is binary
data or not, but convention is
to null-terminate. */
}
void
svn_stringbuf_appendstr (svn_stringbuf_t *targetstr,
const svn_stringbuf_t *appendstr)
{
svn_stringbuf_appendbytes (targetstr, appendstr->data, appendstr->len);
}
void
svn_stringbuf_appendcstr (svn_stringbuf_t *targetstr, const char *cstr)
{
svn_stringbuf_appendbytes (targetstr, cstr, strlen(cstr));
}
svn_stringbuf_t *
svn_stringbuf_dup (const svn_stringbuf_t *original_string, apr_pool_t *pool)
{
return (svn_stringbuf_ncreate (original_string->data,
original_string->len, pool));
}
svn_boolean_t
svn_stringbuf_compare (const svn_stringbuf_t *str1,
const svn_stringbuf_t *str2)
{
/* easy way out :) */
if (str1->len != str2->len)
return FALSE;
/* now that we know they have identical lengths... */
if (memcmp (str1->data, str2->data, str1->len))
return FALSE;
else
return TRUE;
}
apr_size_t
svn_stringbuf_first_non_whitespace (const svn_stringbuf_t *str)
{
apr_size_t i;
for (i = 0; i < str->len; i++)
{
if (! apr_isspace (str->data[i]))
{
return i;
}
}
/* if we get here, then the string must be entirely whitespace */
return (-1);
}
void
svn_stringbuf_strip_whitespace (svn_stringbuf_t *str)
{
apr_size_t i;
/* Find first non-whitespace character */
apr_size_t offset = svn_stringbuf_first_non_whitespace (str);
/* Go ahead! Waste some RAM, we've got pools! :) */
str->data += offset;
str->len -= offset;
str->blocksize -= offset;
/* Now that we've chomped whitespace off the front, search backwards
from the end for the first non-whitespace. */
for (i = (str->len - 1); i >= 0; i--)
{
if (! apr_isspace (str->data[i]))
{
break;
}
}
/* Mmm, waste some more RAM */
str->len = i + 1;
str->data[str->len] = '\0';
}
apr_size_t
svn_stringbuf_find_char_backward (const svn_stringbuf_t *str, char ch)
{
int i; /* signed! */
for (i = (str->len - 1); i >= 0; i--)
{
if (str->data[i] == ch)
return i;
}
return str->len;
}
apr_size_t
svn_stringbuf_chop_back_to_char (svn_stringbuf_t *str, char ch)
{
apr_size_t i = svn_stringbuf_find_char_backward (str, ch);
if (i < str->len)
{
apr_size_t nbytes = (str->len - i);
svn_stringbuf_chop (str, nbytes);
return nbytes;
}
return 0;
}
svn_boolean_t
svn_string_compare_stringbuf (const svn_string_t *str1,
const svn_stringbuf_t *str2)
{
/* easy way out :) */
if (str1->len != str2->len)
return FALSE;
/* now that we know they have identical lengths... */
if (memcmp (str1->data, str2->data, str1->len))
return FALSE;
else
return TRUE;
}
/*** C string stuff. ***/
apr_array_header_t *
svn_cstring_split (const char *input,
char sep_char,
svn_boolean_t chop_whitespace,
apr_pool_t *pool)
{
const char *b = input, *e = input;
svn_boolean_t one_last_time = (! *e);
apr_array_header_t *substrings = apr_array_make (pool, 1, sizeof (input));
while (1)
{
if (chop_whitespace && (apr_isspace (*b)))
{
b++;
if (e < b)
e = b;
}
else if ((*e == sep_char) || (*e == '\0'))
{
const char *e2 = e;
if (chop_whitespace)
{
if (e2 > b)
{
while (apr_isspace (*(--e2)))
;
e2++;
}
}
*((char **) (apr_array_push (substrings)))
= apr_pstrmemdup (pool, b, e2 - b);
b = ++e;
}
else
e++;
if (one_last_time)
break;
if (*e == '\0')
one_last_time = TRUE;
}
return substrings;
}
/* --------------------------------------------------------------
* local variables:
* eval: (load-file "../../tools/dev/svn-dev.el")
* end:
*/