1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* util/support/path.c - Portable path manipulation functions */ 3 /* 4 * Copyright (C) 2011 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #include <k5-platform.h> 28 29 /* For testing purposes, use a different symbol for Windows path semantics. */ 30 #ifdef _WIN32 31 #define WINDOWS_PATHS 32 #endif 33 34 /* 35 * This file implements a limited set of portable path manipulation functions. 36 * When in doubt about edge cases, we follow the Python os.path semantics. 37 */ 38 39 #ifdef WINDOWS_PATHS 40 #define SEP '\\' 41 #define IS_SEPARATOR(c) ((c) == '\\' || (c) == '/') 42 #else 43 #define SEP '/' 44 #define IS_SEPARATOR(c) ((c) == '/') 45 #endif 46 47 /* Find the rightmost path separator in path, or NULL if there is none. */ 48 static inline const char * 49 find_sep(const char *path) 50 { 51 #ifdef WINDOWS_PATHS 52 const char *slash, *backslash; 53 54 slash = strrchr(path, '/'); 55 backslash = strrchr(path, '\\'); 56 if (slash != NULL && backslash != NULL) 57 return (slash > backslash) ? slash : backslash; 58 else 59 return (slash != NULL) ? slash : backslash; 60 #else 61 return strrchr(path, '/'); 62 #endif 63 } 64 65 /* XXX drive letter prefixes */ 66 long 67 k5_path_split(const char *path, char **parent_out, char **basename_out) 68 { 69 const char *pathstart, *sep, *pend, *bstart; 70 char *parent = NULL, *basename = NULL; 71 72 if (parent_out != NULL) 73 *parent_out = NULL; 74 if (basename_out != NULL) 75 *basename_out = NULL; 76 77 pathstart = path; 78 #ifdef WINDOWS_PATHS 79 if (*path != '\0' && path[1] == ':') 80 pathstart = path + 2; 81 #endif 82 83 sep = find_sep(pathstart); 84 if (sep != NULL) { 85 bstart = sep + 1; 86 /* Strip off excess separators before the one we found. */ 87 pend = sep; 88 while (pend > pathstart && IS_SEPARATOR(pend[-1])) 89 pend--; 90 /* But if we hit the start, keep the whole separator sequence. */ 91 if (pend == pathstart) 92 pend = sep + 1; 93 } else { 94 bstart = pathstart; 95 pend = pathstart; 96 } 97 98 if (parent_out) { 99 parent = malloc(pend - path + 1); 100 if (parent == NULL) 101 return ENOMEM; 102 memcpy(parent, path, pend - path); 103 parent[pend - path] = '\0'; 104 } 105 if (basename_out) { 106 basename = strdup(bstart); 107 if (basename == NULL) { 108 free(parent); 109 return ENOMEM; 110 } 111 } 112 113 if (parent_out) 114 *parent_out = parent; 115 if (basename_out) 116 *basename_out = basename; 117 return 0; 118 } 119 120 long 121 k5_path_join(const char *path1, const char *path2, char **path_out) 122 { 123 char *path, c; 124 int ret; 125 126 *path_out = NULL; 127 if (k5_path_isabs(path2) || *path1 == '\0') { 128 /* Discard path1 and return a copy of path2. */ 129 path = strdup(path2); 130 if (path == NULL) 131 return ENOMEM; 132 } else { 133 /* 134 * Compose path1 and path2, adding a separator if path1 is non-empty 135 * there's no separator between them already. (*path2 can be a 136 * separator in the weird case where it starts with /: or \: on 137 * Windows, and Python doesn't insert a separator in this case.) 138 */ 139 c = path1[strlen(path1) - 1]; 140 if (IS_SEPARATOR(c) || IS_SEPARATOR(*path2)) 141 ret = asprintf(&path, "%s%s", path1, path2); 142 else 143 ret = asprintf(&path, "%s%c%s", path1, SEP, path2); 144 if (ret < 0) 145 return ENOMEM; 146 } 147 *path_out = path; 148 return 0; 149 } 150 151 int 152 k5_path_isabs(const char *path) 153 { 154 #ifdef WINDOWS_PATHS 155 if (*path != '\0' && path[1] == ':') 156 path += 2; 157 return (*path == '/' || *path == '\\'); 158 #else 159 return (*path == '/'); 160 #endif 161 } 162