Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <errno.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <unistd.h> | ||
4 | #include "path.h" | ||
5 | #include "str-util.h" | ||
6 | |||
7 | 63 | char *path_absolute(const char *path) | |
8 | { | ||
9 |
2/2✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 62 times.
|
63 | if (unlikely(path[0] == '\0')) { |
10 | 1 | errno = EINVAL; | |
11 | 1 | return NULL; | |
12 | } | ||
13 | |||
14 | 62 | char *abs = realpath(path, NULL); | |
15 |
3/4✓ Branch 0 (5→6) taken 28 times.
✓ Branch 1 (5→13) taken 34 times.
✓ Branch 2 (6→7) taken 28 times.
✗ Branch 3 (6→13) not taken.
|
62 | if (abs || errno != ENOENT) { |
16 | return abs; | ||
17 | } | ||
18 | |||
19 | 28 | const char *base = path_basename(path); | |
20 | 28 | char *dir_relative = path_dirname(path); | |
21 | 28 | char *dir = realpath(dir_relative, NULL); | |
22 | 28 | free(dir_relative); | |
23 |
2/2✓ Branch 0 (9→10) taken 2 times.
✓ Branch 1 (9→11) taken 26 times.
|
28 | if (!dir) { |
24 | 2 | errno = ENOENT; | |
25 | 2 | return NULL; | |
26 | } | ||
27 | |||
28 | // If the full path doesn't exist but the directory part does, return | ||
29 | // the concatenation of the real directory and the non-existent filename | ||
30 | 26 | abs = path_join(dir, base); | |
31 | 26 | free(dir); | |
32 | 26 | return abs; | |
33 | } | ||
34 | |||
35 | 115 | static bool path_component(const char *path, size_t pos) | |
36 | { | ||
37 |
5/6✓ Branch 0 (2→3) taken 114 times.
✓ Branch 1 (2→6) taken 1 times.
✓ Branch 2 (3→4) taken 114 times.
✗ Branch 3 (3→6) not taken.
✓ Branch 4 (4→5) taken 4 times.
✓ Branch 5 (4→6) taken 110 times.
|
115 | return path[pos] == '\0' || pos == 0 || path[pos - 1] == '/'; |
38 | } | ||
39 | |||
40 | // Like path_relative(), but always returning either a "borrowed" pointer | ||
41 | // into `abs` or the static string ".", thus avoiding the need to allocate. | ||
42 | // This most notably means that "../" path components are never used, even | ||
43 | // if doing so would produce a path shorter than `abs`. | ||
44 | 8 | const char *path_slice_relative(const char *abs, const char *cwd) | |
45 | { | ||
46 | 8 | BUG_ON(!path_is_absolute(cwd)); | |
47 | 8 | BUG_ON(!path_is_absolute(abs)); | |
48 | |||
49 | // Special case | ||
50 |
2/2✓ Branch 0 (6→7) taken 2 times.
✓ Branch 1 (6→10) taken 6 times.
|
8 | if (cwd[1] == '\0') { |
51 |
2/2✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→9) taken 1 times.
|
2 | return abs[1] == '\0' ? abs : abs + 1; |
52 | } | ||
53 | |||
54 | 6 | size_t clen = str_common_prefix_length(cwd, abs); | |
55 |
2/2✓ Branch 0 (10→11) taken 4 times.
✓ Branch 1 (10→14) taken 2 times.
|
6 | if (cwd[clen] == '\0') { |
56 |
3/3✓ Branch 0 (11→12) taken 1 times.
✓ Branch 1 (11→13) taken 2 times.
✓ Branch 2 (11→14) taken 1 times.
|
4 | switch (abs[clen]) { |
57 | 1 | case '\0': | |
58 | // Identical strings; abs is current directory | ||
59 | 1 | return "."; | |
60 | 2 | case '/': | |
61 | 2 | return abs + clen + 1; | |
62 | } | ||
63 | } | ||
64 | |||
65 | return abs; | ||
66 | } | ||
67 | |||
68 | 138 | char *path_relative(const char *abs, const char *cwd) | |
69 | { | ||
70 | 138 | BUG_ON(!path_is_absolute(cwd)); | |
71 | 138 | BUG_ON(!path_is_absolute(abs)); | |
72 | |||
73 | // Special case | ||
74 |
2/2✓ Branch 0 (6→7) taken 2 times.
✓ Branch 1 (6→10) taken 136 times.
|
138 | if (cwd[1] == '\0') { |
75 |
2/2✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→9) taken 1 times.
|
2 | return xstrdup(abs[1] == '\0' ? abs : abs + 1); |
76 | } | ||
77 | |||
78 | 136 | size_t clen = str_common_prefix_length(cwd, abs); | |
79 |
2/2✓ Branch 0 (10→11) taken 78 times.
✓ Branch 1 (10→14) taken 58 times.
|
136 | if (cwd[clen] == '\0') { |
80 |
3/3✓ Branch 0 (11→12) taken 2 times.
✓ Branch 1 (11→13) taken 75 times.
✓ Branch 2 (11→14) taken 1 times.
|
78 | switch (abs[clen]) { |
81 | 2 | case '\0': | |
82 | // Identical strings; abs is current directory | ||
83 | 2 | return xstrdup("."); | |
84 | 75 | case '/': | |
85 | // cwd = /home/user | ||
86 | // abs = /home/user/project-a/file.c | ||
87 | // common = /home/user | ||
88 | 75 | return xstrdup(abs + clen + 1); | |
89 | } | ||
90 | } | ||
91 | |||
92 | // Common path components | ||
93 |
4/4✓ Branch 0 (14→15) taken 56 times.
✓ Branch 1 (14→16) taken 3 times.
✓ Branch 2 (15→16) taken 1 times.
✓ Branch 3 (15→19) taken 55 times.
|
59 | if (!path_component(cwd, clen) || !path_component(abs, clen)) { |
94 |
3/4✓ Branch 0 (17→18) taken 17 times.
✗ Branch 1 (17→19) not taken.
✓ Branch 2 (18→17) taken 13 times.
✓ Branch 3 (18→19) taken 4 times.
|
17 | while (clen > 0 && abs[clen - 1] != '/') { |
95 | clen--; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | // Number of "../" needed | ||
100 | 59 | size_t dotdot = 1; | |
101 | 59 | const char *slash = strchr(cwd + clen + 1, '/'); | |
102 |
2/2✓ Branch 0 (19→20) taken 54 times.
✓ Branch 1 (19→22) taken 5 times.
|
59 | if (slash) { |
103 | 54 | dotdot++; | |
104 |
2/2✓ Branch 0 (20→21) taken 4 times.
✓ Branch 1 (20→22) taken 50 times.
|
54 | if (strchr(slash + 1, '/')) { |
105 | // Just use absolute path if `dotdot` would be > 2 | ||
106 | 4 | return xstrdup(abs); | |
107 | } | ||
108 | } | ||
109 | |||
110 | 55 | const char *tail = abs + clen; | |
111 | 55 | return xmemjoin("../../", 3 * dotdot, tail, strlen(tail) + 1); | |
112 | } | ||
113 | |||
114 | 128 | static bool path_has_dir_prefix(const char *path, size_t pathlen, const StringView *dir) | |
115 | { | ||
116 |
4/4✓ Branch 0 (3→4) taken 3 times.
✓ Branch 1 (3→5) taken 125 times.
✓ Branch 2 (4→5) taken 1 times.
✓ Branch 3 (4→6) taken 2 times.
|
128 | return strn_has_strview_prefix(path, pathlen, dir) && path[dir->length] == '/'; |
117 | } | ||
118 | |||
119 | 128 | char *short_filename_cwd(const char *abs, const char *cwd, const StringView *home) | |
120 | { | ||
121 | 128 | char *rel = path_relative(abs, cwd); | |
122 | 128 | size_t abs_len = strlen(abs); | |
123 | 128 | size_t rel_len = strlen(rel); | |
124 | 128 | size_t suffix_len = (abs_len - home->length) + 1; | |
125 | |||
126 |
3/4✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→7) taken 126 times.
✓ Branch 2 (5→6) taken 2 times.
✗ Branch 3 (5→7) not taken.
|
128 | if (path_has_dir_prefix(abs, abs_len, home) && suffix_len < rel_len) { |
127 | // Prefer absolute in tilde notation (e.g. "~/abs/path"), if applicable | ||
128 | // and shorter than relative | ||
129 | 2 | rel[0] = '~'; | |
130 | 2 | memcpy(rel + 1, abs + home->length, suffix_len); | |
131 |
2/2✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→9) taken 125 times.
|
126 | } else if (abs_len < rel_len) { |
132 | // Prefer absolute, if shorter than relative | ||
133 | 1 | memcpy(rel, abs, abs_len + 1); | |
134 | } | ||
135 | |||
136 | 128 | return rel; | |
137 | } | ||
138 | |||
139 | 50 | char *short_filename(const char *abs, const StringView *home) | |
140 | { | ||
141 | 50 | char buf[8192]; | |
142 | 50 | const char *cwd = getcwd(buf, sizeof buf); | |
143 |
1/2✓ Branch 0 (3→4) taken 50 times.
✗ Branch 1 (3→5) not taken.
|
50 | return likely(cwd) ? short_filename_cwd(abs, cwd, home) : xstrdup(abs); |
144 | } | ||
145 |