102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
path.c
Go to the documentation of this file.
1#include <dttr_path.h>
2
3#include <string.h>
4
5#include <windows.h>
6
7static bool copy_path_value(
8 char *out,
9 size_t out_size,
10 const char *value,
11 size_t value_len,
12 bool allow_empty
13) {
14 if (!value || (!allow_empty && value_len == 0) || value_len >= out_size) {
15 return false;
16 }
17
18 memcpy(out, value, value_len);
19 out[value_len] = '\0';
20 return true;
21}
22
23static const char *skip_dot_separators(const char *path) {
24 while (path[0] == '.' && DTTR_Path_IsSeparator(path[1])) {
25 path += 2;
26 }
27
28 return path;
29}
30
31static void trim_trailing_separators(sds path) {
32 const size_t path_len = sdslen(path);
33 size_t len = path_len;
34 while (len > 0 && DTTR_Path_IsSeparator(path[len - 1])) {
35 len--;
36 }
37
38 if (len == path_len) {
39 return;
40 }
41
42 if (len == 0) {
43 sdsclear(path);
44 return;
45 }
46
47 sdsrange(path, 0, (int)len - 1);
48}
49
50char DTTR_Path_AsciiLower(char ch) {
51 if (ch >= 'A' && ch <= 'Z') {
52 return (char)(ch - 'A' + 'a');
53 }
54
55 return ch;
56}
57
58bool DTTR_Path_AsciiIeqN(const char *lhs, const char *rhs, size_t n) {
59 for (size_t i = 0; i < n; i++) {
60 if (DTTR_Path_AsciiLower(lhs[i]) != DTTR_Path_AsciiLower(rhs[i])) {
61 return false;
62 }
63 }
64
65 return true;
66}
67
68bool DTTR_Path_CopyString(char *out, size_t out_size, const char *value) {
69 return copy_path_value(out, out_size, value, value ? strlen(value) : 0, false);
70}
71
72bool DTTR_Path_CopySds(char *out, size_t out_size, sds value) {
73 return copy_path_value(out, out_size, value, value ? sdslen(value) : 0, true);
74}
75
76bool DTTR_Path_IsSeparator(char ch) {
77 return ch == '\\' || ch == '/';
78}
79
80const char *DTTR_Path_SkipSeparators(const char *path) {
81 while (*path && DTTR_Path_IsSeparator(*path)) {
82 path++;
83 }
84
85 return path;
86}
87
88size_t DTTR_Path_SegmentLen(const char *path) {
89 size_t len = 0;
90
91 while (path[len] && !DTTR_Path_IsSeparator(path[len])) {
92 len++;
93 }
94
95 return len;
96}
97
98bool DTTR_Path_IsRelativeSegment(const char *segment, size_t segment_len) {
99 return segment_len == 0 || (segment_len == 1 && segment[0] == '.')
100 || (segment_len == 2 && segment[0] == '.' && segment[1] == '.');
101}
102
103static bool path_has_windows_drive_prefix(const char *path) {
104 return path && path[0] && path[1] == ':';
105}
106
107bool DTTR_Path_IsSafeRelative(const char *path) {
108 if (!path || DTTR_Path_IsAnyAbsolute(path) || path_has_windows_drive_prefix(path)) {
109 return false;
110 }
111
112 const char *p = path;
113 if (!*p) {
114 return false;
115 }
116
117 while (*p) {
118 const size_t segment_len = DTTR_Path_SegmentLen(p);
119 if (DTTR_Path_IsRelativeSegment(p, segment_len)) {
120 return false;
121 }
122
123 p = DTTR_Path_SkipSeparators(p + segment_len);
124 }
125
126 return true;
127}
128
129static sds normalize_path_for_compare(const char *path) {
130 if (!path) {
131 return sdsempty();
132 }
133
134 path = skip_dot_separators(path);
135
136 sds normalized = sdsempty();
137 if (!normalized) {
138 return NULL;
139 }
140
141 bool previous_separator = false;
142 for (const char *p = path; *p; p++) {
143 const bool is_separator = DTTR_Path_IsSeparator(*p);
144 const char ch = is_separator ? '/' : *p;
145 if (is_separator) {
146 if (previous_separator) {
147 continue;
148 }
149
150 previous_separator = true;
151 } else {
152 previous_separator = false;
153 }
154
155 if (!DTTR_Path_AppendChar(&normalized, ch)) {
156 sdsfree(normalized);
157 return NULL;
158 }
159 }
160
161 trim_trailing_separators(normalized);
162 return normalized;
163}
164
165bool DTTR_Path_MatchesNormalized(const char *lhs, const char *rhs) {
166 sds normalized_lhs = normalize_path_for_compare(lhs);
167 sds normalized_rhs = normalize_path_for_compare(rhs);
168 const bool matches = normalized_lhs && normalized_rhs
169 && strcmp(normalized_lhs, normalized_rhs) == 0;
170 sdsfree(normalized_lhs);
171 sdsfree(normalized_rhs);
172 return matches;
173}
174
175bool DTTR_Path_IsWindowsAbsolute(const char *path) {
176 return path && strlen(path) >= 3 && path[1] == ':' && DTTR_Path_IsSeparator(path[2]);
177}
178
179bool DTTR_Path_IsAnyAbsolute(const char *path) {
180 if (!path) {
181 return false;
182 }
183
184 return DTTR_Path_IsWindowsAbsolute(path) || DTTR_Path_IsSeparator(path[0]);
185}
186
187bool DTTR_Path_ExactExists(const char *path) {
188 return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
189}
190
192 char cwd[MAX_PATH];
193 DWORD len = GetCurrentDirectoryA((DWORD)sizeof(cwd), cwd);
194 if (len == 0 || len >= sizeof(cwd)) {
195 return NULL;
196 }
197
198 return sdsnew(cwd);
199}
200
201sds DTTR_Path_ModuleDir(void *module) {
202 char path[MAX_PATH];
203 const DWORD len = GetModuleFileNameA((HMODULE)module, path, (DWORD)sizeof(path));
204 if (len == 0 || len >= sizeof(path)) {
205 return NULL;
206 }
207
208 char *last_sep = strrchr(path, '\\');
209 if (!last_sep) {
210 return NULL;
211 }
212
213 last_sep[1] = '\0';
214 return sdsnew(path);
215}
216
217sds DTTR_Path_ModuleSibling(void *module, const char *relative_path) {
218 sds path = DTTR_Path_ModuleDir(module);
219 if (!path
220 || !DTTR_Path_AppendSegment(&path, relative_path, DTTR_PATH_NATIVE_SEPARATOR)) {
221 sdsfree(path);
222 return NULL;
223 }
224
225 return path;
226}
227
228sds DTTR_Path_ResolveRelativeTo(const char *base_dir, const char *path) {
229 if (!path) {
230 return NULL;
231 }
232
233 if (DTTR_Path_IsAnyAbsolute(path)) {
234 return sdsnew(path);
235 }
236
237 sds resolved = sdsnew(base_dir ? base_dir : "");
238 if (!resolved
240 sdsfree(resolved);
241 return NULL;
242 }
243
244 return resolved;
245}
246
247sds DTTR_Path_NativeRoot(const char *path, const char **rest) {
248 if (DTTR_Path_IsWindowsAbsolute(path)) {
249 *rest = path + 3;
250 return sdscatprintf(sdsempty(), "%c:%c", path[0], DTTR_PATH_NATIVE_SEPARATOR);
251 }
252
253 if (DTTR_Path_IsSeparator(path[0])) {
254 *rest = DTTR_Path_SkipSeparators(path);
255 return sdsnewlen(&(char){DTTR_PATH_NATIVE_SEPARATOR}, 1);
256 }
257
258 *rest = path;
259 return DTTR_Path_CurrentDir();
260}
261
262bool DTTR_Path_AppendChar(sds *path, char ch) {
263 sds next = sdscatlen(*path, &ch, 1);
264 if (!next) {
265 return false;
266 }
267
268 *path = next;
269 return true;
270}
271
272bool DTTR_Path_AppendSeparator(sds *path, char separator) {
273 return DTTR_Path_AppendChar(path, separator);
274}
275
276bool DTTR_Path_AppendSegment(sds *path, const char *segment, char separator) {
277 const size_t len = sdslen(*path);
278 if (len > 0 && !DTTR_Path_IsSeparator((*path)[len - 1])) {
279 if (!DTTR_Path_AppendChar(path, separator)) {
280 return false;
281 }
282 }
283
284 sds next = sdscat(*path, segment);
285 if (!next) {
286 return false;
287 }
288
289 *path = next;
290 return true;
291}
DTTR_Graphics_COM_Direct3DDevice7 void *status DTTR_Graphics_COM_Direct3DDevice7 DWORD DWORD void DWORD n
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
#define DTTR_PATH_NATIVE_SEPARATOR
Definition dttr_path.h:9
static const char * skip_dot_separators(const char *path)
Definition path.c:23
bool DTTR_Path_AsciiIeqN(const char *lhs, const char *rhs, size_t n)
Definition path.c:58
sds DTTR_Path_NativeRoot(const char *path, const char **rest)
Definition path.c:247
bool DTTR_Path_IsSeparator(char ch)
Definition path.c:76
static void trim_trailing_separators(sds path)
Definition path.c:31
const char * DTTR_Path_SkipSeparators(const char *path)
Definition path.c:80
sds DTTR_Path_CurrentDir()
Definition path.c:191
sds DTTR_Path_ModuleSibling(void *module, const char *relative_path)
Definition path.c:217
bool DTTR_Path_IsRelativeSegment(const char *segment, size_t segment_len)
Definition path.c:98
bool DTTR_Path_CopyString(char *out, size_t out_size, const char *value)
Definition path.c:68
bool DTTR_Path_CopySds(char *out, size_t out_size, sds value)
Definition path.c:72
bool DTTR_Path_ExactExists(const char *path)
Definition path.c:187
bool DTTR_Path_AppendChar(sds *path, char ch)
Definition path.c:262
bool DTTR_Path_IsWindowsAbsolute(const char *path)
Definition path.c:175
sds DTTR_Path_ResolveRelativeTo(const char *base_dir, const char *path)
Definition path.c:228
static bool path_has_windows_drive_prefix(const char *path)
Definition path.c:103
sds DTTR_Path_ModuleDir(void *module)
Definition path.c:201
bool DTTR_Path_IsAnyAbsolute(const char *path)
Definition path.c:179
static bool copy_path_value(char *out, size_t out_size, const char *value, size_t value_len, bool allow_empty)
Definition path.c:7
bool DTTR_Path_AppendSeparator(sds *path, char separator)
Definition path.c:272
bool DTTR_Path_AppendSegment(sds *path, const char *segment, char separator)
Definition path.c:276
static sds normalize_path_for_compare(const char *path)
Definition path.c:129
char DTTR_Path_AsciiLower(char ch)
Definition path.c:50
size_t DTTR_Path_SegmentLen(const char *path)
Definition path.c:88
bool DTTR_Path_IsSafeRelative(const char *path)
Definition path.c:107
bool DTTR_Path_MatchesNormalized(const char *lhs, const char *rhs)
Definition path.c:165