]> git.proxmox.com Git - libgit2.git/blame - src/path.h
submodule: normalize slashes in resolve_url
[libgit2.git] / src / path.h
CommitLineData
f79026b4 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
bb742ede
VM
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
f79026b4
VM
6 */
7#ifndef INCLUDE_path_h__
8#define INCLUDE_path_h__
9
10#include "common.h"
2f795d8f 11#include "posix.h"
97769280 12#include "buffer.h"
b6c93aef 13#include "vector.h"
f79026b4 14
1744fafe
RB
15/**
16 * Path manipulation utils
17 *
18 * These are path utilities that munge paths without actually
19 * looking at the real filesystem.
20 */
21
f79026b4
VM
22/*
23 * The dirname() function shall take a pointer to a character string
24 * that contains a pathname, and return a pointer to a string that is a
25 * pathname of the parent directory of that file. Trailing '/' characters
26 * in the path are not counted as part of the path.
27 *
28 * If path does not contain a '/', then dirname() shall return a pointer to
29 * the string ".". If path is a null pointer or points to an empty string,
30 * dirname() shall return a pointer to the string "." .
31 *
32 * The `git_path_dirname` implementation is thread safe. The returned
33 * string must be manually free'd.
34 *
97769280
RB
35 * The `git_path_dirname_r` implementation writes the dirname to a `git_buf`
36 * if the buffer pointer is not NULL.
37 * It returns an error code < 0 if there is an allocation error, otherwise
38 * the length of the dirname (which will be > 0).
f79026b4
VM
39 */
40extern char *git_path_dirname(const char *path);
97769280 41extern int git_path_dirname_r(git_buf *buffer, const char *path);
f79026b4
VM
42
43/*
44 * This function returns the basename of the file, which is the last
45 * part of its full name given by fname, with the drive letter and
46 * leading directories stripped off. For example, the basename of
47 * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo.
48 *
49 * Trailing slashes and backslashes are significant: the basename of
50 * c:/foo/bar/ is an empty string after the rightmost slash.
51 *
52 * The `git_path_basename` implementation is thread safe. The returned
53 * string must be manually free'd.
54 *
97769280
RB
55 * The `git_path_basename_r` implementation writes the basename to a `git_buf`.
56 * It returns an error code < 0 if there is an allocation error, otherwise
57 * the length of the basename (which will be >= 0).
f79026b4
VM
58 */
59extern char *git_path_basename(const char *path);
97769280 60extern int git_path_basename_r(git_buf *buffer, const char *path);
f79026b4 61
ca1b6e54
RB
62/* Return the offset of the start of the basename. Unlike the other
63 * basename functions, this returns 0 if the path is empty.
64 */
65extern size_t git_path_basename_offset(git_buf *buffer);
66
f79026b4
VM
67extern const char *git_path_topdir(const char *path);
68
1744fafe
RB
69/**
70 * Find offset to root of path if path has one.
71 *
72 * This will return a number >= 0 which is the offset to the start of the
73 * path, if the path is rooted (i.e. "/rooted/path" returns 0 and
74 * "c:/windows/rooted/path" returns 2). If the path is not rooted, this
75 * returns < 0.
76 */
97769280 77extern int git_path_root(const char *path);
f79026b4 78
1744fafe
RB
79/**
80 * Ensure path has a trailing '/'.
81 */
97769280 82extern int git_path_to_dir(git_buf *path);
1744fafe
RB
83
84/**
85 * Ensure string has a trailing '/' if there is space for it.
86 */
97769280 87extern void git_path_string_to_dir(char* path, size_t size);
5ad739e8 88
d024419f
BS
89/**
90 * Taken from git.git; returns nonzero if the given path is "." or "..".
91 */
c3b5099f
BS
92GIT_INLINE(int) git_path_is_dot_or_dotdot(const char *name)
93{
94 return (name[0] == '.' &&
95 (name[1] == '\0' ||
96 (name[1] == '.' && name[2] == '\0')));
97}
98
f79026b4 99#ifdef GIT_WIN32
339f3d07
BS
100GIT_INLINE(int) git_path_is_dot_or_dotdotW(const wchar_t *name)
101{
102 return (name[0] == L'.' &&
103 (name[1] == L'\0' ||
104 (name[1] == L'.' && name[2] == L'\0')));
105}
106
1744fafe
RB
107/**
108 * Convert backslashes in path to forward slashes.
109 */
f79026b4
VM
110GIT_INLINE(void) git_path_mkposix(char *path)
111{
112 while (*path) {
113 if (*path == '\\')
114 *path = '/';
115
116 path++;
117 }
118}
119#else
120# define git_path_mkposix(p) /* blank */
121#endif
122
e402d2f1
RB
123/**
124 * Check if string is a relative path (i.e. starts with "./" or "../")
125 */
126GIT_INLINE(int) git_path_is_relative(const char *p)
127{
128 return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/')));
129}
130
f25bc0b2
RB
131/**
132 * Check if string is at end of path segment (i.e. looking at '/' or '\0')
133 */
134GIT_INLINE(int) git_path_at_end_of_segment(const char *p)
135{
136 return !*p || *p == '/';
137}
138
459e2dcd 139extern int git__percent_decode(git_buf *decoded_out, const char *input);
1744fafe
RB
140
141/**
142 * Extract path from file:// URL.
143 */
2017a15d 144extern int git_path_fromurl(git_buf *local_path_out, const char *file_url);
459e2dcd 145
1744fafe
RB
146
147/**
148 * Path filesystem utils
149 *
150 * These are path utilities that actually access the filesystem.
151 */
152
153/**
154 * Check if a file exists and can be accessed.
1a481123 155 * @return true or false
1744fafe 156 */
1a481123 157extern bool git_path_exists(const char *path);
1744fafe
RB
158
159/**
160 * Check if the given path points to a directory.
1a481123 161 * @return true or false
1744fafe 162 */
1a481123 163extern bool git_path_isdir(const char *path);
1744fafe 164
0cfcff5d 165/**
1744fafe 166 * Check if the given path points to a regular file.
1a481123 167 * @return true or false
1744fafe 168 */
1a481123 169extern bool git_path_isfile(const char *path);
1744fafe 170
d024419f
BS
171/**
172 * Check if the given path is a directory, and is empty.
173 */
174extern bool git_path_is_empty_dir(const char *path);
175
deafee7b
RB
176/**
177 * Stat a file and/or link and set error if needed.
178 */
179extern int git_path_lstat(const char *path, struct stat *st);
180
b6c93aef
RB
181/**
182 * Check if the parent directory contains the item.
183 *
184 * @param dir Directory to check.
185 * @param item Item that might be in the directory.
0d0fa7c3 186 * @return 0 if item exists in directory, <0 otherwise.
b6c93aef 187 */
1a481123 188extern bool git_path_contains(git_buf *dir, const char *item);
b6c93aef 189
1744fafe
RB
190/**
191 * Check if the given path contains the given subdirectory.
192 *
193 * @param parent Directory path that might contain subdir
194 * @param subdir Subdirectory name to look for in parent
1a481123 195 * @return true if subdirectory exists, false otherwise.
1744fafe 196 */
1a481123 197extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
1744fafe 198
0ee9f31c
ET
199/**
200 * Make the path relative to the given parent path.
201 *
202 * @param path The path to make relative
203 * @param parent The parent path to make path relative to
204 * @return 0 if path was made relative, GIT_ENOTFOUND
205 * if there was not common root between the paths,
206 * or <0.
207 */
208extern int git_path_make_relative(git_buf *path, const char *parent);
209
1744fafe
RB
210/**
211 * Check if the given path contains the given file.
212 *
213 * @param dir Directory path that might contain file
214 * @param file File name to look for in parent
1a481123 215 * @return true if file exists, false otherwise.
1744fafe 216 */
1a481123 217extern bool git_path_contains_file(git_buf *dir, const char *file);
1744fafe 218
ca1b6e54
RB
219/**
220 * Prepend base to unrooted path or just copy path over.
221 *
222 * This will optionally return the index into the path where the "root"
223 * is, either the end of the base directory prefix or the path root.
224 */
225extern int git_path_join_unrooted(
226 git_buf *path_out, const char *path, const char *base, ssize_t *root_at);
227
1744fafe
RB
228/**
229 * Clean up path, prepending base if it is not already rooted.
230 */
231extern int git_path_prettify(git_buf *path_out, const char *path, const char *base);
232
233/**
234 * Clean up path, prepending base if it is not already rooted and
235 * appending a slash.
236 */
237extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base);
238
239/**
240 * Get a directory from a path.
241 *
242 * If path is a directory, this acts like `git_path_prettify_dir`
243 * (cleaning up path and appending a '/'). If path is a normal file,
244 * this prettifies it, then removed the filename a la dirname and
245 * appends the trailing '/'. If the path does not exist, it is
246 * treated like a regular filename.
247 */
248extern int git_path_find_dir(git_buf *dir, const char *path, const char *base);
249
b0fe1129
RB
250/**
251 * Resolve relative references within a path.
252 *
253 * This eliminates "./" and "../" relative references inside a path,
254 * as well as condensing multiple slashes into single ones. It will
255 * not touch the path before the "ceiling" length.
256 *
257 * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL
258 * prefix and not touch that part of the path.
259 */
260extern int git_path_resolve_relative(git_buf *path, size_t ceiling);
261
262/**
263 * Apply a relative path to base path.
264 *
265 * Note that the base path could be a filename or a URL and this
266 * should still work. The relative path is walked segment by segment
267 * with three rules: series of slashes will be condensed to a single
268 * slash, "." will be eaten with no change, and ".." will remove a
269 * segment from the base path.
270 */
271extern int git_path_apply_relative(git_buf *target, const char *relpath);
272
219d3457
RB
273enum {
274 GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
275 GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
edbfc52c 276 GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT = (1u << 2),
219d3457
RB
277};
278
1744fafe
RB
279/**
280 * Walk each directory entry, except '.' and '..', calling fn(state).
281 *
219d3457 282 * @param pathbuf Buffer the function reads the initial directory
1744fafe 283 * path from, and updates with each successive entry's name.
219d3457
RB
284 * @param flags Combination of GIT_PATH_DIR flags.
285 * @param callback Callback for each entry. Passed the `payload` and each
286 * successive path inside the directory as a full path. This may
25e0b157
RB
287 * safely append text to the pathbuf if needed. Return non-zero to
288 * cancel iteration (and return value will be propagated back).
219d3457 289 * @param payload Passed to callback as first argument.
25e0b157 290 * @return 0 on success or error code from OS error or from callback
1744fafe
RB
291 */
292extern int git_path_direach(
293 git_buf *pathbuf,
219d3457
RB
294 uint32_t flags,
295 int (*callback)(void *payload, git_buf *path),
296 void *payload);
1744fafe
RB
297
298/**
23594c1d 299 * Sort function to order two paths
1744fafe
RB
300 */
301extern int git_path_cmp(
44ef8b1b 302 const char *name1, size_t len1, int isdir1,
0c468633
RB
303 const char *name2, size_t len2, int isdir2,
304 int (*compare)(const char *, const char *, size_t));
23594c1d 305
1744fafe
RB
306/**
307 * Invoke callback up path directory by directory until the ceiling is
308 * reached (inclusive of a final call at the root_path).
309 *
0d0fa7c3 310 * Returning anything other than 0 from the callback function
b874629b 311 * will stop the iteration and propagate the error to the caller.
df743c7d 312 *
1744fafe
RB
313 * @param pathbuf Buffer the function reads the directory from and
314 * and updates with each successive name.
315 * @param ceiling Prefix of path at which to stop walking up. If NULL,
219d3457
RB
316 * this will walk all the way up to the root. If not a prefix of
317 * pathbuf, the callback will be invoked a single time on the
318 * original input path.
319 * @param callback Function to invoke on each path. Passed the `payload`
320 * and the buffer containing the current path. The path should not
25e0b157 321 * be modified in any way. Return non-zero to stop iteration.
1744fafe 322 * @param state Passed to fn as the first ath.
df743c7d 323 */
0cfcff5d 324extern int git_path_walk_up(
1744fafe
RB
325 git_buf *pathbuf,
326 const char *ceiling,
bbb988a5 327 int (*callback)(void *payload, const char *path),
219d3457 328 void *payload);
2fe54afa 329
be3f1049
ET
330
331enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 };
332
333/*
334 * Determines if a path is equal to or potentially a child of another.
335 * @param parent The possible parent
336 * @param child The possible child
337 */
338GIT_INLINE(int) git_path_equal_or_prefixed(
339 const char *parent,
340 const char *child,
341 ssize_t *prefixlen)
342{
343 const char *p = parent, *c = child;
344 int lastslash = 0;
345
346 while (*p && *c) {
347 lastslash = (*p == '/');
348
349 if (*p++ != *c++)
350 return GIT_PATH_NOTEQUAL;
351 }
352
353 if (*p != '\0')
354 return GIT_PATH_NOTEQUAL;
355
356 if (*c == '\0') {
357 if (prefixlen)
358 *prefixlen = p - parent;
359
360 return GIT_PATH_EQUAL;
361 }
362
363 if (*c == '/' || lastslash) {
364 if (prefixlen)
365 *prefixlen = (p - parent) - lastslash;
366
367 return GIT_PATH_PREFIX;
368 }
369
370 return GIT_PATH_NOTEQUAL;
371}
372
373/* translate errno to libgit2 error code and set error message */
374extern int git_path_set_error(
375 int errno_value, const char *path, const char *action);
376
377/* check if non-ascii characters are present in filename */
378extern bool git_path_has_non_ascii(const char *path, size_t pathlen);
379
380#define GIT_PATH_REPO_ENCODING "UTF-8"
381
382#ifdef __APPLE__
383#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
384#else
385#define GIT_PATH_NATIVE_ENCODING "UTF-8"
386#endif
387
388#ifdef GIT_USE_ICONV
389
390#include <iconv.h>
391
392typedef struct {
393 iconv_t map;
394 git_buf buf;
395} git_path_iconv_t;
396
397#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_BUF_INIT }
398
399/* Init iconv data for converting decomposed UTF-8 to precomposed */
400extern int git_path_iconv_init_precompose(git_path_iconv_t *ic);
401
402/* Clear allocated iconv data */
403extern void git_path_iconv_clear(git_path_iconv_t *ic);
404
405/*
406 * Rewrite `in` buffer using iconv map if necessary, replacing `in`
407 * pointer internal iconv buffer if rewrite happened. The `in` pointer
408 * will be left unchanged if no rewrite was needed.
409 */
25bd0aaf 410extern int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen);
be3f1049
ET
411
412#endif /* GIT_USE_ICONV */
413
414extern bool git_path_does_fs_decompose_unicode(const char *root);
415
416
edbfc52c
ET
417typedef struct git_path_diriter git_path_diriter;
418
f63a1b72
ET
419#if defined(GIT_WIN32) && !defined(__MINGW32__)
420
421struct git_path_diriter
422{
423 git_win32_path path;
424 size_t parent_len;
425
426 git_buf path_utf8;
427 size_t parent_utf8_len;
428
429 HANDLE handle;
430
431 unsigned int flags;
432
433 WIN32_FIND_DATAW current;
434 unsigned int needs_next;
435};
436
437#define GIT_PATH_DIRITER_INIT { {0}, 0, GIT_BUF_INIT, 0, INVALID_HANDLE_VALUE }
438
439#else
440
edbfc52c
ET
441struct git_path_diriter
442{
443 git_buf path;
444 size_t parent_len;
445
446 unsigned int flags;
447
448 DIR *dir;
be3f1049
ET
449
450#ifdef GIT_USE_ICONV
451 git_path_iconv_t ic;
452#endif
edbfc52c
ET
453};
454
f63a1b72
ET
455#define GIT_PATH_DIRITER_INIT { GIT_BUF_INIT }
456
457#endif
458
5c387b6c
ET
459/**
460 * Initialize a directory iterator.
461 *
462 * @param diriter Pointer to a diriter structure that will be setup.
463 * @param path The path that will be iterated over
464 * @param flags Directory reader flags
465 * @return 0 or an error code
466 */
edbfc52c
ET
467extern int git_path_diriter_init(
468 git_path_diriter *diriter,
469 const char *path,
470 unsigned int flags);
471
5c387b6c
ET
472/**
473 * Advance the directory iterator. Will return GIT_ITEROVER when
474 * the iteration has completed successfully.
475 *
476 * @param diriter The directory iterator
477 * @return 0, GIT_ITEROVER, or an error code
478 */
479extern int git_path_diriter_next(git_path_diriter *diriter);
480
481/**
482 * Returns the file name of the current item in the iterator.
483 *
484 * @param out Pointer to store the path in
485 * @param out_len Pointer to store the length of the path in
486 * @param diriter The directory iterator
487 * @return 0 or an error code
488 */
489extern int git_path_diriter_filename(
edbfc52c
ET
490 const char **out,
491 size_t *out_len,
492 git_path_diriter *diriter);
493
5c387b6c
ET
494/**
495 * Returns the full path of the current item in the iterator; that
496 * is the current filename plus the path of the directory that the
497 * iterator was constructed with.
498 *
499 * @param out Pointer to store the path in
500 * @param out_len Pointer to store the length of the path in
501 * @param diriter The directory iterator
502 * @return 0 or an error code
503 */
edbfc52c
ET
504extern int git_path_diriter_fullpath(
505 const char **out,
506 size_t *out_len,
507 git_path_diriter *diriter);
508
5c387b6c
ET
509/**
510 * Performs an `lstat` on the current item in the iterator.
511 *
512 * @param out Pointer to store the stat data in
513 * @param diriter The directory iterator
514 * @return 0 or an error code
515 */
edbfc52c
ET
516extern int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter);
517
5c387b6c
ET
518/**
519 * Closes the directory iterator.
520 *
521 * @param diriter The directory iterator
522 */
edbfc52c
ET
523extern void git_path_diriter_free(git_path_diriter *diriter);
524
b6c93aef
RB
525/**
526 * Load all directory entries (except '.' and '..') into a vector.
527 *
528 * For cases where `git_path_direach()` is not appropriate, this
529 * allows you to load the filenames in a directory into a vector
530 * of strings. That vector can then be sorted, iterated, or whatever.
531 * Remember to free alloc of the allocated strings when you are done.
532 *
07bbc045 533 * @param contents Vector to fill with directory entry names.
b6c93aef
RB
534 * @param path The directory to read from.
535 * @param prefix_len When inserting entries, the trailing part of path
536 * will be prefixed after this length. I.e. given path "/a/b" and
537 * prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
219d3457 538 * @param flags Combination of GIT_PATH_DIR flags.
b6c93aef
RB
539 */
540extern int git_path_dirload(
07bbc045 541 git_vector *contents,
b6c93aef
RB
542 const char *path,
543 size_t prefix_len,
07bbc045 544 uint32_t flags);
74fa4bfa 545
43a04135 546
18d7896c 547/* Used for paths to repositories on the filesystem */
529fd30d 548extern bool git_path_is_local_file_url(const char *file_url);
18d7896c
CMN
549extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
550
a64119e3
ET
551/* Flags to determine path validity in `git_path_isvalid` */
552#define GIT_PATH_REJECT_TRAVERSAL (1 << 0)
553#define GIT_PATH_REJECT_DOT_GIT (1 << 1)
ec74b40c
ET
554#define GIT_PATH_REJECT_SLASH (1 << 2)
555#define GIT_PATH_REJECT_BACKSLASH (1 << 3)
556#define GIT_PATH_REJECT_TRAILING_DOT (1 << 4)
557#define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5)
558#define GIT_PATH_REJECT_TRAILING_COLON (1 << 6)
a64119e3
ET
559#define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
560#define GIT_PATH_REJECT_NT_CHARS (1 << 8)
11d67b75 561#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9)
ec74b40c 562#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10)
a64119e3 563
ec74b40c
ET
564/* Default path safety for writing files to disk: since we use the
565 * Win32 "File Namespace" APIs ("\\?\") we need to protect from
566 * paths that the normal Win32 APIs would not write.
567 */
a64119e3
ET
568#ifdef GIT_WIN32
569# define GIT_PATH_REJECT_DEFAULTS \
570 GIT_PATH_REJECT_TRAVERSAL | \
571 GIT_PATH_REJECT_BACKSLASH | \
572 GIT_PATH_REJECT_TRAILING_DOT | \
573 GIT_PATH_REJECT_TRAILING_SPACE | \
574 GIT_PATH_REJECT_TRAILING_COLON | \
a64119e3
ET
575 GIT_PATH_REJECT_DOS_PATHS | \
576 GIT_PATH_REJECT_NT_CHARS
577#else
578# define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL
579#endif
580
581/*
582 * Determine whether a path is a valid git path or not - this must not contain
583 * a '.' or '..' component, or a component that is ".git" (in any case).
584 *
585 * `repo` is optional. If specified, it will be used to determine the short
586 * path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified),
587 * in addition to the default of "git~1".
588 */
589extern bool git_path_isvalid(
590 git_repository *repo,
591 const char *path,
592 unsigned int flags);
593
f79026b4 594#endif