aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2007-04-19 01:07:41 +0000
committerMike Frysinger <vapier@gentoo.org>2007-04-19 01:07:41 +0000
commita71ecdff555f477135798a8f49c35de172fdc7c2 (patch)
tree68ebd70e8b27a79dec11d7ecde4fed2c94ac2e8a /libsbutil/src
parentINSTALL: update to current autoconf (diff)
downloadsandbox-a71ecdff555f477135798a8f49c35de172fdc7c2.tar.gz
sandbox-a71ecdff555f477135798a8f49c35de172fdc7c2.tar.bz2
sandbox-a71ecdff555f477135798a8f49c35de172fdc7c2.zip
libsbutil: drop the svn external links
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Diffstat (limited to 'libsbutil/src')
-rw-r--r--libsbutil/src/Makefile.am19
-rw-r--r--libsbutil/src/config.c244
-rw-r--r--libsbutil/src/debug.c335
-rw-r--r--libsbutil/src/dynbuf.c488
-rw-r--r--libsbutil/src/file.c488
-rw-r--r--libsbutil/src/librcutil.map68
-rw-r--r--libsbutil/src/simple-regex.c870
-rw-r--r--libsbutil/src/string.c108
8 files changed, 2620 insertions, 0 deletions
diff --git a/libsbutil/src/Makefile.am b/libsbutil/src/Makefile.am
new file mode 100644
index 0000000..086764b
--- /dev/null
+++ b/libsbutil/src/Makefile.am
@@ -0,0 +1,19 @@
+AUTOMAKE_OPTIONS = foreign
+
+INCLUDES = \
+ -I$(top_srcdir)/include \
+ $(RCSCRIPTS_DEFINES)
+
+lib_LTLIBRARIES = librcutil.la
+
+librcutil_la_LDFLAGS = \
+ -Wl,--version-script,$(srcdir)/librcutil.map
+librcutil_la_SOURCES = \
+ debug.c \
+ string.c \
+ file.c \
+ config.c \
+ dynbuf.c \
+ simple-regex.c
+
+EXTRA_DIST = librcutil.map
diff --git a/libsbutil/src/config.c b/libsbutil/src/config.c
new file mode 100644
index 0000000..0778ad3
--- /dev/null
+++ b/libsbutil/src/config.c
@@ -0,0 +1,244 @@
+/*
+ * misc.c
+ *
+ * Miscellaneous macro's and functions.
+ *
+ * Copyright 2004-2007 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "rcscripts/rcutil.h"
+
+/* This handles simple 'entry="bar"' type variables. If it is more complex
+ * ('entry="$(pwd)"' or such), it will obviously not work, but current behaviour
+ * should be fine for the type of variables we want. */
+char *
+rc_get_cnf_entry (const char *pathname, const char *entry, const char *sep)
+{
+ rc_dynbuf_t *dynbuf = NULL;
+ char *buf = NULL;
+ char *str_ptr;
+ char *value = NULL;
+ char *token;
+
+ rc_errno_save ();
+
+ if ((!check_arg_str (pathname)) || (!check_arg_str (entry)))
+ return NULL;
+
+ /* If it is not a file or symlink pointing to a file, bail */
+ if (!rc_is_file (pathname, TRUE))
+ {
+ rc_errno_set (ENOENT);
+ DBG_MSG ("'%s' is not a file or do not exist!\n", pathname);
+ return NULL;
+ }
+
+ if (0 == rc_get_size (pathname, TRUE))
+ {
+ /* XXX: Should we set errno here ? */
+ DBG_MSG ("'%s' have a size of 0!\n", pathname);
+ return NULL;
+ }
+
+ dynbuf = rc_dynbuf_new_mmap_file (pathname);
+ if (NULL == dynbuf)
+ {
+ DBG_MSG ("Could not open config file for reading!\n");
+ return NULL;
+ }
+
+ /* Make sure we do not get false positives below */
+ rc_errno_clear ();
+ while (NULL != (buf = rc_dynbuf_read_line (dynbuf)))
+ {
+ str_ptr = buf;
+
+ /* Strip leading spaces/tabs */
+ while ((str_ptr[0] == ' ') || (str_ptr[0] == '\t'))
+ str_ptr++;
+
+ /* Get entry and value */
+ token = strsep (&str_ptr, "=");
+ /* Bogus entry or value */
+ if (NULL == token)
+ goto _continue;
+
+ /* Make sure we have a string that is larger than 'entry', and
+ * the first part equals 'entry' */
+ if ((strlen (token) > 0) && (0 == strcmp (entry, token)))
+ {
+ do
+ {
+ /* Bash variables are usually quoted */
+ token = strsep (&str_ptr, "\"\'");
+ /* If quoted, the first match will be "" */
+ }
+ while ((NULL != token) && (0 == strlen (token)));
+
+ /* We have a 'entry='. We respect bash rules, so NULL
+ * value for now (if not already) */
+ if (NULL == token)
+ {
+ /* We might have 'entry=' and later 'entry="bar"',
+ * so just continue for now ... we will handle
+ * it below when 'value == NULL' */
+ if ((!check_str(sep)) && (NULL != value))
+ {
+ free (value);
+ value = NULL;
+ }
+ goto _continue;
+ }
+
+ if ((!check_str(sep)) ||
+ ((check_str(sep)) && (NULL == value)))
+ {
+ /* If we have already allocated 'value', free it */
+ if (NULL != value)
+ free (value);
+
+ value = xstrndup (token, strlen (token));
+ if (NULL == value)
+ {
+ rc_dynbuf_free (dynbuf);
+ free (buf);
+
+ return NULL;
+ }
+ }
+ else
+ {
+ value = xrealloc (value, strlen(value) + strlen(token) +
+ strlen(sep) + 1);
+ if (NULL == value)
+ {
+ rc_dynbuf_free (dynbuf);
+ free (buf);
+
+ return NULL;
+ }
+ snprintf(value + strlen(value), strlen(token) + strlen(sep) + 1,
+ "%s%s", sep, token);
+ }
+
+ /* We do not break, as there might be more than one entry
+ * defined, and as bash uses the last, so should we */
+ /* break; */
+ }
+
+_continue:
+ free (buf);
+ }
+
+ /* rc_dynbuf_read_line() returned NULL with errno set */
+ if ((NULL == buf) && (rc_errno_is_set ()))
+ {
+ DBG_MSG ("Failed to read line from dynamic buffer!\n");
+ rc_dynbuf_free (dynbuf);
+ if (NULL != value)
+ free (value);
+
+ return NULL;
+ }
+
+
+ if (NULL == value)
+ /* NULL without errno set means everything went OK, but we did not get a
+ * entry */
+ DBG_MSG ("Failed to get value for config entry '%s'!\n", entry);
+
+ rc_dynbuf_free (dynbuf);
+
+ rc_errno_restore ();
+
+ return value;
+}
+
+char **
+rc_get_list_file (char **list, char *filename)
+{
+ rc_dynbuf_t *dynbuf = NULL;
+ char *buf = NULL;
+ char *tmp_p = NULL;
+ char *token = NULL;
+
+ rc_errno_save ();
+
+ if (!check_arg_str (filename))
+ return NULL;
+
+ dynbuf = rc_dynbuf_new_mmap_file (filename);
+ if (NULL == dynbuf)
+ return NULL;
+
+ /* Make sure we do not get false positives below */
+ rc_errno_clear ();
+ while (NULL != (buf = rc_dynbuf_read_line (dynbuf)))
+ {
+ tmp_p = buf;
+
+ /* Strip leading spaces/tabs */
+ while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
+ tmp_p++;
+
+ /* Get entry - we do not want comments, and only the first word
+ * on a line is valid */
+ token = strsep (&tmp_p, "# \t");
+ if (check_str (token))
+ {
+ tmp_p = xstrndup (token, strlen (token));
+ if (NULL == tmp_p)
+ {
+ if (NULL != list)
+ str_list_free (list);
+ rc_dynbuf_free (dynbuf);
+ free (buf);
+
+ return NULL;
+ }
+
+ str_list_add_item (list, tmp_p, error);
+ }
+
+ free (buf);
+ }
+
+ /* rc_dynbuf_read_line() returned NULL with errno set */
+ if ((NULL == buf) && (rc_errno_is_set ()))
+ {
+ DBG_MSG ("Failed to read line from dynamic buffer!\n");
+error:
+ if (NULL != list)
+ str_list_free (list);
+ rc_dynbuf_free (dynbuf);
+
+ return NULL;
+ }
+
+ rc_dynbuf_free (dynbuf);
+
+ rc_errno_restore ();
+
+ return list;
+}
+
diff --git a/libsbutil/src/debug.c b/libsbutil/src/debug.c
new file mode 100644
index 0000000..7fae087
--- /dev/null
+++ b/libsbutil/src/debug.c
@@ -0,0 +1,335 @@
+/*
+ * debug.c
+ *
+ * Simle debugging/logging macro's and functions.
+ *
+ * Copyright 2004-2007 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "rcscripts/rcutil.h"
+
+volatile static bool debug_enabled = TRUE;
+volatile static int debug_errno = 0;
+
+static char log_domain_default[] = "rcscripts";
+volatile static char *log_domain = log_domain_default;
+
+void
+rc_log_domain (const char *new_domain)
+{
+ if (check_str (new_domain))
+ log_domain = (char *)new_domain;
+}
+
+void
+rc_debug_enabled (bool enabled)
+{
+ debug_enabled = enabled;
+}
+
+void
+rc_errno_set (int rc_errno)
+{
+ if (rc_errno >= 0)
+ debug_errno = rc_errno;
+}
+
+void
+rc_errno_clear (void)
+{
+ debug_errno = 0;
+}
+
+int
+rc_errno_get (void)
+{
+ return debug_errno;
+}
+
+bool
+rc_errno_is_set (void)
+{
+ return (debug_errno > 0);
+}
+
+void
+debug_message (const char *file, const char *func, int line,
+ const char *format, ...)
+{
+ va_list arg;
+ char *format_str;
+ int length;
+
+#if !defined(RC_DEBUG)
+ if (!debug_enabled)
+ return;
+#endif
+
+ length = strlen (log_domain) + strlen ("(): ") + 1;
+ /* Do not use xmalloc() here, else we may have recursive issues */
+ format_str = malloc (length);
+ if (NULL == format_str)
+ {
+ fprintf (stderr, "(%s) error: in %s, function %s(), line %i:\n",
+ log_domain, __FILE__, __FUNCTION__, __LINE__);
+ fprintf (stderr, "(%s) Failed to allocate buffer!\n",
+ log_domain);
+ abort ();
+ }
+
+ snprintf (format_str, length, "(%s) ", log_domain);
+
+ va_start (arg, format);
+
+#if !defined(RC_DEBUG)
+ /* Bit of a hack, as how we do things tend to cause seek
+ * errors when reading the parent/child pipes */
+ /* if ((0 != errno) && (ESPIPE != errno)) { */
+ if (rc_errno_is_set ())
+ {
+#endif
+ if (rc_errno_is_set ())
+ fprintf (stderr, "(%s) error: ", log_domain);
+ else
+ fprintf (stderr, "(%s) debug: ", log_domain);
+
+ fprintf (stderr, "in %s, function %s(), line %i:\n", file, func, line);
+
+ if (rc_errno_is_set ())
+ fprintf (stderr, "%s strerror() = '%s'\n", format_str, strerror (rc_errno_get ()));
+
+ fprintf (stderr, "%s ", format_str);
+ vfprintf (stderr, format, arg);
+#if !defined(RC_DEBUG)
+ }
+#endif
+
+ va_end (arg);
+
+ free (format_str);
+}
+
+inline bool
+check_ptr (const void *ptr)
+{
+ if (NULL == ptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+check_str (const char *str)
+{
+ if ((NULL == str) || (0 == strlen (str)))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+check_strv (char **str)
+{
+ if ((NULL == str) || (NULL == *str) || (0 == strlen (*str)))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+check_fd (int fd)
+{
+ if ((0 >= fd) || (-1 == fcntl (fd, F_GETFL)))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+check_fp (FILE *fp)
+{
+ if ((NULL == fp) || (-1 == fileno (fp)))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_ptr (const void *ptr, const char *file, const char *func, size_t line)
+{
+ if (!check_ptr (ptr))
+ {
+ rc_errno_set (EINVAL);
+
+ debug_message (file, func, line, "Invalid pointer passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_str (const char *str, const char *file, const char *func, size_t line)
+{
+ if (!check_str (str))
+ {
+ rc_errno_set (EINVAL);
+
+ debug_message (file, func, line, "Invalid string passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_strv (char **str, const char *file, const char *func, size_t line)
+{
+ if (!check_strv (str))
+ {
+ rc_errno_set (EINVAL);
+
+ debug_message (file, func, line, "Invalid string array passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_fd (int fd, const char *file, const char *func, size_t line)
+{
+ if (!check_fd (fd))
+ {
+ rc_errno_set (EBADF);
+
+ debug_message (file, func, line, "Invalid file descriptor passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_fp (FILE *fp, const char *file, const char *func, size_t line)
+{
+ if (!check_fp (fp))
+ {
+ rc_errno_set (EBADF);
+
+ debug_message (file, func, line, "Invalid file descriptor passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline void *
+__xcalloc(size_t nmemb, size_t size, const char *file,
+ const char *func, size_t line)
+{
+ void *new_ptr;
+
+ new_ptr = calloc (nmemb, size);
+ if (NULL == new_ptr)
+ {
+ /* Set errno in case specific malloc() implementation does not */
+ rc_errno_set (ENOMEM);
+
+ debug_message (file, func, line, "Failed to allocate buffer!\n");
+
+ return NULL;
+ }
+
+ return new_ptr;
+}
+
+inline void *
+__xmalloc (size_t size, const char *file, const char *func, size_t line)
+{
+ void *new_ptr;
+
+ new_ptr = malloc (size);
+ if (NULL == new_ptr)
+ {
+ /* Set errno in case specific malloc() implementation does not */
+ rc_errno_set (ENOMEM);
+
+ debug_message (file, func, line, "Failed to allocate buffer!\n");
+
+ return NULL;
+ }
+
+ return new_ptr;
+}
+
+inline void *
+__xrealloc (void *ptr, size_t size, const char *file,
+ const char *func, size_t line)
+{
+ void *new_ptr;
+
+ new_ptr = realloc (ptr, size);
+ if (NULL == new_ptr)
+ {
+ /* Set errno in case specific realloc() implementation does not */
+ rc_errno_set (ENOMEM);
+
+ debug_message (file, func, line, "Failed to reallocate buffer!\n");
+
+ return NULL;
+ }
+
+ return new_ptr;
+}
+
+inline char *
+__xstrndup (const char *str, size_t size, const char *file,
+ const char *func, size_t line)
+{
+ char *new_ptr;
+
+ new_ptr = rc_strndup (str, size);
+ if (NULL == new_ptr)
+ {
+ /* Set errno in case specific realloc() implementation does not */
+ rc_errno_set (ENOMEM);
+
+ debug_message (file, func, line,
+ "Failed to duplicate string via rc_strndup() !\n");
+
+ return NULL;
+ }
+
+ return new_ptr;
+}
+
diff --git a/libsbutil/src/dynbuf.c b/libsbutil/src/dynbuf.c
new file mode 100644
index 0000000..29d0dfb
--- /dev/null
+++ b/libsbutil/src/dynbuf.c
@@ -0,0 +1,488 @@
+/*
+ * dynbuf.c
+ *
+ * Dynamic allocated buffers.
+ *
+ * Copyright 2004-2007 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rcscripts/rcutil.h"
+
+#define DYNAMIC_BUFFER_SIZE (sizeof (char) * 2 * 1024)
+
+static rc_dynbuf_t *rc_dynbuf_reallocate (rc_dynbuf_t *dynbuf, size_t needed);
+
+rc_dynbuf_t *
+rc_dynbuf_new (void)
+{
+ rc_dynbuf_t *dynbuf = NULL;
+
+ dynbuf = xmalloc (sizeof (rc_dynbuf_t));
+ if (NULL == dynbuf)
+ return NULL;
+
+ dynbuf->data = xmalloc (DYNAMIC_BUFFER_SIZE);
+ if (NULL == dynbuf->data)
+ {
+ free (dynbuf);
+ return NULL;
+ }
+
+ dynbuf->length = DYNAMIC_BUFFER_SIZE;
+ dynbuf->rd_index = 0;
+ dynbuf->wr_index = 0;
+ dynbuf->file_map = FALSE;
+
+ return dynbuf;
+}
+
+rc_dynbuf_t *
+rc_dynbuf_new_mmap_file (const char *name)
+{
+ rc_dynbuf_t *dynbuf = NULL;
+
+ dynbuf = xmalloc (sizeof (rc_dynbuf_t));
+ if (NULL == dynbuf)
+ return NULL;
+
+ if (-1 == rc_file_map (name, &dynbuf->data, &dynbuf->length))
+ {
+ DBG_MSG ("Failed to mmap file '%s'\n", name);
+ free (dynbuf);
+
+ return NULL;
+ }
+
+ dynbuf->wr_index = dynbuf->length;
+ dynbuf->rd_index = 0;
+ dynbuf->file_map = TRUE;
+
+ return dynbuf;
+}
+
+rc_dynbuf_t *
+rc_dynbuf_reallocate (rc_dynbuf_t *dynbuf, size_t needed)
+{
+ int len;
+
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return NULL;
+
+ if (dynbuf->file_map)
+ {
+ rc_errno_set (EPERM);
+ DBG_MSG ("Cannot reallocate mmap()'d file!\n");
+
+ return NULL;
+ }
+
+ len = sizeof (char) * (dynbuf->wr_index + needed + 1);
+
+ if (dynbuf->length < len)
+ {
+ char *new_ptr;
+
+ /* Increase size in chunks to minimize reallocations */
+ if (len < (dynbuf->length + DYNAMIC_BUFFER_SIZE))
+ len = dynbuf->length + DYNAMIC_BUFFER_SIZE;
+
+ new_ptr = xrealloc (dynbuf->data, len);
+ if (NULL == new_ptr)
+ return NULL;
+
+ dynbuf->data = new_ptr;
+ dynbuf->length = len;
+ }
+
+ return dynbuf;
+}
+
+void
+rc_dynbuf_free (rc_dynbuf_t *dynbuf)
+{
+ if (NULL == dynbuf)
+ return;
+
+ if (!dynbuf->file_map)
+ {
+ if (NULL != dynbuf->data)
+ {
+ free (dynbuf->data);
+ dynbuf->data = NULL;
+ }
+ }
+ else
+ {
+ rc_file_unmap (dynbuf->data, dynbuf->length);
+ }
+
+ dynbuf->length = 0;
+ dynbuf->rd_index = 0;
+ dynbuf->wr_index = 0;
+
+ free (dynbuf);
+ dynbuf = NULL;
+}
+
+int
+rc_dynbuf_write (rc_dynbuf_t *dynbuf, const char *buf, size_t length)
+{
+ int len;
+
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return -1;
+
+ if (!check_arg_str (buf))
+ return -1;
+
+ if (dynbuf->file_map)
+ {
+ rc_errno_set (EPERM);
+ DBG_MSG ("Cannot write to readonly mmap()'d file!\n");
+
+ return -1;
+ }
+
+ if (NULL == rc_dynbuf_reallocate (dynbuf, length))
+ {
+ DBG_MSG ("Could not reallocate dynamic buffer!\n");
+ return -1;
+ }
+
+ len = snprintf ((dynbuf->data + dynbuf->wr_index), length + 1, "%s", buf);
+
+ /* If len is less than length, it means the string was shorter than
+ * given length */
+ if (length > len)
+ length = len;
+
+ if (0 < length)
+ dynbuf->wr_index += length;
+
+ if (-1 == length)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to write to dynamic buffer!\n");
+ }
+
+ return length;
+}
+
+int rc_dynbuf_write_fd (rc_dynbuf_t *dynbuf, int fd, size_t length)
+{
+ size_t len;
+ size_t total = 0;
+
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return -1;
+
+ if (!check_arg_fd (fd))
+ return -1;
+
+ if (dynbuf->file_map)
+ {
+ rc_errno_set (EPERM);
+ DBG_MSG ("Cannot write to readonly mmap()'d file!\n");
+
+ return -1;
+ }
+
+ if (NULL == rc_dynbuf_reallocate (dynbuf, length))
+ {
+ DBG_MSG ("Could not reallocate dynamic buffer!\n");
+ return -1;
+ }
+
+ do
+ {
+ len = read (fd, (dynbuf->data + dynbuf->wr_index + total),
+ (length - total));
+ if (-1 == len)
+ {
+ if (EINTR == errno)
+ {
+ /* We were interrupted, continue reading */
+ errno = 0;
+ /* Make sure we retry */
+ len = 1;
+ }
+ else
+ {
+ rc_errno_set (errno);
+ break;
+ }
+ }
+ else
+ {
+ total += len;
+ }
+ }
+ while ((0 < len) && (total < len));
+
+ if (0 < total)
+ dynbuf->wr_index += total;
+
+ dynbuf->data[dynbuf->wr_index] = '\0';
+
+ if (-1 == len)
+ /* XXX: We set errno in above while loop. */
+ DBG_MSG ("Failed to write to dynamic buffer!\n");
+
+ return (-1 == len) ? len : total;
+}
+
+int
+rc_dynbuf_sprintf (rc_dynbuf_t *dynbuf, const char *format, ...)
+{
+ va_list arg1, arg2;
+ char test_str[10];
+ int needed, written = 0;
+
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return -1;
+
+ if (!check_arg_str (format))
+ return -1;
+
+ if (dynbuf->file_map)
+ {
+ rc_errno_set (EPERM);
+ DBG_MSG ("Cannot write to readonly mmap()'d file!\n");
+
+ return -1;
+ }
+
+ va_start (arg1, format);
+ va_copy (arg2, arg1);
+
+ /* XXX: Lame way to try and figure out how much space we need */
+ needed = vsnprintf (test_str, sizeof (test_str), format, arg2);
+ va_end (arg2);
+
+ if (NULL == rc_dynbuf_reallocate (dynbuf, needed))
+ {
+ DBG_MSG ("Could not reallocate dynamic buffer!\n");
+ return -1;
+ }
+
+ written = vsnprintf ((dynbuf->data + dynbuf->wr_index), needed + 1,
+ format, arg1);
+ va_end (arg1);
+
+ if (0 < written)
+ dynbuf->wr_index += written;
+
+ if (-1 == written)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to write to dynamic buffer!\n");
+ }
+
+ return written;
+}
+
+int
+rc_dynbuf_read (rc_dynbuf_t *dynbuf, char *buf, size_t length)
+{
+ int len = length;
+
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return -1;
+
+ if (!check_arg_ptr (buf))
+ return -1;
+
+ if (dynbuf->rd_index >= dynbuf->length)
+ return 0;
+
+ if (dynbuf->wr_index < (dynbuf->rd_index + length))
+ len = dynbuf->wr_index - dynbuf->rd_index;
+
+ len = snprintf (buf, len + 1, "%s", (dynbuf->data + dynbuf->rd_index));
+
+ /* If len is less than length, it means the string was shorter than
+ * given length */
+ if (length > len)
+ length = len;
+
+ if (0 < length)
+ dynbuf->rd_index += length;
+
+ if (-1 == length)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to write from dynamic buffer!\n");
+ }
+
+ return length;
+}
+
+int
+rc_dynbuf_read_fd (rc_dynbuf_t *dynbuf, int fd, size_t length)
+{
+ size_t len;
+ size_t total = 0;
+ size_t max_read = length;
+
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return -1;
+
+ if (!check_arg_fd (fd))
+ return -1;
+
+ if (dynbuf->rd_index >= dynbuf->length)
+ return 0;
+
+ if (dynbuf->wr_index < (dynbuf->rd_index + length))
+ max_read = dynbuf->wr_index - dynbuf->rd_index;
+
+ do
+ {
+ len = write (fd, (dynbuf->data + dynbuf->rd_index + total),
+ (max_read - total));
+ if (-1 == len)
+ {
+ if (EINTR == errno)
+ {
+ /* We were interrupted, continue reading */
+ errno = 0;
+ /* Make sure we retry */
+ len = 1;
+ }
+ else
+ {
+ rc_errno_set (errno);
+ break;
+ }
+ }
+ else
+ {
+ total += len;
+ }
+ }
+ while ((0 < len) && (total < len));
+
+ if (0 < total)
+ dynbuf->rd_index += total;
+
+ if (-1 == len)
+ /* XXX: We set errno in above while loop. */
+ DBG_MSG ("Failed to write from dynamic buffer!\n");
+
+ return (-1 == len) ? len : total;
+}
+
+char *
+rc_dynbuf_read_line (rc_dynbuf_t *dynbuf)
+{
+ char *buf = NULL;
+ size_t count = 0;
+
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return NULL;
+
+ if (dynbuf->rd_index == dynbuf->wr_index)
+ return NULL;
+
+ for (count = dynbuf->rd_index; count < dynbuf->wr_index && dynbuf->data[count] != '\n'; count++);
+
+ if (count <= dynbuf->wr_index)
+ {
+ buf = xstrndup ((dynbuf->data + dynbuf->rd_index),
+ (count - dynbuf->rd_index));
+ if (NULL == buf)
+ return NULL;
+
+ dynbuf->rd_index = count;
+
+ /* Also skip the '\n' .. */
+ if (dynbuf->rd_index < dynbuf->wr_index)
+ dynbuf->rd_index++;
+ }
+
+ return buf;
+}
+
+int
+rc_dynbuf_replace_char (rc_dynbuf_t *dynbuf, const char old, const char new)
+{
+ int i;
+ int count = 0;
+
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return -1;
+
+ if (0 == dynbuf->wr_index)
+ return 0;
+
+ for (i = 0; i < dynbuf->wr_index; i++)
+ {
+ if (old == dynbuf->data[i])
+ {
+ dynbuf->data[i] = new;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+bool
+rc_dynbuf_read_eof (rc_dynbuf_t *dynbuf)
+{
+ if (!rc_check_arg_dynbuf (dynbuf))
+ return FALSE;
+
+ if (dynbuf->rd_index >= dynbuf->wr_index)
+ return TRUE;
+
+ return FALSE;
+}
+
+inline bool
+rc_check_dynbuf (rc_dynbuf_t *dynbuf)
+{
+ if ((NULL == dynbuf) || (NULL == dynbuf->data) || (0 == dynbuf->length))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+__rc_check_arg_dynbuf (rc_dynbuf_t *dynbuf, const char *file, const char *func,
+ size_t line)
+{
+ if (!rc_check_dynbuf (dynbuf))
+ {
+ rc_errno_set (EINVAL);
+
+ debug_message (file, func, line, "Invalid dynamic buffer passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
diff --git a/libsbutil/src/file.c b/libsbutil/src/file.c
new file mode 100644
index 0000000..b712d62
--- /dev/null
+++ b/libsbutil/src/file.c
@@ -0,0 +1,488 @@
+/*
+ * file.c
+ *
+ * Miscellaneous file related macro's and functions.
+ *
+ * Copyright 2004-2007 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "rcscripts/rcutil.h"
+
+bool
+rc_file_exists (const char *pathname)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_str (pathname))
+ return FALSE;
+
+ retval = lstat (pathname, &buf);
+ if (-1 != retval)
+ retval = TRUE;
+ else
+ retval = FALSE;
+
+ return retval;
+}
+
+bool
+rc_is_file (const char *pathname, bool follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_str (pathname))
+ return FALSE;
+
+ retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
+ if ((-1 != retval) && (S_ISREG (buf.st_mode)))
+ retval = TRUE;
+ else
+ retval = FALSE;
+
+ return retval;
+}
+
+bool
+rc_is_link (const char *pathname)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_str (pathname))
+ return FALSE;
+
+ retval = lstat (pathname, &buf);
+ if ((-1 != retval) && (S_ISLNK (buf.st_mode)))
+ retval = TRUE;
+ else
+ retval = FALSE;
+
+ return retval;
+}
+
+bool
+rc_is_dir (const char *pathname, bool follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_str (pathname))
+ return FALSE;
+
+ retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
+ if ((-1 != retval) && (S_ISDIR (buf.st_mode)))
+ retval = TRUE;
+ else
+ retval = FALSE;
+
+ return retval;
+}
+
+off_t
+rc_get_size (const char *pathname, bool follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_str (pathname))
+ return 0;
+
+ retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
+ if (-1 != retval)
+ retval = buf.st_size;
+ else
+ retval = 0;
+
+ return retval;
+}
+
+time_t
+rc_get_mtime (const char *pathname, bool follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_str (pathname))
+ return 0;
+
+ retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
+ if (-1 != retval)
+ retval = buf.st_mtime;
+ else
+ retval = 0;
+
+ return retval;
+}
+
+#if !defined(HAVE_REMOVE)
+int
+remove (const char *pathname)
+{
+ int retval;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ if (rc_is_dir (pathname, FALSE))
+ retval = rmdir (pathname);
+ else
+ retval = unlink (pathname);
+
+ if (0 != errno)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to remove() '%s'!\n", pathname);
+ }
+
+ return retval;
+}
+#endif
+
+int
+rc_mktree (const char *pathname, mode_t mode)
+{
+ char *temp_name = NULL;
+ char *temp_token = NULL;
+ char *token_p;
+ char *token;
+ int retval;
+ int lenght;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ /* Lenght of 'pathname' + extra for "./" if needed */
+ lenght = strlen (pathname) + 2;
+ /* lenght + '\0' */
+ temp_name = xmalloc (lenght + 1);
+ if (NULL == temp_name)
+ return -1;
+
+ temp_token = xstrndup (pathname, strlen (pathname));
+ if (NULL == temp_token)
+ goto error;
+
+ token_p = temp_token;
+
+ if (pathname[0] == '/')
+ temp_name[0] = '\0';
+ else
+ /* If not an absolute path, make it local */
+ strncpy (temp_name, ".", lenght);
+
+ token = strsep (&token_p, "/");
+ /* First token might be "", but that is OK as it will be when the
+ * pathname starts with '/' */
+ while (NULL != token)
+ {
+ strncat (temp_name, "/", lenght - strlen (temp_name));
+ strncat (temp_name, token, lenght - strlen (temp_name));
+
+ /* If it does not exist, create the dir. If it does exit,
+ * but is not a directory, we will catch it below. */
+ if (!rc_file_exists (temp_name))
+ {
+ retval = mkdir (temp_name, mode);
+ if (-1 == retval)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to create directory '%s'!\n", temp_name);
+ goto error;
+ }
+ /* Not a directory or symlink pointing to a directory */
+ }
+ else if (!rc_is_dir (temp_name, TRUE))
+ {
+ rc_errno_set (ENOTDIR);
+ DBG_MSG ("Component in '%s' is not a directory!\n", temp_name);
+ goto error;
+ }
+
+ do
+ {
+ token = strsep (&token_p, "/");
+ /* The first "" was Ok, but rather skip double '/' after that */
+ }
+ while ((NULL != token) && (0 == strlen (token)));
+ }
+
+ free (temp_name);
+ free (temp_token);
+
+ return 0;
+
+error:
+ free (temp_name);
+ free (temp_token);
+
+ return -1;
+}
+
+int
+rc_rmtree (const char *pathname)
+{
+ char **dirlist = NULL;
+ int i = 0;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ if (!rc_file_exists (pathname))
+ {
+ rc_errno_set (ENOENT);
+ DBG_MSG ("'%s' does not exist!\n", pathname);
+ return -1;
+ }
+
+ if (!rc_is_dir (pathname, FALSE))
+ {
+ rc_errno_set (ENOTDIR);
+ DBG_MSG ("'%s' is not a directory!\n", pathname);
+ return -1;
+ }
+
+
+ dirlist = rc_ls_dir (pathname, TRUE, FALSE);
+ if ((NULL == dirlist) && (rc_errno_is_set ()))
+ {
+ /* Do not error out - caller should decide itself if it
+ * it is an issue */
+ DBG_MSG ("Failed to ls_dir() directory '%s'!\n", pathname);
+ return -1;
+ }
+
+ while ((NULL != dirlist) && (NULL != dirlist[i]))
+ {
+ /* If it is a directory, call rc_rmtree() again with
+ * it as argument */
+ if (rc_is_dir (dirlist[i], FALSE))
+ {
+ if (-1 == rc_rmtree (dirlist[i]))
+ {
+ DBG_MSG ("Failed to rm_tree() directory '%s'!\n", dirlist[i]);
+ goto error;
+ }
+ }
+
+ /* Now actually remove it. Note that if it was a directory,
+ * it should already be removed by above rc_rmtree() call */
+ if ((rc_file_exists (dirlist[i]) && (-1 == remove (dirlist[i]))))
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to remove() '%s'!\n", dirlist[i]);
+ goto error;
+ }
+ i++;
+ }
+
+ str_list_free (dirlist);
+
+ /* Now remove the parent */
+ if (-1 == remove (pathname))
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to remove '%s'!\n", pathname);
+ goto error;
+ }
+
+ return 0;
+error:
+ str_list_free (dirlist);
+
+ return -1;
+}
+
+char **
+rc_ls_dir (const char *pathname, bool hidden, bool sort)
+{
+ DIR *dp;
+ struct dirent *dir_entry;
+ char **dirlist = NULL;
+
+ if (!check_arg_str (pathname))
+ return NULL;
+
+ if (!rc_is_dir (pathname, TRUE))
+ {
+ /* XXX: Should we error here? */
+ DBG_MSG ("'%s' is not a directory.\n", pathname);
+ return NULL;
+ }
+
+ dp = opendir (pathname);
+ if (NULL == dp)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to opendir() '%s'!\n", pathname);
+ /* errno will be set by opendir() */
+ goto error;
+ }
+
+ do
+ {
+ /* Clear errno to distinguish between EOF and error */
+ errno = 0;
+ dir_entry = readdir (dp);
+ /* Only an error if 'errno' != 0, else EOF */
+ if ((NULL == dir_entry) && (0 != errno))
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to readdir() '%s'!\n", pathname);
+ goto error;
+ }
+ if ((NULL != dir_entry)
+ /* Should we display hidden files? */
+ && (hidden ? 1 : dir_entry->d_name[0] != '.'))
+ {
+ char *d_name = dir_entry->d_name;
+ char *str_ptr;
+
+ /* Do not list current or parent entries */
+ if ((0 == strcmp (d_name, ".")) || (0 == strcmp (d_name, "..")))
+ continue;
+
+ str_ptr = rc_strcatpaths (pathname, d_name);
+ if (NULL == str_ptr)
+ {
+ DBG_MSG ("Failed to allocate buffer!\n");
+ goto error;
+ }
+
+ if (sort)
+ str_list_add_item_sorted (dirlist, str_ptr, error);
+ else
+ str_list_add_item (dirlist, str_ptr, error);
+ }
+ }
+ while (NULL != dir_entry);
+
+ if (!check_strv (dirlist))
+ {
+ if (NULL != dirlist)
+ str_list_free (dirlist);
+
+ DBG_MSG ("Directory '%s' is empty.\n", pathname);
+ }
+
+ closedir (dp);
+
+ return dirlist;
+
+error:
+ /* Free dirlist on error */
+ str_list_free (dirlist);
+
+ if (NULL != dp)
+ closedir (dp);
+
+ return NULL;
+}
+
+
+/*
+ * Below two functions (rc_file_map and rc_file_unmap) are
+ * from udev-050 (udev_utils.c).
+ * (Some are slightly modified, please check udev for originals.)
+ *
+ * Copyright 2004 Kay Sievers <kay@vrfy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+int
+rc_file_map (const char *filename, char **buf, size_t * bufsize)
+{
+ struct stat stats;
+ int fd;
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to open file!\n");
+ return -1;
+ }
+
+ if (fstat (fd, &stats) < 0)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to stat file!\n");
+
+ close (fd);
+
+ return -1;
+ }
+
+ if (0 == stats.st_size)
+ {
+ rc_errno_set (EINVAL);
+ DBG_MSG ("Failed to mmap file with 0 size!\n");
+
+ close (fd);
+
+ return -1;
+ }
+
+ *buf = mmap (NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (*buf == MAP_FAILED)
+ {
+ rc_errno_set (errno);
+ DBG_MSG ("Failed to mmap file!\n");
+
+ close (fd);
+
+ return -1;
+ }
+ *bufsize = stats.st_size;
+
+ close (fd);
+
+ return 0;
+}
+
+void
+rc_file_unmap (char *buf, size_t bufsize)
+{
+ munmap (buf, bufsize);
+}
+
diff --git a/libsbutil/src/librcutil.map b/libsbutil/src/librcutil.map
new file mode 100644
index 0000000..3b45515
--- /dev/null
+++ b/libsbutil/src/librcutil.map
@@ -0,0 +1,68 @@
+RCUTIL_0.0.0 {
+ global:
+ # debug.c
+ rc_log_domain;
+ rc_debug_enabled;
+ rc_errno_set;
+ rc_errno_clear;
+ rc_errno_get;
+ rc_errno_is_set;
+ debug_message;
+ check_ptr;
+ check_str;
+ check_strv;
+ check_fd;
+ check_fp;
+ __check_arg_ptr;
+ __check_arg_str;
+ __check_arg_strv;
+ __check_arg_fd;
+ __check_arg_fp;
+ __xcalloc;
+ __xmalloc;
+ __xrealloc;
+ __xstrndup;
+
+ # config.c
+ rc_get_cnf_entry;
+ rc_get_list_file;
+
+ # dynbuf.c
+ rc_dynbuf_new;
+ rc_dynbuf_new_mmap_file;
+ rc_dynbuf_free;
+ rc_dynbuf_write;
+ rc_dynbuf_write_fd;
+ rc_dynbuf_sprintf;
+ rc_dynbuf_read;
+ rc_dynbuf_read_fd;
+ rc_dynbuf_read_line;
+ rc_dynbuf_read_eof;
+ rc_check_dynbuf;
+ __rc_check_arg_dynbuf;
+
+ # file.c
+ rc_file_exists;
+ rc_is_file;
+ rc_is_link;
+ rc_is_dir;
+ rc_get_mtime;
+ remove;
+ rc_mktree;
+ rc_rmtree;
+ rc_ls_dir;
+ rc_file_map;
+ rc_file_unmap;
+
+ # simple-regex.c
+ match;
+
+ # string.c
+ rc_memrepchr;
+ rc_strcatpaths;
+ rc_strndup;
+ rc_basename;
+
+ local:
+ *;
+};
diff --git a/libsbutil/src/simple-regex.c b/libsbutil/src/simple-regex.c
new file mode 100644
index 0000000..25f9eb9
--- /dev/null
+++ b/libsbutil/src/simple-regex.c
@@ -0,0 +1,870 @@
+/*
+ * simple_regex.c
+ *
+ * Simle regex library.
+ *
+ * Copyright 2004-2007 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+/*
+ * Some notes:
+ *
+ * - This is a very simple regex library (read: return a match if some string
+ * matches some regex). It is probably not POSIX (if there are a POSIX or
+ * other standard) compatible.
+ *
+ * - I primarily wrote it to _not_ use glibc type regex functions, in case we
+ * might want to use it in code that have to be linked agaist klibc, etc.
+ *
+ * - It really is not optimized in any way yet.
+ *
+ * - Supported operators are:
+ *
+ * '.', '?', '*', '+' - So called 'wildcards'
+ * '[a-z]', '[^a-z]' - Basic 'lists'. Note that 'a-z' just specify that
+ * it supports basic lists as well as sequences ..
+ * The '^' is for an inverted list of course.
+ * '^', '$' - The 'from start' and 'to end' operators. If these
+ * are not used at the start ('^') or end ('$') of the
+ * regex, they will be treated as normal characters
+ * (this of course exclude the use of '^' in a 'list').
+ *
+ * - If an invalid argument was passed, the functions returns 0 with
+ * 'regex_data->match == 0' (no error with no match) rather than -1. It may
+ * not be consistant with other practices, but I personally do not feel it is
+ * a critical error for these types of functions, and there are debugging you
+ * can enable to verify that there are no such issues.
+ *
+ * - __somefunction() is usually a helper function for somefunction(). I guess
+ * recursion might be an alternative, but I try to avoid it.
+ *
+ * - In general if we are matching a 'wildcard' ('*', '+' or '?'), a 'word'
+ * (read: some part of the regex that do not contain a 'wildcard' or 'list')
+ * will have a greater 'weight' than the 'wildcard'. This means that we
+ * will only continue to evaluate the 'wildcard' until the following 'word'
+ * (if any) matches. Currently this do not hold true for a 'list' not
+ * followed by a 'wildcard' - I might fix this in future.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rcscripts/rcutil.h"
+
+/* Macro to check if a regex_data_t pointer is valid */
+#define CHECK_REGEX_DATA_P(_regex_data, _on_error) \
+ do { \
+ if ((NULL == _regex_data) \
+ || (NULL == _regex_data->data) \
+ /* We do not check for this, as it might still \
+ * provide a match ('*' or '?' wildcard) */ \
+ /* || (0 == strlen(_regex_data->data)) */ \
+ || (NULL == _regex_data->regex) \
+ || (0 == strlen(_regex_data->regex))) \
+ { \
+ rc_errno_set (EINVAL); \
+ DBG_MSG ("Invalid argument passed!\n"); \
+ goto _on_error; \
+ } \
+ } while (0)
+
+static size_t get_word (const char *regex, char **r_word);
+static int match_word (regex_data_t * regex_data);
+static size_t get_list_size (const char *regex);
+static size_t get_list (const char *regex, char **r_list);
+static int __match_list (regex_data_t * regex_data);
+static int match_list (regex_data_t * regex_data);
+static size_t get_wildcard (const char *regex, char *r_wildcard);
+static int __match_wildcard (regex_data_t * regex_data,
+ int (*match_func) (regex_data_t * regex_data),
+ const char *regex);
+static int match_wildcard (regex_data_t * regex_data);
+static int __match (regex_data_t * regex_data);
+
+/*
+ * Return values for match_* functions
+ *
+ * 0 - There was no error. If there was a match, regex_data->match
+ * - will be > 0 (this is the definitive check - if not true, the
+ * - other values of the struct may be bogus), regex_data->count
+ * - will be the amount of data that was matched (might be 0 for
+ * - some wildcards), and regex_data->r_count will be > 0.
+ *
+ * -1 - An error occured. Check errno for more info.
+ *
+ */
+
+size_t
+get_word (const char *regex, char **r_word)
+{
+ char *r_list;
+ char *str_ptr;
+ size_t count = 0;
+ size_t tmp_count;
+
+ if (!check_arg_str (regex))
+ return 0;
+
+ *r_word = xmalloc (strlen (regex) + 1);
+ if (NULL == r_word)
+ return 0;
+
+ str_ptr = *r_word;
+
+ while (strlen (regex) > 0)
+ {
+ switch (regex[0])
+ {
+ case '*':
+ case '+':
+ case '?':
+ /* If its a wildcard, backup one step */
+ *(--str_ptr) = '\0';
+ count--;
+ return count;
+ case '[':
+ tmp_count = get_list (regex, &r_list);
+ free (r_list);
+ /* In theory should not happen, but you never know
+ * what may happen in future ... */
+ if (-1 == tmp_count)
+ goto error;
+
+ /* Bail if we have a list */
+ if (tmp_count > 0)
+ {
+ str_ptr[0] = '\0';
+ return count;
+ }
+ default:
+ *str_ptr++ = *regex++;
+ count++;
+ break;
+ }
+ }
+
+ str_ptr[0] = '\0';
+
+ return count;
+
+error:
+ free (*r_word);
+
+ return -1;
+}
+
+int
+match_word (regex_data_t * regex_data)
+{
+ char *data_p = regex_data->data;
+ char *r_word = NULL, *r_word_p;
+ size_t count = 0;
+
+ CHECK_REGEX_DATA_P (regex_data, exit);
+
+ count = get_word (regex_data->regex, &r_word);
+ if (-1 == count)
+ goto error;
+ if (0 == count)
+ goto exit;
+ r_word_p = r_word;
+
+ while ((strlen (data_p) > 0) && (strlen (r_word_p) > 0))
+ {
+ /* If 'r_word' is not 100% part of 'string', we do not have
+ * a match. If its a '.', it matches no matter what. */
+ if ((data_p[0] != r_word_p[0]) && ('.' != r_word_p[0]))
+ {
+ count = 0;
+ goto exit;
+ }
+
+ data_p++;
+ r_word_p++;
+ }
+
+ /* If 'string' is shorter than 'r_word', we do not have a match */
+ if ((0 == strlen (data_p)) && (0 < strlen (r_word_p)))
+ {
+ count = 0;
+ goto exit;
+ }
+
+exit:
+ /* Fill in our structure */
+ if (0 == count)
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen (regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = count;
+
+ free (r_word);
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ free (r_word);
+ return -1;
+}
+
+size_t
+get_list_size (const char *regex)
+{
+ size_t count = 0;
+
+ if (!check_arg_str (regex))
+ return 0;
+
+ if ('[' != regex[0])
+ {
+ rc_errno_set (EINVAL);
+ DBG_MSG ("Invalid argument passed!\n");
+ return 0;
+ }
+
+ regex++;
+
+ while ((strlen (regex) > 0) && (']' != regex[0]))
+ {
+ /* We have a sequence (x-y) */
+ if (('-' == regex[0])
+ && (']' != regex[1])
+ && (strlen (regex) >= 2) && (regex[-1] < regex[1]))
+ {
+ /* Add current + diff in sequence */
+ count += regex[1] - regex[-1];
+ /* Take care of '-' and next char */
+ regex += 2;
+ }
+ else
+ {
+ regex++;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+size_t
+get_list (const char *regex, char **r_list)
+{
+ char *buf = NULL;
+ size_t count = 0;
+ size_t size;
+
+ if (!check_arg_str (regex))
+ return 0;
+
+ /* Bail if we do not have a list. Do not add debugging, as
+ * it is very noisy (used a lot when we call match_list() in
+ * __match() and match() to test for list matching) */
+ if ('[' != regex[0])
+ return 0;
+
+ size = get_list_size (regex);
+ if (0 == size)
+ {
+ /* Should not be an issue, but just in case */
+ DBG_MSG ("0 returned by get_list_size.\n");
+ return 0;
+ }
+
+ *r_list = xmalloc (size + 1);
+ if (NULL == *r_list)
+ return -1;
+
+ buf = *r_list;
+
+ /* Take care of '[' */
+ regex++;
+ count++;
+
+ while ((strlen (regex) > 0) && (']' != regex[0]))
+ {
+ /* We have a sequence (x-y) */
+ if (('-' == regex[0])
+ && (']' != regex[1])
+ && (strlen (regex) >= 2) && (regex[-1] < regex[1]))
+ {
+ /* Fill in missing chars in sequence */
+ while (buf[-1] < regex[1])
+ {
+ buf[0] = (char) (buf[-1] + 1);
+ buf++;
+ /* We do not increase count */
+ }
+ /* Take care of '-' and next char */
+ count += 2;
+ regex += 2;
+ }
+ else
+ {
+ *buf++ = *regex++;
+ count++;
+ }
+ }
+
+ buf[0] = '\0';
+ /* Take care of ']' */
+ count++;
+
+ /* We do not have a list as it does not end in ']' */
+ if (']' != regex[0])
+ {
+ count = 0;
+ free (*r_list);
+ }
+
+ return count;
+}
+
+/* If the first is the '^' character, everything but the list is matched
+ * NOTE: We only evaluate _ONE_ data character at a time!! */
+int
+__match_list (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *list_p = regex_data->regex;
+ char test_regex[2] = { '\0', '\0' };
+ int invert = 0;
+ int lmatch;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ if ('^' == list_p[0])
+ {
+ /* We need to invert the match */
+ invert = 1;
+ /* Make sure '^' is not part of our list */
+ list_p++;
+ }
+
+ if (invert)
+ /* All should be a match if not in the list */
+ lmatch = 1;
+ else
+ /* We only have a match if in the list */
+ lmatch = 0;
+
+ while (strlen (list_p) > 0)
+ {
+ test_regex[0] = list_p[0];
+
+ FILL_REGEX_DATA (tmp_data, data_p, test_regex);
+ retval = match_word (&tmp_data);
+ if (-1 == retval)
+ goto error;
+
+ if (REGEX_MATCH (tmp_data))
+ {
+ if (invert)
+ /* If we exclude the list from
+ * characters we try to match, we
+ * have a match until one of the
+ * list is found. */
+ lmatch = 0;
+ else
+ /* If not, we have to keep looking
+ * until one from the list match
+ * before we have a match */
+ lmatch = 1;
+ break;
+ }
+ list_p++;
+ }
+
+ /* Fill in our structure */
+ if (lmatch)
+ {
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ regex_data->where = regex_data->data;
+ regex_data->count = 1;
+ /* This one is more cosmetic, as match_list() will
+ * do the right thing */
+ regex_data->r_count = 0; /* strlen(regex_data->regex); */
+ }
+ else
+ {
+failed:
+ regex_data->match = REGEX_NO_MATCH;
+ regex_data->where = NULL;
+ regex_data->count = 0;
+ regex_data->r_count = 0;
+ }
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int
+match_list (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *list_p = regex_data->regex;
+ char *r_list = NULL;
+ size_t r_count = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ r_count = get_list (list_p, &r_list);
+ if (-1 == r_count)
+ goto error;
+ if (0 == r_count)
+ goto failed;
+
+ FILL_REGEX_DATA (tmp_data, data_p, &list_p[r_count - 1]);
+ retval = __match_wildcard (&tmp_data, __match_list, r_list);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ {
+ /* This should be 2 ('word' + 'wildcard'), so just remove
+ * the wildcard */
+ tmp_data.r_count--;
+ goto exit;
+ }
+
+ FILL_REGEX_DATA (tmp_data, data_p, r_list);
+ retval = __match_list (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ tmp_data.match = REGEX_NO_MATCH;
+ tmp_data.where = NULL;
+ tmp_data.count = 0;
+ tmp_data.r_count = 0;
+
+exit:
+ /* Fill in our structure */
+ regex_data->match = tmp_data.match;
+ regex_data->where = tmp_data.where;
+ regex_data->count = tmp_data.count;
+ if (regex_data->match != REGEX_NO_MATCH)
+ /* tmp_data.r_count for __match_wildcard will take care of the
+ * wildcard, and tmp_data.r_count for __match_list will be 0 */
+ regex_data->r_count = r_count + tmp_data.r_count;
+ else
+ regex_data->r_count = 0;
+
+ free (r_list);
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ free (r_list);
+ return -1;
+}
+
+size_t
+get_wildcard (const char *regex, char *r_wildcard)
+{
+ if (!check_arg_str (regex))
+ return 0;
+
+ r_wildcard[0] = regex[0];
+ r_wildcard[2] = '\0';
+
+ switch (regex[1])
+ {
+ case '*':
+ case '+':
+ case '?':
+ r_wildcard[1] = regex[1];
+ break;
+ default:
+ r_wildcard[0] = '\0';
+ return 0;
+ }
+
+ return strlen (r_wildcard);
+}
+
+int
+__match_wildcard (regex_data_t * regex_data,
+ int (*match_func) (regex_data_t * regex_data),
+ const char *regex)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *wildcard_p = regex_data->regex;
+ char r_wildcard[3];
+ size_t count = 0;
+ size_t r_count = 0;
+ int is_match = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, exit);
+
+ if (NULL == match_func)
+ {
+ rc_errno_set (EINVAL);
+ DBG_MSG ("NULL match_func was passed!\n");
+ goto exit;
+ }
+
+ r_count = get_wildcard (wildcard_p, r_wildcard);
+ if (0 == r_count)
+ goto exit;
+
+ FILL_REGEX_DATA (tmp_data, data_p, (char *) regex);
+ retval = match_func (&tmp_data);
+ if (-1 == retval)
+ goto error;
+
+ switch (r_wildcard[1])
+ {
+ case '*':
+ case '?':
+ /* '*' and '?' always matches */
+ is_match = 1;
+ case '+':
+ /* We need to match all of them */
+ do
+ {
+ /* If we have at least one match for '+', or none
+ * for '*' or '?', check if we have a word or list match.
+ * We do this because a word weights more than a wildcard */
+ if ((strlen (wildcard_p) > 2)
+ && ((count > 0)
+ || ('*' == r_wildcard[1])
+ || ('?' == r_wildcard[1])))
+ {
+ regex_data_t tmp_data2;
+#if 0
+ printf ("data_p = %s, wildcard_p = %s\n", data_p, wildcard_p);
+#endif
+
+ FILL_REGEX_DATA (tmp_data2, data_p, &wildcard_p[2]);
+ retval = match (&tmp_data2);
+ if (-1 == retval)
+ goto error;
+
+ if (
+ /* '.' might be a special case ... */
+ /* ('.' != wildcard_p[2]) && */
+ ((REGEX_MATCH (tmp_data2))
+ && (REGEX_FULL_MATCH == tmp_data2.match)))
+ {
+ goto exit;
+ }
+ }
+
+ if (REGEX_MATCH (tmp_data))
+ {
+ data_p += tmp_data.count;
+ count += tmp_data.count;
+ is_match = 1;
+
+ FILL_REGEX_DATA (tmp_data, data_p, (char *) regex);
+ retval = match_func (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ }
+ /* Only once for '?' */
+ }
+ while ((REGEX_MATCH (tmp_data)) && ('?' != r_wildcard[1]));
+
+ break;
+ default:
+ /* No wildcard */
+ break;
+ }
+
+exit:
+ /* Fill in our structure */
+ /* We can still have a match ('*' and '?'), although count == 0 */
+ if ((0 == count) && (0 == is_match))
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen (regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int
+match_wildcard (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *wildcard_p = regex_data->regex;
+ char r_wildcard[3];
+ size_t r_count;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ /* Invalid wildcard - we need a character + a regex operator */
+ if (strlen (wildcard_p) < 2)
+ goto failed;
+
+ r_count = get_wildcard (wildcard_p, r_wildcard);
+ if (0 == r_count)
+ goto failed;
+
+ /* Needed so that match_word() will not bail if it sees the wildcard */
+ r_wildcard[1] = '\0';
+
+ FILL_REGEX_DATA (tmp_data, data_p, wildcard_p);
+ retval = __match_wildcard (&tmp_data, match_word, r_wildcard);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ tmp_data.match = REGEX_NO_MATCH;
+ tmp_data.where = NULL;
+ tmp_data.count = 0;
+ tmp_data.r_count = 0;
+
+exit:
+ /* Fill in our structure */
+ regex_data->match = tmp_data.match;
+ regex_data->where = tmp_data.where;
+ regex_data->count = tmp_data.count;
+ regex_data->r_count = tmp_data.r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int
+__match (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *regex_p = regex_data->regex;
+ size_t count = 0;
+ size_t r_count = 0;
+ int rmatch = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ while (strlen (regex_p) > 0)
+ {
+#if 0
+ printf ("data_p = '%s', regex_p = '%s'\n", data_p, regex_p);
+#endif
+
+ FILL_REGEX_DATA (tmp_data, data_p, regex_p);
+ retval = match_list (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto have_match;
+
+ FILL_REGEX_DATA (tmp_data, data_p, regex_p);
+ retval = match_wildcard (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto have_match;
+
+ FILL_REGEX_DATA (tmp_data, data_p, regex_p);
+ retval = match_word (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto have_match;
+
+ break;
+
+have_match:
+ data_p += tmp_data.count;
+ count += tmp_data.count;
+ regex_p += tmp_data.r_count;
+ r_count += tmp_data.r_count;
+ rmatch = 1;
+
+ /* Check that we do not go out of bounds */
+ if (((data_p - regex_data->data) > strlen (regex_data->data))
+ || ((regex_p - regex_data->regex) > strlen (regex_data->regex)))
+ goto failed;
+ }
+
+ /* We could not match the whole regex (data too short?) */
+ if (0 != strlen (regex_p))
+ goto failed;
+
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ count = 0;
+ r_count = 0;
+ rmatch = 0;
+
+exit:
+ /* Fill in our structure */
+ /* We can still have a match ('*' and '?'), although count == 0 */
+ if ((0 == count) && (0 == rmatch))
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen (regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int
+match (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *regex_p;
+ char *buf = NULL;
+ int from_start = 0;
+ int to_end = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ /* We might be modifying regex_p, so make a copy */
+ buf = xstrndup (regex_data->regex, strlen (regex_data->regex));
+ if (NULL == buf)
+ goto error;
+
+ regex_p = buf;
+
+ /* Should we only match from the start? */
+ if ('^' == regex_p[0])
+ {
+ regex_p++;
+ from_start = 1;
+ }
+
+ /* Should we match up to the end? */
+ if ('$' == regex_p[strlen (regex_p) - 1])
+ {
+ regex_p[strlen (regex_p) - 1] = '\0';
+ to_end = 1;
+ }
+
+ do
+ {
+ FILL_REGEX_DATA (tmp_data, data_p, regex_p);
+ retval = __match (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ }
+ while ((strlen (data_p++) > 0)
+ && (!REGEX_MATCH (tmp_data)) && (0 == from_start));
+
+ /* Compensate for above extra inc */
+ data_p--;
+
+ /* Fill in our structure */
+ if (REGEX_MATCH (tmp_data))
+ {
+ /* Check if we had an '$' at the end of the regex, and
+ * verify that we still have a match */
+ if ((1 == to_end) && (tmp_data.count != strlen (data_p)))
+ {
+ goto failed;
+ }
+
+ if ((data_p == regex_data->data)
+ && (tmp_data.match == REGEX_FULL_MATCH))
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ regex_data->where = data_p;
+ regex_data->count = tmp_data.count;
+ regex_data->r_count = tmp_data.r_count;
+ if (1 == from_start)
+ regex_data->r_count++;
+ if (1 == to_end)
+ regex_data->r_count++;
+ }
+ else
+ {
+failed:
+ regex_data->match = REGEX_NO_MATCH;
+ regex_data->where = NULL;
+ regex_data->count = 0;
+ regex_data->r_count = 0;
+ }
+
+ free (buf);
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+ free (buf);
+
+ return -1;
+}
+
diff --git a/libsbutil/src/string.c b/libsbutil/src/string.c
new file mode 100644
index 0000000..32b52e0
--- /dev/null
+++ b/libsbutil/src/string.c
@@ -0,0 +1,108 @@
+/*
+ * string.c
+ *
+ * Miscellaneous macro's and functions.
+ *
+ * Copyright 2004-2007 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "rcscripts/rcutil.h"
+
+char *
+rc_memrepchr (char **str, char old, char new, size_t size)
+{
+ char *str_p;
+
+ if (!check_arg_strv (str))
+ return NULL;
+
+ str_p = memchr (*str, old, size);
+
+ while (NULL != str_p)
+ {
+ str_p[0] = new;
+ str_p = memchr (&str_p[1], old, size - (str_p - *str) - 1);
+ }
+
+ return *str;
+}
+
+char *
+rc_strcatpaths (const char *pathname1, const char *pathname2)
+{
+ char *new_path = NULL;
+ int lenght;
+
+ if ((!check_arg_str (pathname1)) || (!check_arg_str (pathname2)))
+ return 0;
+
+ /* Lenght of pathname1 + lenght of pathname2 + '/' if needed */
+ lenght = strlen (pathname1) + strlen (pathname2) + 2;
+ /* lenght + '\0' */
+ new_path = xmalloc (lenght);
+ if (NULL == new_path)
+ return NULL;
+
+ snprintf (new_path, lenght, "%s%s%s", pathname1,
+ (pathname1[strlen (pathname1) - 1] != '/') ? "/" : "",
+ pathname2);
+
+ return new_path;
+}
+
+char *
+rc_strndup (const char *str, size_t size)
+{
+ char *new_str = NULL;
+ size_t len;
+
+ /* We cannot check if its a valid string here, as it might
+ * not be '\0' terminated ... */
+ if (!check_arg_ptr (str))
+ return NULL;
+
+ /* Check lenght of str without breaching the size limit */
+ for (len = 0; (len < size) && ('\0' != str[len]); len++);
+
+ new_str = xmalloc (len + 1);
+ if (NULL == new_str)
+ return NULL;
+
+ /* Make sure our string is NULL terminated */
+ new_str[len] = '\0';
+
+ return (char *) memcpy (new_str, str, len);
+}
+
+char *
+rc_basename (const char *path)
+{
+ char *new_path = NULL;
+
+ if (!check_arg_str (path))
+ return NULL;
+
+ /* Copied from glibc */
+ new_path = strrchr (path, '/');
+ return new_path ? new_path + 1 : (char *) path;
+}
+