dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 97.3% 73 / 0 / 75
Functions: 100.0% 7 / 0 / 7
Branches: 88.3% 53 / 8 / 68

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