From df8b16d7d55d97001ed71f629fc21511ec9f8d3b Mon Sep 17 00:00:00 2001 From: John Dennis Date: Tue, 14 Jul 2009 11:25:37 -0400 Subject: add path_utils filesystem path manipulation utility functions remove trace macro, not needed modifications to satisfy Stephen's checkin review correct spelling of PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED add checks for truncation, return error add checks for getcwd errors modify (dst >= dst_end) test to be (dst > dst_end) remove all use of tabs remove all trailing whitespace add missing truncation check after strncpy() Fix path_utils.pc Also correct a minor typo. --- common/path_utils/path_utils.c | 535 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 535 insertions(+) create mode 100644 common/path_utils/path_utils.c (limited to 'common/path_utils/path_utils.c') 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 +#include +#include +#include +#include +#include +#include + +#include + +#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; +} + -- cgit