summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/misc.c')
-rw-r--r--src/core/misc.c649
1 files changed, 649 insertions, 0 deletions
diff --git a/src/core/misc.c b/src/core/misc.c
new file mode 100644
index 0000000..3f1035f
--- /dev/null
+++ b/src/core/misc.c
@@ -0,0 +1,649 @@
+/*
+ * misc.c
+ *
+ * Miscellaneous macro's and functions.
+ *
+ * Copyright (C) 2004,2005 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "debug.h"
+#include "misc.h"
+
+char *memrepchr(char **str, char old, char new, size_t size) {
+ char *str_p;
+
+ if ((NULL == str) || (NULL == *str) || (0 == strlen(*str))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ 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 *strcatpaths(const char *pathname1, const char *pathname2) {
+ char *new_path = NULL;
+ int lenght;
+
+ if ((NULL == pathname1) || (0 == strlen(pathname1)) ||
+ (NULL == pathname2) || (0 == strlen(pathname2))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Lenght of pathname1 + lenght of pathname2 + '/' if needed */
+ lenght = strlen(pathname1) + strlen(pathname2) + 1;
+ /* lenght + '\0' */
+ new_path = malloc(lenght + 1);
+ if (NULL == new_path) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return NULL;
+ }
+
+ strncpy(new_path, pathname1, lenght);
+ /* Should we add a '/' ? */
+ if (new_path[strlen(new_path)-1] != '/')
+ strncat(new_path, "/", lenght - strlen(new_path));
+ strncat(new_path, pathname2, lenght - strlen(new_path));
+
+ return new_path;
+}
+
+char *strndup(const char *str, size_t size) {
+ char *new_str = NULL;
+ size_t len;
+
+ if (NULL == str) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Check lenght of str without breaching the size limit */
+ for (len = 0;(len < size) && ('\0' != str[len]);len++);
+
+ new_str = malloc(len + 1);
+ if (NULL == new_str) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return NULL;
+ }
+
+ /* Make sure our string is NULL terminated */
+ new_str[len] = '\0';
+
+ return (char *)memcpy(new_str, str, len);
+}
+
+char *gbasename(const char *path) {
+ char *new_path = NULL;
+
+ if ((NULL == path) || (0 == strlen(path))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Copied from glibc */
+ new_path = strrchr (path, '/');
+ return new_path ? new_path + 1 : (char *)path;
+}
+
+
+int exists(const char *pathname) {
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = lstat(pathname, &buf);
+ if (-1 != retval)
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int is_file(const char *pathname, int follow_link) {
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
+ if ((-1 != retval) && (S_ISREG(buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int is_link(const char *pathname) {
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = lstat(pathname, &buf);
+ if ((-1 != retval) && (S_ISLNK(buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int is_dir(const char *pathname, int follow_link) {
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
+ if ((-1 != retval) && (S_ISDIR(buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+time_t get_mtime(const char *pathname, int follow_link) {
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
+ if (-1 != retval)
+ return buf.st_mtime;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+#ifdef __KLIBC__
+int remove(const char *pathname) {
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (is_dir(pathname, 0))
+ retval = rmdir(pathname);
+ else
+ retval = unlink(pathname);
+
+ return retval;
+}
+#endif
+
+int 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 ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Lenght of 'pathname' + extra for "./" if needed */
+ lenght = strlen(pathname) + 2;
+ /* lenght + '\0' */
+ temp_name = malloc(lenght + 1);
+ if (NULL == temp_name) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ return -1;
+ }
+
+ temp_token = strndup(pathname, strlen(pathname));
+ if (NULL == temp_token) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ 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 (!exists(temp_name)) {
+ retval = mkdir(temp_name, mode);
+ if (-1 == retval) {
+ DBG_MSG("Failed to create directory!\n");
+ goto error;
+ }
+ /* Not a directory or symlink pointing to a directory */
+ } else if (!is_dir(temp_name, 1)) {
+ DBG_MSG("Component in pathname is not a directory!\n");
+ errno = ENOTDIR;
+ 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 rmtree(const char *pathname) {
+ char **dirlist = NULL;
+ int i = 0;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!exists(pathname)) {
+ DBG_MSG("'%s' does not exists!\n", pathname);
+ errno = ENOENT;
+ return -1;
+ }
+
+ dirlist = ls_dir(pathname, 1);
+ if ((NULL == dirlist) && (0 != errno)) {
+ /* If errno = ENOENT and the directory exists, then it means
+ * it is empty, so we should not error out */
+ if (ENOENT != errno) {
+ DBG_MSG("Could not get listing for '%s'!\n", pathname);
+ return -1;
+ }
+ }
+
+ while ((NULL != dirlist) && (NULL != dirlist[i])) {
+ /* If it is a directory, call rmtree() again with
+ * it as argument */
+ if (is_dir(dirlist[i], 0)) {
+ if (-1 == rmtree(dirlist[i])) {
+ DBG_MSG("Failed to delete sub directory!\n");
+ goto error;
+ }
+ }
+
+ /* Now actually remove it. Note that if it was a directory,
+ * it should already be removed by above rmtree() call */
+ if ((exists(dirlist[i]) && (-1 == remove(dirlist[i])))) {
+ DBG_MSG("Failed to remove '%s'!\n", dirlist[i]);
+ goto error;
+ }
+ i++;
+ }
+
+ STRING_LIST_FREE(dirlist);
+
+ /* Now remove the parent */
+ if (-1 == remove(pathname)) {
+ DBG_MSG("Failed to remove '%s'!\n", pathname);
+ goto error;
+ }
+
+ return 0;
+error:
+ STRING_LIST_FREE(dirlist);
+
+ return -1;
+}
+
+char **ls_dir(const char *pathname, int hidden) {
+ DIR *dirfd;
+ struct dirent *dir_entry;
+ char **dirlist = NULL;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ dirfd = opendir(pathname);
+ if (NULL == dirfd) {
+ DBG_MSG("Failed to call opendir()!\n");
+ /* errno will be set by opendir() */
+ goto error;
+ }
+
+ do {
+ /* Clear errno to distinguish between EOF and error */
+ errno = 0;
+ dir_entry = readdir(dirfd);
+ /* Only an error if 'errno' != 0, else EOF */
+ if ((NULL == dir_entry) && (0 != errno)) {
+ DBG_MSG("Failed to call readdir()!\n");
+ 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 *tmp_p;
+
+ /* Do not list current or parent entries */
+ if ((0 == strcmp(d_name, ".")) ||
+ (0 == strcmp(d_name, "..")))
+ continue;
+
+ tmp_p = strcatpaths(pathname, d_name);
+ if (NULL == tmp_p) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ /* errno = ENOMEM */
+ goto error;
+ }
+
+ STRING_LIST_ADD(dirlist, tmp_p, error);
+ }
+ } while (NULL != dir_entry);
+
+ if ((NULL == dirlist) || (NULL == dirlist[0])) {
+ DBG_MSG("Directory is empty.\n");
+ errno = ENOENT;
+ goto error;
+ }
+
+ closedir(dirfd);
+
+ return dirlist;
+
+error:
+ /* Free dirlist on error */
+ STRING_LIST_FREE(dirlist);
+
+ if (NULL != dirfd) {
+ int old_errno = errno;
+ closedir(dirfd);
+ /* closedir() might have changed it */
+ errno = old_errno;
+ }
+
+ return NULL;
+}
+
+/* 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 *get_cnf_entry(const char *pathname, const char *entry) {
+ char *buf = NULL;
+ char *tmp_buf = NULL;
+ char *tmp_p;
+ char *value = NULL;
+ char *token;
+ size_t lenght;
+ int count;
+ int current = 0;
+
+
+ if ((NULL == pathname) || (0 == strlen(pathname)) ||
+ (NULL == entry) || (0 == strlen(entry))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* If it is not a file or symlink pointing to a file, bail */
+ if (!is_file(pathname, 1)) {
+ DBG_MSG("Given pathname is not a file or do not exist!\n");
+ /* FIXME: Might need to set this to something better? */
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (-1 == file_map(pathname, &buf, &lenght)) {
+ DBG_MSG("Could not open config file for reading!\n");
+ return NULL;
+ }
+
+ while (current < lenght) {
+ count = buf_get_line(buf, lenght, current);
+
+ tmp_buf = strndup(&buf[current], count);
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+ tmp_p = tmp_buf;
+
+ /* Strip leading spaces/tabs */
+ while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
+ tmp_p++;
+
+ /* Get entry and value */
+ token = strsep(&tmp_p, "=");
+ /* 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(&tmp_p, "\"\'");
+ /* 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 (NULL != value) {
+ free(value);
+ value = NULL;
+ }
+ goto _continue;
+ }
+
+ /* If we have already allocated 'value', free it */
+ if (NULL != value)
+ free(value);
+
+ value = strndup(token, strlen(token));
+ if (NULL == value)
+ /* errno = ENOMEM */
+ goto error;
+
+ /* We do not break, as there might be more than one entry
+ * defined, and as bash uses the last, so should we */
+ /* break; */
+ }
+
+_continue:
+ current += count + 1;
+ free(tmp_buf);
+ /* Set to NULL in case we error out above and have
+ * to free below */
+ tmp_buf = NULL;
+ }
+
+
+ if (NULL == value) {
+ DBG_MSG("Failed to get value for config entry '%s'!\n", entry);
+ errno = ENOMSG;
+ goto error;
+ }
+
+ file_unmap(buf, lenght);
+
+ return value;
+
+error:
+ free(tmp_buf);
+ free(value);
+
+ if (NULL != buf) {
+ int old_errno = errno;
+ file_unmap(buf, lenght);
+ /* unmmap() might have changed it */
+ errno = old_errno;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Below three functions (file_map, file_unmap and buf_get_line) are
+ * from udev-050 (udev_utils.c).
+ * (Some are slightly modified, please check udev for originals.)
+ *
+ * Copyright (C) 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 file_map(const char *filename, char **buf, size_t *bufsize)
+{
+ struct stat stats;
+ int fd;
+ int old_errno;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ DBG_MSG("Failed to open file!\n");
+ return -1;
+ }
+
+ if (fstat(fd, &stats) < 0) {
+ DBG_MSG("Failed to stat file!\n");
+ old_errno = errno;
+ close(fd);
+ /* close() might have changed it */
+ errno = old_errno;
+ return -1;
+ }
+
+ *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (*buf == MAP_FAILED) {
+ DBG_MSG("Failed to mmap file!\n");
+ old_errno = errno;
+ close(fd);
+ /* close() might have changed it */
+ errno = old_errno;
+ return -1;
+ }
+ *bufsize = stats.st_size;
+
+ close(fd);
+
+ return 0;
+}
+
+void file_unmap(char *buf, size_t bufsize)
+{
+ munmap(buf, bufsize);
+}
+
+size_t buf_get_line(char *buf, size_t buflen, size_t cur)
+{
+ size_t count = 0;
+
+ for (count = cur; count < buflen && buf[count] != '\n'; count++);
+
+ return count - cur;
+}
+