path.c 7.8 KB

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