dte test coverage


Directory: ./
File: src/util/path.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 69 69 100.0%
Functions: 7 7 100.0%
Branches: 51 56 91.1%

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