diff options
Diffstat (limited to 'src/libs/uio/paths.c')
-rw-r--r-- | src/libs/uio/paths.c | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/src/libs/uio/paths.c b/src/libs/uio/paths.c new file mode 100644 index 0000000..f8411cd --- /dev/null +++ b/src/libs/uio/paths.c @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "paths.h" +#include "uioport.h" +#include "mem.h" + +static inline uio_PathComp *uio_PathComp_alloc(void); +static inline void uio_PathComp_free(uio_PathComp *pathComp); + +// gets the first dir component of a path +// sets '*start' to the start of the first component +// sets '*end' to the end of the first component +// if *start >= dirEnd, then the end has been reached. +void +getFirstPathComponent(const char *dir, const char *dirEnd, + const char **startComp, const char **endComp) { + assert(dir <= dirEnd); + *startComp = dir; + if (*startComp == dirEnd) { + *endComp = *startComp; + return; + } + *endComp = memchr(*startComp, '/', dirEnd - *startComp); + if (*endComp == NULL) + *endComp = dirEnd; +} + +// gets the first dir component of a path +// sets '*start' to the start of the first component +// sets '*end' to the end of the first component +// if **start == '\0', then the end has been reached. +void +getFirstPath0Component(const char *dir, + const char **startComp, const char **endComp) { + *startComp = dir; + if (**startComp == '\0') { + *endComp = *startComp; + return; + } + *endComp = strchr(*startComp, '/'); + if (*endComp == NULL) + *endComp = *startComp + strlen(*startComp); +} + +// gets the next component of a path +// '*start' should be set to the start of the last component +// '*end' should be set to the end of the last component +// '*start' will be set to the start of the next component +// '*end' will be set to the end of the next component +// if *start >= dirEnd, then the end has been reached. +void +getNextPathComponent(const char *dirEnd, + const char **startComp, const char **endComp) { + assert(*endComp <= dirEnd); + if (*endComp == dirEnd) { + *startComp = *endComp; + return; + } + assert(**endComp == '/'); + *startComp = *endComp + 1; + *endComp = memchr(*startComp, '/', dirEnd - *startComp); + if (*endComp == NULL) + *endComp = dirEnd; +} + +// gets the next component of a path +// '*start' should be set to the start of the last component +// '*end' should be set to the end of the last component +// '*start' will be set to the start of the next component +// '*end' will be set to the end of the next component +// if **start == '\0', then the end has been reached. +void +getNextPath0Component(const char **startComp, const char **endComp) { + if (**endComp == '\0') { + *startComp = *endComp; + return; + } + assert(**endComp == '/'); + *startComp = *endComp + 1; + *endComp = strchr(*startComp, '/'); + if (*endComp == NULL) + *endComp = *startComp + strlen(*startComp); +} + +// if *end == dir, the beginning has been reached +void +getLastPathComponent(const char *dir, const char *endDir, + const char **startComp, const char **endComp) { + *endComp = endDir; +// if (*(*endComp - 1) == '/') +// (*endComp)--; + *startComp = *endComp; + // TODO: use memrchr where available + while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/') + (*startComp)--; +} + +// if *end == dir, the beginning has been reached +// pre: dir is \0-terminated +void +getLastPath0Component(const char *dir, + const char **startComp, const char **endComp) { + *endComp = dir + strlen(dir); +// if (*(*endComp - 1) == '/') +// (*endComp)--; + *startComp = *endComp; + // TODO: use memrchr where available + while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/') + (*startComp)--; +} + +// if *end == dir, the beginning has been reached +void +getPreviousPathComponent(const char *dir, + const char **startComp, const char **endComp) { + if (*startComp == dir) { + *endComp = *startComp; + return; + } + *endComp = *startComp - 1; + *startComp = *endComp; + while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/') + (*startComp)--; +} + +// Combine two parts of a paths into a new path. +// The new path starts with a '/' only when the first part does. +// The first path may (but doesn't have to) end on a '/', or may be empty. +// Pre: the second path doesn't start with a '/' +char * +joinPaths(const char *first, const char *second) { + char *result, *resPtr; + size_t firstLen, secondLen; + + if (first[0] == '\0') + return uio_strdup(second); + + firstLen = strlen(first); + if (first[firstLen - 1] == '/') + firstLen--; + secondLen = strlen(second); + result = uio_malloc(firstLen + secondLen + 2); + resPtr = result; + + memcpy(resPtr, first, firstLen); + resPtr += firstLen; + + *resPtr = '/'; + resPtr++; + + memcpy(resPtr, second, secondLen); + resPtr += secondLen; + + *resPtr = '\0'; + return result; +} + +// Combine two parts of a paths into a new path. +// The new path will always start with a '/'. +// The first path may (but doesn't have to) end on a '/', or may be empty. +// Pre: the second path doesn't start with a '/' +char * +joinPathsAbsolute(const char *first, const char *second) { + char *result, *resPtr; + size_t firstLen, secondLen; + + if (first[0] == '\0') { + secondLen = strlen(second); + result = uio_malloc(secondLen + 2); + result[0] = '/'; + memcpy(&result[1], second, secondLen); + result[secondLen + 1] = '\0'; + return result; + } + + firstLen = strlen(first); + if (first[firstLen - 1] == '/') + firstLen--; + secondLen = strlen(second); + result = uio_malloc(firstLen + secondLen + 3); + resPtr = result; + + *resPtr = '/'; + resPtr++; + + memcpy(resPtr, first, firstLen); + resPtr += firstLen; + + *resPtr = '/'; + resPtr++; + + memcpy(resPtr, second, secondLen); + resPtr += secondLen; + + *resPtr = '\0'; + return result; +} + +// Returns 'false' if +// - one of the path components is empty, or +// - one of the path components is ".", or +// - one of the path components is ".." +// and 'true' otherwise. +uio_bool +validPathName(const char *path, size_t len) { + const char *pathEnd; + const char *start, *end; + + pathEnd = path + len; + getFirstPathComponent(path, pathEnd, &start, &end); + while (start < pathEnd) { + if (end == start || (end - start == 1 && start[0] == '.') || + (end - start == 2 && start[0] == '.' && start[1] == '.')) + return false; + getNextPathComponent(pathEnd, &start, &end); + } + return true; +} + +// returns 0 if the path is not a valid UNC path. +// Does not skip trailing slashes. +size_t +uio_skipUNCServerShare(const char *inPath) { + const char *path = inPath; + + // Skip the initial two backslashes. + if (path[0] != '\\' || path[1] != '\\') + return (size_t) 0; + path += 2; + + // Skip the server part. + while (*path != '\\' && *path != '/') { + if (*path == '\0') + return (size_t) 0; + path++; + } + + // Skip the seperator. + path++; + + // Skip the share part. + while (*path != '\0' && *path != '\\' && *path != '/') + path++; + + return (size_t) (path - inPath); +} + +/** + * Find the server and share part of a Windows "Universal Naming Convention" + * path (a path of the form '\\server\share\path\file'). + * The path must contain at least a server and share path to be valid. + * The initial two slashes must be backslashes, the other slashes may each + * be either a forward slash or a backslash. + * + * @param[in] inPath The path to parse. + * @param[out] outPath Will contain a newly allocated string (to be + * freed using uio_free(), containing the server and share part + * of inPath, separated by a backslash, or NULL if 'inPath' was + * not a valid UNC path. + * @param[out] outLen If not NULL on entry, it will contain the string + * length of '*outPath', or 0 if 'inPath' was not a valid UNC path. + * + * @returns The number of characters to add to 'inPath' to get to the first + * path component past the server and share part, or 0 if 'inPath' + * was not a valid UNC path. + */ +size_t +uio_getUNCServerShare(const char *inPath, char **outPath, size_t *outLen) { + const char *ptr; + const char *server; + const char *serverEnd; + const char *share; + const char *shareEnd; + char *name; + char *nameEnd; + size_t nameLen; + + ptr = inPath; + + if (ptr[0] != '\\' || ptr[1] != '\\') + goto noMatch; + + // Parse the server part. + server = ptr + 2; + serverEnd = server; + for (;;) { + if (*serverEnd == '\0') + goto noMatch; + if (isPathDelimiter(*serverEnd)) + break; + serverEnd++; + } + if (serverEnd == server) + goto noMatch; + + // Parse the share part. + share = serverEnd + 1; + shareEnd = share; + while (*shareEnd != '\0') { + if (isPathDelimiter(*shareEnd)) + break; + serverEnd++; + } + + // Skip any trailing path delimiters. + ptr = shareEnd; + while (isPathDelimiter(*ptr)) + ptr++; + + // Allocate a new string and fill it. + nameLen = (serverEnd - server) + (shareEnd - share) + 3; + name = uio_malloc(nameLen + 1); + nameEnd = name; + *(nameEnd++) = '\\'; + *(nameEnd++) = '\\'; + memcpy(nameEnd, server, serverEnd - server); + *(nameEnd++) = '\\'; + memcpy(nameEnd, share, shareEnd - share); + *nameEnd = '\0'; + + *outPath = name; + if (outLen != NULL) + *outLen = nameLen; + return (size_t) (ptr - inPath); + +noMatch: + *outPath = NULL; + if (outLen != NULL) + *outLen = 0; + return (size_t) 0; +} + +// Decomposes a path into its components. +// If isAbsolute is not NULL, *isAbsolute will be set to true +// iff the path is absolute. +// As POSIX considers multiple consecutive slashes to be equivalent to +// a single slash, so will uio (but not in the "\\MACHINE\share" part +// of a Windows UNC path). +int +decomposePath(const char *path, uio_PathComp **pathComp, + uio_bool *isAbsolute) { + uio_PathComp *result; + uio_PathComp *last; + uio_PathComp **endResult = &result; + uio_bool absolute = false; + char *name; +#ifdef HAVE_UNC_PATHS + size_t nameLen; +#endif /* HAVE_UNC_PATHS */ + + if (path[0] == '\0') { + errno = ENOENT; + return -1; + } + + last = NULL; +#ifdef HAVE_UNC_PATHS + path += uio_getUNCServerShare(path, &name, &nameLen); + if (name != NULL) { + // UNC path + *endResult = uio_PathComp_new(name, nameLen, last); + last = *endResult; + endResult = &last->next; + + absolute = true; + } else +#endif /* HAVE_UNC_PATHS */ +#ifdef HAVE_DRIVE_LETTERS + if (isDriveLetter(path[0]) && path[1] == ':') { + // DOS/Windows drive letter. + if (path[2] != '\0' && !isPathDelimiter(path[2])) { + errno = ENOENT; + return -1; + } + name = uio_memdup0(path, 2); + *endResult = uio_PathComp_new(name, 2, last); + last = *endResult; + endResult = &last->next; + absolute = true; + } else +#endif /* HAVE_DRIVE_LETTERS */ + { + if (isPathDelimiter(*path)) { + absolute = true; + do { + path++; + } while (isPathDelimiter(*path)); + } + } + + while (*path != '\0') { + const char *start = path; + while (*path != '\0' && !isPathDelimiter(*path)) + path++; + + name = uio_memdup0(path, path - start); + *endResult = uio_PathComp_new(name, path - start, last); + last = *endResult; + endResult = &last->next; + + while (isPathDelimiter(*path)) + path++; + } + + *endResult = NULL; + *pathComp = result; + if (isAbsolute != NULL) + *isAbsolute = absolute; + return 0; +} + +// Pre: pathComp forms a valid path for the platform. +void +composePath(const uio_PathComp *pathComp, uio_bool absolute, + char **path, size_t *pathLen) { + size_t len; + const uio_PathComp *ptr; + char *result; + char *pathPtr; + + assert(pathComp != NULL); + + // First determine how much space is required. + len = 0; + if (absolute) + len++; + ptr = pathComp; + while (ptr != NULL) { + len += ptr->nameLen; + ptr = ptr->next; + } + + // Allocate the required space. + result = (char *) uio_malloc(len + 1); + + // Fill the path. + pathPtr = result; + ptr = pathComp; + if (absolute) { +#ifdef HAVE_UNC_PATHS + if (ptr->name[0] == '\\') { + // UNC path + assert(ptr->name[1] == '\\'); + // Nothing to do. + } else +#endif /* HAVE_UNC_PATHS */ +#ifdef HAVE_DRIVE_LETTERS + if (ptr->nameLen == 2 && ptr->name[1] == ':' + && isDriveLetter(ptr->name[0])) { + // Nothing to do. + } + else +#endif /* HAVE_DRIVE_LETTERS */ + { + *(pathPtr++) = '/'; + } + } + + for (;;) { + memcpy(pathPtr, ptr->name, ptr->nameLen); + pathPtr += ptr->nameLen; + + ptr = ptr->next; + if (ptr == NULL) + break; + + *(pathPtr++) = '/'; + } + + *path = result; + *pathLen = len; +} + + +// *** uio_PathComp *** // + +static inline uio_PathComp * +uio_PathComp_alloc(void) { + uio_PathComp *result = uio_malloc(sizeof (uio_PathComp)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_PathComp, (void *) result); +#endif + return result; +} + +static inline void +uio_PathComp_free(uio_PathComp *pathComp) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_PathComp, (void *) pathComp); +#endif + uio_free(pathComp); +} + +// 'name' should be a null terminated string. It is stored in the PathComp, +// no copy is made. +// 'namelen' should be the length of 'name' +uio_PathComp * +uio_PathComp_new(char *name, size_t nameLen, uio_PathComp *upComp) { + uio_PathComp *result; + + result = uio_PathComp_alloc(); + result->name = name; + result->nameLen = nameLen; + result->up = upComp; + return result; +} + +void +uio_PathComp_delete(uio_PathComp *pathComp) { + uio_PathComp *next; + + while (pathComp != NULL) { + next = pathComp->next; + uio_free(pathComp->name); + uio_PathComp_free(pathComp); + pathComp = next; + } +} + +// Count the number of path components that 'comp' leads to. +int +uio_countPathComps(const uio_PathComp *comp) { + int count; + + count = 0; + for (; comp != NULL; comp = comp->next) + count++; + return count; +} + +uio_PathComp * +uio_lastPathComp(uio_PathComp *comp) { + if (comp == NULL) + return NULL; + + while (comp->next != NULL) + comp = comp->next; + return comp; +} + +// make a list of uio_PathComps from a path string +uio_PathComp * +uio_makePathComps(const char *path, uio_PathComp *upComp) { + const char *start, *end; + char *str; + uio_PathComp *result; + uio_PathComp **compPtr; // Where to put the next PathComp + + compPtr = &result; + getFirstPath0Component(path, &start, &end); + while (*start != '\0') { + str = uio_malloc(end - start + 1); + memcpy(str, start, end - start); + str[end - start] = '\0'; + + *compPtr = uio_PathComp_new(str, end - start, upComp); + upComp = *compPtr; + compPtr = &(*compPtr)->next; + getNextPath0Component(&start, &end); + } + *compPtr = NULL; + return result; +} + +void +uio_printPathComp(FILE *outStream, const uio_PathComp *comp) { + fprintf(outStream, "%s", comp->name); +} + +void +uio_printPathToComp(FILE *outStream, const uio_PathComp *comp) { + if (comp == NULL) + return; + uio_printPathToComp(outStream, comp->up); + fprintf(outStream, "/"); + uio_printPathComp(outStream, comp); +} + + |