summaryrefslogtreecommitdiff
path: root/common/path_utils
diff options
context:
space:
mode:
Diffstat (limited to 'common/path_utils')
-rw-r--r--common/path_utils/Makefile.am17
-rw-r--r--common/path_utils/configure.ac18
-rw-r--r--common/path_utils/m4/.dir0
-rw-r--r--common/path_utils/path_utils.c535
-rw-r--r--common/path_utils/path_utils.h257
-rw-r--r--common/path_utils/path_utils.pc.in11
6 files changed, 838 insertions, 0 deletions
diff --git a/common/path_utils/Makefile.am b/common/path_utils/Makefile.am
new file mode 100644
index 00000000..fdda791c
--- /dev/null
+++ b/common/path_utils/Makefile.am
@@ -0,0 +1,17 @@
+AM_CFLAGS =
+if HAVE_GCC
+ AM_CFLAGS += \
+ -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings
+endif
+
+ACLOCAL_AMFLAGS = -I m4
+
+pkgconfigdir = $(libdir)/pkgconfig
+dist_noinst_DATA = path_utils.pc
+
+noinst_LTLIBRARIES = libpath_utils.la
+libpath_utils_la_SOURCES = \
+ path_utils.c \
+ path_utils.h
+
diff --git a/common/path_utils/configure.ac b/common/path_utils/configure.ac
new file mode 100644
index 00000000..9207a075
--- /dev/null
+++ b/common/path_utils/configure.ac
@@ -0,0 +1,18 @@
+AC_INIT([path_utils], [0.1], [jdennis@redhat.com])
+AC_CONFIG_SRCDIR([path_utils.c])
+AC_CONFIG_AUX_DIR([build])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AC_CONFIG_MACRO_DIR([m4])
+AC_PROG_INSTALL
+
+AM_CONDITIONAL([HAVE_GCC], [test "$ac_cv_prog_gcc" = yes])
+
+m4_pattern_allow([AM_SILENT_RULES])
+AM_SILENT_RULES
+
+AC_CONFIG_HEADERS([config.h])
+
+AC_CONFIG_FILES([Makefile path_utils.pc])
+AC_OUTPUT
diff --git a/common/path_utils/m4/.dir b/common/path_utils/m4/.dir
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/common/path_utils/m4/.dir
diff --git a/common/path_utils/path_utils.c b/common/path_utils/path_utils.c
new file mode 100644
index 00000000..b957392b
--- /dev/null
+++ b/common/path_utils/path_utils.c
@@ -0,0 +1,535 @@
+/* gcc -O0 -g -o path-utils path-utils.c */
+
+/*****************************************************************************/
+/******************************** Documentation ******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************************* Include Files *******************************/
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <libgen.h>
+
+#include "path_utils.h"
+
+/*****************************************************************************/
+/****************************** Internal Defines *****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/************************** Internal Type Definitions ************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/********************** External Function Declarations *********************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/********************** Internal Function Declarations *********************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/************************* External Global Variables ***********************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/************************* Internal Global Variables ***********************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/**************************** Inline Functions *****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*************************** Internal Functions ****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/**************************** Exported Functions ***************************/
+/*****************************************************************************/
+
+const char *path_utils_error_string(int error)
+{
+ switch(error) {
+ case SUCCESS: return _("Success");
+ case PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED: return _("Path could not be fully normalized");
+ }
+ return NULL;
+}
+
+int get_basename(char *base_name, size_t base_name_size, const char *path)
+{
+ char tmp_path[PATH_MAX];
+
+ if (!base_name || base_name_size < 1) return ENOBUFS;
+
+ strncpy(tmp_path, path, sizeof(tmp_path));
+ if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS;
+ strncpy(base_name, basename(tmp_path), base_name_size);
+ if (base_name[base_name_size-1] != 0) return ENOBUFS;
+ return SUCCESS;
+}
+
+int get_dirname(char *dir_path, size_t dir_path_size, const char *path)
+{
+ char tmp_path[PATH_MAX];
+
+ if (!dir_path || dir_path_size < 1) return ENOBUFS;
+
+ strncpy(tmp_path, path, sizeof(tmp_path));
+ if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS;
+ strncpy(dir_path, dirname(tmp_path), dir_path_size);
+ if (dir_path[dir_path_size-1] != 0) return ENOBUFS;
+ if (strcmp(dir_path, ".") == 0) {
+ if (getcwd(dir_path, dir_path_size) == NULL) {
+ if (errno == ERANGE)
+ return ENOBUFS;
+ else
+ return errno;
+ }
+ } else if (strcmp(dir_path, "..") == 0) {
+ if (getcwd(tmp_path, sizeof(tmp_path)) == NULL) {
+ if (errno == ERANGE)
+ return ENOBUFS;
+ else
+ return errno;
+ }
+ strncpy(dir_path, dirname(tmp_path), dir_path_size);
+ if (dir_path[dir_path_size-1] != 0) return ENOBUFS;
+ }
+ return SUCCESS;
+}
+
+int get_directory_and_base_name(char *dir_path, size_t dir_path_size, char *base_name, size_t base_name_size, const char *path)
+{
+ char tmp_path[PATH_MAX];
+
+ if (!dir_path || dir_path_size < 1) return ENOBUFS;
+ if (!base_name || base_name_size < 1) return ENOBUFS;
+
+ strncpy(tmp_path, path, sizeof(tmp_path));
+ if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS;
+ strncpy(base_name, basename(tmp_path), base_name_size);
+ if (base_name[base_name_size-1] != 0) return ENOBUFS;
+
+ strncpy(tmp_path, path, sizeof(tmp_path));
+ if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS;
+ strncpy(dir_path, dirname(tmp_path), dir_path_size);
+ if (dir_path[dir_path_size-1] != 0) return ENOBUFS;
+
+ if (strcmp(dir_path, ".") == 0) {
+ if (getcwd(dir_path, dir_path_size) == NULL) {
+ if (errno == ERANGE)
+ return ENOBUFS;
+ else
+ return errno;
+ }
+ } else if (strcmp(dir_path, "..") == 0) {
+ if (getcwd(tmp_path, sizeof(tmp_path)) == NULL) {
+ if (errno == ERANGE)
+ return ENOBUFS;
+ else
+ return errno;
+ }
+ strncpy(dir_path, dirname(tmp_path), dir_path_size);
+ if (dir_path[dir_path_size-1] != 0) return ENOBUFS;
+ }
+ return SUCCESS;
+}
+
+bool is_absolute_path(const char *path)
+{
+ if (!path) return false;
+ return path[0] == '/';
+}
+
+int path_concat(char *path, size_t path_size, const char *head, const char *tail)
+{
+ const char *p, *src;
+ char *dst, *dst_end;
+
+ if (!path || path_size < 1) return ENOBUFS;
+
+ dst = path;
+ dst_end = path + path_size - 1; /* -1 allows for NULL terminator */
+
+ if (head && *head) {
+ for (p = head; *p; p++); /* walk to end of head */
+ for (p--; p >= head && *p == '/'; p--); /* skip any trailing slashes in head */
+ for (src = head; src <= p && dst < dst_end;) *dst++ = *src++; /* copy head */
+ }
+ if (tail && *tail) {
+ for (p = tail; *p && *p == '/'; p++); /* skip any leading slashes in tail */
+ if (dst > path)
+ if (dst < dst_end) *dst++ = '/'; /* insert single slash between head & tail */
+ for (src = p; *src && dst < dst_end;) *dst++ = *src++; /* copy tail */
+ }
+ *dst = 0;
+ if (dst > dst_end) {
+ return ENOBUFS;
+ }
+ return SUCCESS;
+
+}
+
+int make_path_absolute(char *absolute_path, size_t absolute_path_size, const char *path)
+{
+ int result = SUCCESS;
+ const char *src;
+ char *dst, *dst_end;
+
+ if (!absolute_path || absolute_path_size < 1) return ENOBUFS;
+
+ dst = absolute_path;
+ dst_end = absolute_path + absolute_path_size - 1; /* -1 allows for NULL terminator */
+
+ if (is_absolute_path(path)) {
+ for (src = path; *src && dst < dst_end;) *dst++ = *src++;
+ *dst = 0;
+ if (dst > dst_end) result = ENOBUFS;
+ return result;
+ }
+
+ if ((getcwd(absolute_path, absolute_path_size) == NULL)) {
+ if (errno == ERANGE)
+ return ENOBUFS;
+ else
+ return errno;
+ }
+ for (dst = absolute_path; *dst && dst < dst_end; dst++);
+ if (dst > dst_end) {
+ *dst = 0;
+ result = ENOBUFS;
+ return result;
+ }
+ if (!(path && *path)) return result;
+ *dst++ = '/';
+ if (dst > dst_end) {
+ *dst = 0;
+ result = ENOBUFS;
+ return result;
+ }
+ for (src = path; *src && dst < dst_end;) *dst++ = *src++;
+ *dst = 0;
+ if (dst > dst_end) {
+ result = ENOBUFS;
+ }
+ return result;
+}
+
+char **split_path(const char *path, int *count)
+{
+ int n_components, component_len, total_component_len, alloc_len;
+ const char *start, *end;
+ char *mem_block, **array_ptr, *component_ptr;
+
+ /* If path is absolute add in special "/" root component */
+ if (*path == '/') {
+ n_components = 1;
+ total_component_len = 2;
+ } else {
+ n_components = 0;
+ total_component_len = 0;
+ }
+
+ /* Scan for components, keep several counts */
+ for (start = end = path; *start; start = end) {
+ for (start = end; *start && *start == '/'; start++);
+ for (end = start; *end && *end != '/'; end++);
+ if ((component_len = end - start) == 0) break;
+ n_components++;
+ total_component_len += component_len + 1;
+ }
+
+ /*
+ * Allocate a block big enough for component array (with trailing NULL
+ * entry, hence n_components+1) and enough room for a copy of each NULL
+ * terminated component. We'll copy the components into the same allocation
+ * block after the end of the pointer array.
+ */
+ alloc_len = ((n_components+1) * sizeof(char *)) + total_component_len;
+
+ if ((mem_block = malloc(alloc_len)) == NULL) {
+ if (count) *count = -1;
+ return NULL;
+ }
+
+ /* component array */
+ array_ptr = (char **)mem_block;
+ /* components copied after end of array */
+ component_ptr = mem_block + ((n_components+1)*sizeof(char *));
+
+ /* If path is absolute add in special "/" root component */
+ if (*path == '/') {
+ *array_ptr++ = component_ptr;
+ *component_ptr++ = '/';
+ *component_ptr++ = 0;
+ }
+
+ for (start = end = path; *start; start = end) {
+ for (start = end; *start && *start == '/'; start++);
+ for (end = start; *end && *end != '/'; end++);
+ if ((component_len = end - start) == 0) break;
+
+ *array_ptr++ = component_ptr;
+ while (start < end) *component_ptr++ = *start++;
+ *component_ptr++ = 0;
+ }
+ *array_ptr++ = NULL;
+ if (count) *count = n_components;
+ return (char **)mem_block;
+}
+
+int normalize_path(char *normalized_path, size_t normalized_path_size, const char *path)
+{
+ int result = SUCCESS;
+ int component_len;
+ bool is_absolute, can_backup;
+ const char *start, *end;
+ char *dst, *dst_end, *p, *limit;
+
+ if (!normalized_path || normalized_path_size < 1) return ENOBUFS;
+
+ dst = normalized_path;
+ dst_end = normalized_path + normalized_path_size - 1; /* -1 allows for NULL terminator */
+ can_backup = true;
+
+ if (!path || !*path) {
+ if (dst > dst_end) {
+ *dst = 0;
+ return ENOBUFS;
+ }
+ *dst++ = '.';
+ *dst = 0;
+ return result;
+ }
+
+ if ((is_absolute = *path == '/')) {
+ if (dst < dst_end) {
+ *dst++ = '/';
+ } else {
+ *dst = 0;
+ return ENOBUFS;
+ }
+ }
+
+ for (start = end = path; *start; start = end) {
+ for (start = end; *start && *start == '/'; start++);
+ for (end = start; *end && *end != '/'; end++);
+ if ((component_len = end - start) == 0) break;
+ if (component_len == 1 && start[0] == '.') continue;
+ if (component_len == 2 && start[0] == '.' && start[1] == '.' && can_backup) {
+ /* back up one level */
+ if ((is_absolute && dst == normalized_path+1) || (!is_absolute && dst == normalized_path)) {
+ if (is_absolute) continue;
+ can_backup = false;
+ result = PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED;
+ } else {
+ if (is_absolute)
+ limit = normalized_path+1;
+ else
+ limit = normalized_path;
+ for (p = dst-1; p >= limit && *p != '/'; p--);
+ if (p < limit)
+ dst = limit;
+ else
+ dst = p;
+ continue;
+ }
+ }
+
+ if ((dst > normalized_path) && (dst < dst_end) && (dst[-1] != '/')) *dst++ = '/';
+ while ((start < end) && (dst < dst_end)) *dst++ = *start++;
+ if (dst > dst_end) {
+ *dst = 0;
+ return ENOBUFS;
+ }
+ }
+
+ if (dst == normalized_path) {
+ if (is_absolute)
+ *dst++ = '/';
+ else
+ *dst++ = '.';
+ }
+ *dst = 0;
+ return result;
+}
+
+int common_path_prefix(char *common_path, size_t common_path_size, int *common_count, const char *path1, const char *path2)
+{
+ int count1, count2, min_count, i, n_common, result;
+ char **split1, **split2;
+ char *dst, *dst_end, *src;
+
+ if (!common_path || common_path_size < 1) return ENOBUFS;
+
+ result = SUCCESS;
+ n_common = 0;
+ split1 = split_path(path1, &count1);
+ split2 = split_path(path2, &count2);
+
+ if (count1 <= count2)
+ min_count = count1;
+ else
+ min_count = count2;
+
+ if (min_count <= 0) {
+ result = 0;
+ *common_path = 0;
+ goto done;
+ }
+
+ for (n_common = 0; n_common < min_count; n_common++) {
+ if (strcmp(split1[n_common], split2[n_common]) != 0) break;
+ }
+
+ if (n_common < 0) {
+ result = 0;
+ *common_path = 0;
+ goto done;
+ }
+
+ dst = common_path;
+ dst_end = common_path + common_path_size - 1; /* -1 allows for NULL terminator */
+ for (i = 0; i < n_common; i++) {
+ for (src = split1[i]; *src && dst < dst_end;) *dst++ = *src++;
+ if (dst == dst_end && *src) {
+ *dst = 0;
+ result = ENOBUFS;
+ goto done;
+ }
+ if (dst[-1] != '/' && i < n_common-1) { /* insert path separator */
+ if (dst == dst_end) {
+ *dst = 0;
+ result = ENOBUFS;
+ goto done;
+ }
+ *dst++ = '/';
+ }
+ }
+ *dst = 0;
+
+ done:
+ free(split1);
+ free(split2);
+ if (common_count) *common_count = n_common;
+ return result;
+}
+
+int make_normalized_absolute_path(char *result_path, size_t result_path_size, const char *path)
+{
+ int error;
+ char absolute_path[PATH_MAX];
+
+ if (!result_path || result_path_size < 1) return ENOBUFS;
+ *result_path = 0;
+ if ((error = make_path_absolute(absolute_path, sizeof(absolute_path), path)) != SUCCESS) return error;
+ if ((error = normalize_path(result_path, result_path_size, absolute_path)) != SUCCESS) return error;
+ return SUCCESS;
+}
+
+int find_existing_directory_ancestor(char *ancestor, size_t ancestor_size, const char *path)
+{
+ int error;
+ char base_name[PATH_MAX];
+ char dir_path[PATH_MAX];
+ struct stat info;
+
+ if (!ancestor || ancestor_size < 1) return ENOBUFS;
+ *ancestor = 0;
+ strncpy(dir_path, path, sizeof(dir_path));
+ if (dir_path[sizeof(dir_path)-1] != 0) return ENOBUFS;
+
+ while (strcmp(dir_path, "/") != 0) {
+ if (lstat(dir_path, &info) < 0) {
+ error = errno;
+ if (error != ENOENT) return error;
+ } else {
+ if (S_ISDIR(info.st_mode)) break;
+ }
+ get_directory_and_base_name(dir_path, sizeof(dir_path), base_name, sizeof(base_name), dir_path);
+ }
+
+ strncpy(ancestor, dir_path, ancestor_size);
+ if (ancestor[ancestor_size-1] != 0) return ENOBUFS;
+ return SUCCESS;
+}
+
+int directory_list(const char *path, bool recursive, directory_list_callback_t callback, void *user_data)
+{
+ DIR * dir;
+ struct dirent *entry;
+ struct stat info;
+ int error = 0;
+ char entry_path[PATH_MAX];
+ bool prune = false;
+
+ if (!(dir = opendir(path))) {
+ error = errno;
+ return error;
+ }
+
+ for (entry = readdir(dir); entry; entry = readdir(dir)) {
+ prune = false;
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
+
+ path_concat(entry_path, sizeof(entry_path), path, entry->d_name);
+
+ if (lstat(entry_path, &info) < 0) {
+ continue;
+ }
+
+ prune = !callback(path, entry->d_name, entry_path, &info, user_data);
+ if (S_ISDIR(info.st_mode)) {
+ if (recursive && !prune) directory_list(entry_path, recursive, callback, user_data);
+
+ }
+ }
+ closedir(dir);
+ return SUCCESS;
+}
+
+bool is_ancestor_path(const char *ancestor, const char *path)
+{
+ char **path_components, **ancestor_components;
+ int i, path_count, ancestor_count;
+ bool result;
+
+ result = false;
+ path_components = split_path(path, &path_count);
+ ancestor_components = split_path(ancestor, &ancestor_count);
+
+ if (!path_components || !ancestor_components) {
+ result = false;
+ goto exit;
+ }
+
+ if (ancestor_count >= path_count) {
+ result = false;
+ goto exit;
+ }
+
+ for (i = 0; i < ancestor_count; i++) {
+ if (strcmp(path_components[i], ancestor_components[i]) != 0) {
+ result = false;
+ goto exit;
+ }
+ }
+
+ result = true;
+
+ exit:
+ free(path_components);
+ free(ancestor_components);
+ return result;
+}
+
diff --git a/common/path_utils/path_utils.h b/common/path_utils/path_utils.h
new file mode 100644
index 00000000..9c1598ef
--- /dev/null
+++ b/common/path_utils/path_utils.h
@@ -0,0 +1,257 @@
+#ifndef PATH_UTILS_H
+#define PATH_UTILS_H
+
+/*****************************************************************************/
+/******************************** Documentation ******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************************* Include Files *******************************/
+/*****************************************************************************/
+
+#include <stdbool.h>
+#include <libintl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+/*****************************************************************************/
+/*********************************** Defines *********************************/
+/*****************************************************************************/
+
+#ifndef _
+#define _(String) gettext(String)
+#endif
+
+#ifndef SUCCESS
+#define SUCCESS 0
+#endif
+
+#define PATH_UTILS_ERROR_BASE -3000
+#define PATH_UTILS_ERROR_LIMIT (PATH_UTILS_ERROR_BASE+20)
+#define IS_PATH_UTILS_ERROR(error) (((error) >= PATH_UTILS_ERROR_BASE) && ((error) < PATH_UTILS_ERROR_LIMIT))
+
+#define PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED (PATH_UTILS_ERROR_BASE + 1)
+
+/*****************************************************************************/
+/******************************* Type Definitions ****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/************************* External Global Variables ***********************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/**************************** Exported Functions ***************************/
+/*****************************************************************************/
+
+/**
+ * Given an error code return the string description.
+ * If error code is not recognized NULL is returned.
+ */
+const char *path_utils_error_string(int error);
+
+/**
+ * Given a path, copy the basename component into the buffer base_name whose
+ * length is base_name_size.
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS if the buffer space is too small
+ */
+int get_basename(char *base_name, size_t base_name_size, const char *path);
+
+/**
+ * Given a path, copy the directory components into the buffer dir_path whose
+ * length is dir_path_size.
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS If the buffer space is too small
+ * EACCES Permission to read or search a component of the filename was denied.
+ * ENAMETOOLONG The size of the null-terminated pathname exceeds PATH_MAX bytes.
+ * ENOENT The current working directory has been unlinked.
+ */
+int get_dirname(char *dir_path, size_t dir_path_size, const char *path);
+
+/**
+ * Given a path, copy the directory components into the buffer dir_path whose
+ * length is dir_path_size and copy the basename component into the buffer
+ * base_name whose length is base_name_size.
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS If the buffer space is too small
+ * EACCES Permission to read or search a component of the filename was denied.
+ * ENAMETOOLONG The size of the null-terminated pathname exceeds PATH_MAX bytes.
+ * ENOENT The current working directory has been unlinked.
+ */
+int get_directory_and_base_name(char *dir_path, size_t dir_path_size, char *base_name, size_t base_name_size, const char *path);
+
+/**
+ * Return true if the path is absolute, false otherwise.
+ */
+bool is_absolute_path(const char *path);
+
+/**
+ * Given two paths, head & tail, copy their concatenation into the buffer path
+ * whose length is path_size.
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS If the buffer space is too small
+ */
+int path_concat(char *path, size_t path_size, const char *head, const char *tail);
+
+/**
+ * Given a path make it absolute storing the absolute path in into the buffer
+ * absolute_path whose length is absolute_path_size.
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS If the buffer space is too small
+ * ENOMEM If user memory cannot be mapped
+ * ENOENT If directory does not exist (i.e. it has been deleted)
+ * EFAULT If memory access violation occurs while copying
+ */
+int make_path_absolute(char *absolute_path, size_t absolute_path_size, const char *path);
+
+/**
+ * Split a file system path into individual components. Return a pointer to an
+ * array of char pointers, each array entry is a pointer to a copy of the
+ * component. As a special case if the path begins with / then the first
+ * component is "/" so the caller can identify the pah as absolute with the
+ * first component being the root. The last entry in the array is NULL serving
+ * as a termination sentinel. An optional integer count parameter can be
+ * provided, which if non-NULL will have the number of components written into
+ * it. Thus the caller can iterate on the array until it sees a NULL pointer or
+ * iterate count times indexing the array.
+ *
+ * The caller is responsible for calling free() on the returned array. This
+ * frees both the array of component pointers and the copies of each component
+ * in one operation because the copy of each component is stored in the same
+ * allocation block.
+ *
+ * The original path parameter is not modified.
+ *
+ * In the event of an error NULL is returned and count (if specified) will be -1.
+ *
+ * Examples:
+ *
+ * char **components, **component;
+ * int i;
+ *
+ * components = split_path(path, NULL);
+ * for (component = components; *component; component++)
+ * printf("\"%s\" ", *component);
+ * free(components);
+ *
+ * -OR-
+ *
+ * components = split_path(path, &count);
+ * for (i = 0; i < count; i++)
+ * printf("\"%s\" ", components[i]);
+ * free(components);
+ *
+ */
+char **split_path(const char *path, int *count);
+
+/**
+ * Normalizes a path copying the resulting normalized path into the buffer
+ * normalized_path whose length is normalized_size.
+ *
+ * A path is normalized when:
+ * only 1 slash separates all path components
+ * there are no . path components (except if . is the only component)
+ * there are no .. path components
+ *
+ * The input path may either be an absolute path or a path fragment.
+ *
+ * As a special case if the input path is NULL, the empty string "", or "." the
+ * returned normalized path will be ".".
+ *
+ * .. path components point to the parent directory which effectively means
+ * poping the parent off the path. But what happens when there are more .. path
+ * components than ancestors in the path? The answer depends on whether the path
+ * is an absolute path or a path fragment. If the path is absolute then the
+ * extra .. components which would move above the root (/) are simply
+ * ignored. This effectively limits the path to the root. However if the path is
+ * not absolute, rather it is a path fragment, and there are more .. components
+ * than ancestors which can be "popped off" then as many .. components will be
+ * popped off the fragement as possible without changing the meaning of the path
+ * fragment. In this case some extra .. components will be left in the path and
+ * the function will return the error
+ * ERROR_COULD_NOT_NORMALIZE_PATH_FULLY. However the function will still
+ * normalize as much of the path fragment as is possible. The behavior of
+ * .. components when the input path is a fragment is adopted because after
+ * normalizing a path fragment then the normalized path fragment if made
+ * absolute should reference the same file system name as if the unnormalized
+ * fragment were made absolute. Note this also means
+ * ERROR_COULD_NOT_NORMALIZE_PATH_FULLY will never be returned if the input path
+ * is absolute.
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS If the buffer space is too small
+ * ERROR_COULD_NOT_NORMALIZE_PATH_FULLY If not all .. path components could be removed
+ */
+int normalize_path(char *normalized_path, size_t normalized_path_size, const char *path);
+
+/**
+ * Finds the common prefix between two paths, returns the common prefix and
+ * optionally the count of how many path components were common between the two
+ * paths (if common_count is non-NULL).
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS if the buffer space is too small
+ */
+int common_path_prefix(char *common_path, size_t common_path_size, int *common_count, const char *path1, const char *path2);
+
+
+/**
+ * Make the input path absolute if it's not already, then normalize it.
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS if the buffer space is too small
+ */
+int make_normalized_absolute_path(char *result_path, size_t result_path_size, const char *path);
+
+/**
+ * Find the first path component which is an existing directory by walking from
+ * the tail of the path to it's head, return the path of the existing directory.
+ *
+ * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors:
+ * ENOBUFS if the buffer space is too small
+ * EACCES Search permission is denied for one of the directories.
+ * ELOOP Too many symbolic links encountered while traversing the path.
+ * ENAMETOOLONG File name too long.
+ * ENOMEM Out of memory (i.e., kernel memory).
+ */
+int find_existing_directory_ancestor(char *ancestor, size_t ancestor_size, const char *path);
+
+/**
+ * Walk a directory. If recursive is true child directories will be descended
+ * into. The supplied callback is invoked for each entry in the directory.
+ *
+ * The callback is provided with the directory name, basename the full pathname
+ * (i.e. directory name + basename) a stat sturcture for the path item and a
+ * pointer to any user supplied data specified in the user_data parameter. If
+ * the callback returns false for a directory the recursive descent into that
+ * directory does not occur thus effectively "pruning" the tree.
+ */
+typedef bool (*directory_list_callback_t)(const char *directory, const char *basename, const char *path,
+ struct stat *info, void *user_data);
+int directory_list(const char *path, bool recursive, directory_list_callback_t callback, void *user_data);
+
+/**
+ * Test to see if the path passed in the ancestor parameter is an ancestor of
+ * the path passed in the path parameter returning true if it is, false otherwise.
+ *
+ * The test "static" as such it is performed on the string components in each
+ * path. Live symbolic links in the file system are not taken into
+ * consideration. The test operates by splitting each path into it's individual
+ * components and then comparing each component pairwise for string
+ * equality. Both paths mush share a common root component for the test to be
+ * meaningful (e.g. don't attempt to compare an absolute path with a relative
+ * path).
+ *
+ * Example:
+ * is_ancestor_path("/a/b/c" "/a/b/c/d") => true
+ * is_ancestor_path("/a/b/c/d" "/a/b/c/d") => false // equal, not ancestor
+ * is_ancestor_path("/a/x/c" "/a/b/c/d") => false
+ */
+bool is_ancestor_path(const char *ancestor, const char *path);
+#endif
diff --git a/common/path_utils/path_utils.pc.in b/common/path_utils/path_utils.pc.in
new file mode 100644
index 00000000..aaf7531b
--- /dev/null
+++ b/common/path_utils/path_utils.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: @PACKAGE_NAME@
+Description: Utility functions to manipulate filesystem pathnames
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lpath_utils
+Cflags: -I${includedir}
+URL: http://fedorahosted.org/sssd/