dte test coverage


Directory: ./
File: src/util/path.h
Date: 2025-09-07 23:01:39
Exec Total Coverage
Lines: 34 34 100.0%
Functions: 7 7 100.0%
Branches: 20 20 100.0%

Line Branch Exec Source
1 #ifndef UTIL_PATH_H
2 #define UTIL_PATH_H
3
4 #include <stdbool.h>
5 #include <stddef.h>
6 #include <string.h>
7 #include "debug.h"
8 #include "macros.h"
9 #include "string-view.h"
10 #include "xmalloc.h"
11
12 NONNULL_ARGS
13 323 static inline bool path_is_absolute(const char *path)
14 {
15 323 return path[0] == '/';
16 }
17
18 // filename must not contain trailing slashes (but it can be "/")
19 NONNULL_ARGS_AND_RETURN
20 395 static inline const char *path_basename(const char *filename)
21 {
22 395 const char *slash = strrchr(filename, '/');
23
2/2
✓ Branch 0 (2→3) taken 269 times.
✓ Branch 1 (2→4) taken 126 times.
395 return slash ? slash + 1 : filename;
24 }
25
26 NONNULL_ARGS
27 73 static inline StringView path_slice_dirname(const char *filename)
28 {
29 73 const char *slash = strrchr(filename, '/');
30
2/2
✓ Branch 0 (2→3) taken 11 times.
✓ Branch 1 (2→4) taken 62 times.
73 if (!slash) {
31 11 return strview(".");
32 }
33 62 bool slash_is_root_dir = (slash == filename);
34
2/2
✓ Branch 0 (4→5) taken 60 times.
✓ Branch 1 (4→6) taken 2 times.
62 return string_view(filename, slash_is_root_dir ? 1 : slash - filename);
35 }
36
37 XSTRDUP
38 49 static inline char *path_dirname(const char *filename)
39 {
40 49 const StringView dir = path_slice_dirname(filename);
41 49 return xstrcut(dir.data, dir.length);
42 }
43
44 119 static inline char *path_join_sv(StringView s1, StringView s2, bool trailing_slash)
45 {
46 119 const char slash[2] = "/";
47 119 size_t n1 = s1.length;
48 119 size_t n2 = s2.length;
49
4/4
✓ Branch 0 (2→3) taken 107 times.
✓ Branch 1 (2→4) taken 12 times.
✓ Branch 2 (3→4) taken 5 times.
✓ Branch 3 (3→5) taken 102 times.
119 size_t sep = n1 && n2 && s1.data[n1 - 1] != '/'; // Separating slash length (1 or 0)
50
4/4
✓ Branch 0 (5→6) taken 4 times.
✓ Branch 1 (5→7) taken 115 times.
✓ Branch 2 (6→7) taken 1 times.
✓ Branch 3 (6→8) taken 3 times.
119 size_t ts = trailing_slash && n2 && s2.data[n2 - 1] != '/'; // Trailing slash length (1 or 0)
51 119 char *path = xmalloc(xadd3(n1, n2, sep + ts + 1));
52
2/2
✓ Branch 0 (10→11) taken 3 times.
✓ Branch 1 (10→12) taken 116 times.
122 xmempcpy4(path, s1.data, n1, slash, sep, s2.data, n2, slash + !ts, 2 - !ts);
53 119 return path;
54 }
55
56 59 static inline char *path_join(const char *s1, const char *s2)
57 {
58 59 return path_join_sv(strview(s1), strview(s2), false);
59 }
60
61 // If path is the root directory, return false. Otherwise, mutate the
62 // path argument to become its parent directory and return true. Note
63 // that path *must* be canonical (i.e. as returned by path_absolute()).
64 11 static inline bool path_parent(StringView *path)
65 {
66 11 BUG_ON(!strview_has_prefix(*path, "/"));
67
2/2
✓ Branch 0 (5→6) taken 9 times.
✓ Branch 1 (5→14) taken 2 times.
11 if (unlikely(path->length == 1)) {
68 return false; // Root dir
69 }
70
71 // Remove up to 1 trailing slash
72
2/2
✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→11) taken 8 times.
9 if (unlikely(strview_remove_matching_suffix(path, "/"))) {
73 1 BUG_ON(strview_has_suffix(*path, "/"));
74 }
75
76 // Adjust the length, to exclude the last path component
77 9 ssize_t slash_idx = strview_memrchr_idx(*path, '/');
78 9 BUG_ON(slash_idx < 0);
79 9 path->length = MAX(slash_idx, 1); // Shortest valid path is "/"
80 9 return true;
81 }
82
83 char *path_absolute(const char *path) MALLOC NONNULL_ARGS;
84 char *path_relative(const char *absolute, const char *cwd) XSTRDUP;
85 char *short_filename(const char *absolute, StringView home_dir) XSTRDUP;
86 char *short_filename_cwd(const char *absolute, const char *cwd, StringView home_dir) XSTRDUP;
87 const char *path_slice_relative(const char *abs, const char *cwd) NONNULL_ARGS RETURNS_NONNULL;
88
89 #endif
90