diff options
author | Mike Frysinger <vapier@gentoo.org> | 2007-04-19 01:07:41 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2007-04-19 01:07:41 +0000 |
commit | a71ecdff555f477135798a8f49c35de172fdc7c2 (patch) | |
tree | 68ebd70e8b27a79dec11d7ecde4fed2c94ac2e8a /libsbutil/src | |
parent | INSTALL: update to current autoconf (diff) | |
download | sandbox-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.am | 19 | ||||
-rw-r--r-- | libsbutil/src/config.c | 244 | ||||
-rw-r--r-- | libsbutil/src/debug.c | 335 | ||||
-rw-r--r-- | libsbutil/src/dynbuf.c | 488 | ||||
-rw-r--r-- | libsbutil/src/file.c | 488 | ||||
-rw-r--r-- | libsbutil/src/librcutil.map | 68 | ||||
-rw-r--r-- | libsbutil/src/simple-regex.c | 870 | ||||
-rw-r--r-- | libsbutil/src/string.c | 108 |
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; +} + |