1 /* 2 * I'm tired of doing "vsnprintf()" etc just to open a 3 * file, so here's a "return static buffer with printf" 4 * interface for paths. 5 * 6 * It's obviously not thread-safe. Sue me. But it's quite 7 * useful for doing things like 8 * 9 * f = open(mkpath("%s/%s.perf", base, name), O_RDONLY); 10 * 11 * which is what it's designed for. 12 */ 13 #include "cache.h" 14 15 static char bad_path[] = "/bad-path/"; 16 /* 17 * Two hacks: 18 */ 19 20 static const char *get_perf_dir(void) 21 { 22 return "."; 23 } 24 25 size_t strlcpy(char *dest, const char *src, size_t size) 26 { 27 size_t ret = strlen(src); 28 29 if (size) { 30 size_t len = (ret >= size) ? size - 1 : ret; 31 memcpy(dest, src, len); 32 dest[len] = '\0'; 33 } 34 return ret; 35 } 36 37 38 static char *get_pathname(void) 39 { 40 static char pathname_array[4][PATH_MAX]; 41 static int idx; 42 43 return pathname_array[3 & ++idx]; 44 } 45 46 static char *cleanup_path(char *path) 47 { 48 /* Clean it up */ 49 if (!memcmp(path, "./", 2)) { 50 path += 2; 51 while (*path == '/') 52 path++; 53 } 54 return path; 55 } 56 57 char *mksnpath(char *buf, size_t n, const char *fmt, ...) 58 { 59 va_list args; 60 unsigned len; 61 62 va_start(args, fmt); 63 len = vsnprintf(buf, n, fmt, args); 64 va_end(args); 65 if (len >= n) { 66 strlcpy(buf, bad_path, n); 67 return buf; 68 } 69 return cleanup_path(buf); 70 } 71 72 static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args) 73 { 74 const char *perf_dir = get_perf_dir(); 75 size_t len; 76 77 len = strlen(perf_dir); 78 if (n < len + 1) 79 goto bad; 80 memcpy(buf, perf_dir, len); 81 if (len && !is_dir_sep(perf_dir[len-1])) 82 buf[len++] = '/'; 83 len += vsnprintf(buf + len, n - len, fmt, args); 84 if (len >= n) 85 goto bad; 86 return cleanup_path(buf); 87 bad: 88 strlcpy(buf, bad_path, n); 89 return buf; 90 } 91 92 char *perf_snpath(char *buf, size_t n, const char *fmt, ...) 93 { 94 va_list args; 95 va_start(args, fmt); 96 (void)perf_vsnpath(buf, n, fmt, args); 97 va_end(args); 98 return buf; 99 } 100 101 char *perf_pathdup(const char *fmt, ...) 102 { 103 char path[PATH_MAX]; 104 va_list args; 105 va_start(args, fmt); 106 (void)perf_vsnpath(path, sizeof(path), fmt, args); 107 va_end(args); 108 return xstrdup(path); 109 } 110 111 char *mkpath(const char *fmt, ...) 112 { 113 va_list args; 114 unsigned len; 115 char *pathname = get_pathname(); 116 117 va_start(args, fmt); 118 len = vsnprintf(pathname, PATH_MAX, fmt, args); 119 va_end(args); 120 if (len >= PATH_MAX) 121 return bad_path; 122 return cleanup_path(pathname); 123 } 124 125 char *perf_path(const char *fmt, ...) 126 { 127 const char *perf_dir = get_perf_dir(); 128 char *pathname = get_pathname(); 129 va_list args; 130 unsigned len; 131 132 len = strlen(perf_dir); 133 if (len > PATH_MAX-100) 134 return bad_path; 135 memcpy(pathname, perf_dir, len); 136 if (len && perf_dir[len-1] != '/') 137 pathname[len++] = '/'; 138 va_start(args, fmt); 139 len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args); 140 va_end(args); 141 if (len >= PATH_MAX) 142 return bad_path; 143 return cleanup_path(pathname); 144 } 145 146 147 /* perf_mkstemp() - create tmp file honoring TMPDIR variable */ 148 int perf_mkstemp(char *path, size_t len, const char *template) 149 { 150 const char *tmp; 151 size_t n; 152 153 tmp = getenv("TMPDIR"); 154 if (!tmp) 155 tmp = "/tmp"; 156 n = snprintf(path, len, "%s/%s", tmp, template); 157 if (len <= n) { 158 errno = ENAMETOOLONG; 159 return -1; 160 } 161 return mkstemp(path); 162 } 163 164 165 const char *make_relative_path(const char *abs_path, const char *base) 166 { 167 static char buf[PATH_MAX + 1]; 168 int baselen; 169 170 if (!base) 171 return abs_path; 172 173 baselen = strlen(base); 174 if (prefixcmp(abs_path, base)) 175 return abs_path; 176 if (abs_path[baselen] == '/') 177 baselen++; 178 else if (base[baselen - 1] != '/') 179 return abs_path; 180 181 strcpy(buf, abs_path + baselen); 182 183 return buf; 184 } 185 186 /* 187 * It is okay if dst == src, but they should not overlap otherwise. 188 * 189 * Performs the following normalizations on src, storing the result in dst: 190 * - Ensures that components are separated by '/' (Windows only) 191 * - Squashes sequences of '/'. 192 * - Removes "." components. 193 * - Removes ".." components, and the components the precede them. 194 * Returns failure (non-zero) if a ".." component appears as first path 195 * component anytime during the normalization. Otherwise, returns success (0). 196 * 197 * Note that this function is purely textual. It does not follow symlinks, 198 * verify the existence of the path, or make any system calls. 199 */ 200 int normalize_path_copy(char *dst, const char *src) 201 { 202 char *dst0; 203 204 if (has_dos_drive_prefix(src)) { 205 *dst++ = *src++; 206 *dst++ = *src++; 207 } 208 dst0 = dst; 209 210 if (is_dir_sep(*src)) { 211 *dst++ = '/'; 212 while (is_dir_sep(*src)) 213 src++; 214 } 215 216 for (;;) { 217 char c = *src; 218 219 /* 220 * A path component that begins with . could be 221 * special: 222 * (1) "." and ends -- ignore and terminate. 223 * (2) "./" -- ignore them, eat slash and continue. 224 * (3) ".." and ends -- strip one and terminate. 225 * (4) "../" -- strip one, eat slash and continue. 226 */ 227 if (c == '.') { 228 if (!src[1]) { 229 /* (1) */ 230 src++; 231 } else if (is_dir_sep(src[1])) { 232 /* (2) */ 233 src += 2; 234 while (is_dir_sep(*src)) 235 src++; 236 continue; 237 } else if (src[1] == '.') { 238 if (!src[2]) { 239 /* (3) */ 240 src += 2; 241 goto up_one; 242 } else if (is_dir_sep(src[2])) { 243 /* (4) */ 244 src += 3; 245 while (is_dir_sep(*src)) 246 src++; 247 goto up_one; 248 } 249 } 250 } 251 252 /* copy up to the next '/', and eat all '/' */ 253 while ((c = *src++) != '\0' && !is_dir_sep(c)) 254 *dst++ = c; 255 if (is_dir_sep(c)) { 256 *dst++ = '/'; 257 while (is_dir_sep(c)) 258 c = *src++; 259 src--; 260 } else if (!c) 261 break; 262 continue; 263 264 up_one: 265 /* 266 * dst0..dst is prefix portion, and dst[-1] is '/'; 267 * go up one level. 268 */ 269 dst--; /* go to trailing '/' */ 270 if (dst <= dst0) 271 return -1; 272 /* Windows: dst[-1] cannot be backslash anymore */ 273 while (dst0 < dst && dst[-1] != '/') 274 dst--; 275 } 276 *dst = '\0'; 277 return 0; 278 } 279 280 /* 281 * path = Canonical absolute path 282 * prefix_list = Colon-separated list of absolute paths 283 * 284 * Determines, for each path in prefix_list, whether the "prefix" really 285 * is an ancestor directory of path. Returns the length of the longest 286 * ancestor directory, excluding any trailing slashes, or -1 if no prefix 287 * is an ancestor. (Note that this means 0 is returned if prefix_list is 288 * "/".) "/foo" is not considered an ancestor of "/foobar". Directories 289 * are not considered to be their own ancestors. path must be in a 290 * canonical form: empty components, or "." or ".." components are not 291 * allowed. prefix_list may be null, which is like "". 292 */ 293 int longest_ancestor_length(const char *path, const char *prefix_list) 294 { 295 char buf[PATH_MAX+1]; 296 const char *ceil, *colon; 297 int len, max_len = -1; 298 299 if (prefix_list == NULL || !strcmp(path, "/")) 300 return -1; 301 302 for (colon = ceil = prefix_list; *colon; ceil = colon+1) { 303 for (colon = ceil; *colon && *colon != PATH_SEP; colon++); 304 len = colon - ceil; 305 if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) 306 continue; 307 strlcpy(buf, ceil, len+1); 308 if (normalize_path_copy(buf, buf) < 0) 309 continue; 310 len = strlen(buf); 311 if (len > 0 && buf[len-1] == '/') 312 buf[--len] = '\0'; 313 314 if (!strncmp(path, buf, len) && 315 path[len] == '/' && 316 len > max_len) { 317 max_len = len; 318 } 319 } 320 321 return max_len; 322 } 323 324 /* strip arbitrary amount of directory separators at end of path */ 325 static inline int chomp_trailing_dir_sep(const char *path, int len) 326 { 327 while (len && is_dir_sep(path[len - 1])) 328 len--; 329 return len; 330 } 331 332 /* 333 * If path ends with suffix (complete path components), returns the 334 * part before suffix (sans trailing directory separators). 335 * Otherwise returns NULL. 336 */ 337 char *strip_path_suffix(const char *path, const char *suffix) 338 { 339 int path_len = strlen(path), suffix_len = strlen(suffix); 340 341 while (suffix_len) { 342 if (!path_len) 343 return NULL; 344 345 if (is_dir_sep(path[path_len - 1])) { 346 if (!is_dir_sep(suffix[suffix_len - 1])) 347 return NULL; 348 path_len = chomp_trailing_dir_sep(path, path_len); 349 suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); 350 } 351 else if (path[--path_len] != suffix[--suffix_len]) 352 return NULL; 353 } 354 355 if (path_len && !is_dir_sep(path[path_len - 1])) 356 return NULL; 357 return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); 358 } 359