1 /*
2 * Copyright (c) 2016-2021, The OSKAR Developers.
3 * See the LICENSE file at the top-level directory of this distribution.
4 */
5
6 #include "utility/oskar_dir.h"
7
8 #ifndef OSKAR_OS_WIN
9 #define _BSD_SOURCE
10 #include <dirent.h>
11 #include <errno.h>
12 #include <limits.h>
13 #include <pwd.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #else
18 #define WIN32_LEAN_AND_MEAN
19 #include <windows.h>
20 #endif
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29
oskar_dir_cwd(void)30 char* oskar_dir_cwd(void)
31 {
32 char* str = 0;
33 size_t len = 0;
34 #ifndef OSKAR_OS_WIN
35 do
36 {
37 len += 256;
38 str = (char*) realloc(str, len);
39 if (!str) return 0;
40 }
41 while (getcwd(str, len) == NULL);
42 #else
43 len = GetCurrentDirectory(0, NULL);
44 str = (char*) calloc(len, sizeof(char));
45 if (!str) return 0;
46 GetCurrentDirectory((DWORD) len, str);
47 #endif
48 return str;
49 }
50
51
oskar_dir_exists(const char * dir_path)52 int oskar_dir_exists(const char* dir_path)
53 {
54 #ifndef OSKAR_OS_WIN
55 struct stat s;
56 return (!stat(dir_path, &s) && S_ISDIR(s.st_mode)) ? 1 : 0;
57 #else
58 int attrib = GetFileAttributes(dir_path);
59 return (attrib != INVALID_FILE_ATTRIBUTES &&
60 (attrib & FILE_ATTRIBUTE_DIRECTORY));
61 #endif
62 }
63
64
oskar_dir_file_exists(const char * dir_path,const char * file_name)65 int oskar_dir_file_exists(const char* dir_path, const char* file_name)
66 {
67 FILE* f = 0;
68 char* path = 0;
69 path = oskar_dir_get_path(dir_path, file_name);
70 f = fopen(path, "rb");
71 if (f)
72 {
73 fclose(f);
74 free(path);
75 return 1;
76 }
77 free(path);
78 return 0;
79 }
80
81
oskar_dir_get_home_path(const char * item_name,int * exists)82 char* oskar_dir_get_home_path(const char* item_name, int* exists)
83 {
84 char *path = 0, *home_dir = 0;
85 home_dir = oskar_dir_home();
86 path = oskar_dir_get_path(home_dir, item_name);
87 if (exists) *exists = oskar_dir_file_exists(home_dir, item_name);
88 free(home_dir);
89 return path;
90 }
91
92
oskar_dir_get_path(const char * dir_path,const char * item_name)93 char* oskar_dir_get_path(const char* dir_path, const char* item_name)
94 {
95 char* buffer = 0;
96 int buf_len = 0, dir_path_len = 0;
97 dir_path_len = (int) strlen(dir_path);
98 buf_len = 2 + dir_path_len + (int) strlen(item_name);
99 buffer = (char*) calloc(buf_len, sizeof(char));
100 if (!buffer) return 0;
101 if (dir_path[dir_path_len - 1] == oskar_dir_separator())
102 {
103 sprintf(buffer, "%s%s", dir_path, item_name);
104 }
105 else
106 {
107 sprintf(buffer, "%s%c%s", dir_path, oskar_dir_separator(), item_name);
108 }
109 return buffer;
110 }
111
112
oskar_dir_home(void)113 char* oskar_dir_home(void)
114 {
115 char *tmp = 0, *home_dir = 0;
116 #ifndef OSKAR_OS_WIN
117 char *buffer = 0;
118 struct passwd pwd, *result = 0;
119 tmp = getenv("HOME");
120 if (!tmp)
121 {
122 long int buf_len = sysconf(_SC_GETPW_R_SIZE_MAX);
123 if (buf_len == -1) buf_len = 16384;
124 buffer = (char*) malloc((size_t) buf_len);
125 if (!buffer) return 0;
126 getpwuid_r(geteuid(), &pwd, buffer, (size_t) buf_len, &result);
127 if (result != NULL) tmp = pwd.pw_dir;
128 }
129 if (tmp)
130 {
131 home_dir = (char*) calloc(1 + strlen(tmp), 1);
132 if (home_dir) strcpy(home_dir, tmp);
133 }
134 free(buffer);
135 #else
136 tmp = getenv("USERPROFILE");
137 if (tmp)
138 {
139 home_dir = (char*) calloc(1 + strlen(tmp), 1);
140 if (home_dir) strcpy(home_dir, tmp);
141 }
142 #endif
143 return home_dir;
144 }
145
146
name_cmp(const void * a,const void * b)147 static int name_cmp(const void* a, const void* b)
148 {
149 return strcmp(*(char* const*)a, *(char* const*)b);
150 }
151
152
153 /* From http://c-faq.com/lib/regex.html */
match(const char * pattern,const char * str)154 static int match(const char *pattern, const char *str)
155 {
156 switch (*pattern)
157 {
158 case '\0': return !*str;
159 case '*': return match(pattern+1, str) || (*str && match(pattern, str+1));
160 case '?': return *str && match(pattern+1, str+1);
161 default: return *pattern == *str && match(pattern+1, str+1);
162 }
163 }
164
165
item(const char * dir_path,const char * name,const char * wildcard,int match_files,int match_dirs,int * i,int num_items,char ** items)166 static void item(const char* dir_path, const char* name, const char* wildcard,
167 int match_files, int match_dirs, int* i, int num_items, char** items)
168 {
169 if (!strcmp(name, ".") || !strcmp(name, "..")) return;
170 if (wildcard && !match(wildcard, name)) return;
171 if (match_files ^ match_dirs)
172 {
173 int rejected = 0;
174 char* item_path = 0;
175 item_path = oskar_dir_get_path(dir_path, name);
176 const int is_dir = oskar_dir_exists(item_path);
177 if ((is_dir && !match_dirs) || (!is_dir && match_dirs)) rejected = 1;
178 free(item_path);
179 if (rejected) return;
180 }
181 if (items && *i < num_items)
182 {
183 items[*i] = (char*) realloc(items[*i], 1 + strlen(name));
184 if (items[*i]) strcpy(items[*i], name);
185 }
186 ++(*i);
187 }
188
189
get_items(const char * dir_path,const char * wildcard,int match_files,int match_dirs,int num_items,char ** items)190 static int get_items(const char* dir_path, const char* wildcard,
191 int match_files, int match_dirs, int num_items, char** items)
192 {
193 int i = 0;
194 #ifndef OSKAR_OS_WIN
195 DIR *d = 0;
196 struct dirent *t = 0;
197 if (!(d = opendir(dir_path))) return 0;
198 while ((t = readdir(d)) != 0)
199 {
200 item(dir_path, t->d_name, wildcard, match_files, match_dirs,
201 &i, num_items, items);
202 }
203 (void) closedir(d);
204 #else
205 WIN32_FIND_DATA f;
206 HANDLE h = 0;
207 char* buffer = 0;
208 buffer = (char*) malloc(3 + strlen(dir_path));
209 if (!buffer) return 0;
210 (void) sprintf(buffer, "%s\\*", dir_path);
211 if ((h = FindFirstFile(buffer, &f)) == INVALID_HANDLE_VALUE)
212 {
213 free(buffer);
214 return 0;
215 }
216 do
217 {
218 item(dir_path, f.cFileName, wildcard, match_files, match_dirs,
219 &i, num_items, items);
220 }
221 while (FindNextFile(h, &f));
222 FindClose(h);
223 free(buffer);
224 #endif
225 return i;
226 }
227
228
oskar_dir_items(const char * dir_path,const char * wildcard,int match_files,int match_dirs,int * num_items,char *** items)229 void oskar_dir_items(const char* dir_path, const char* wildcard,
230 int match_files, int match_dirs, int* num_items, char*** items)
231 {
232 int i = 0, old_num_items = *num_items;
233
234 /* Count the number of items. */
235 *num_items = get_items(dir_path, wildcard, match_files, match_dirs, 0, 0);
236
237 /* Get the sorted list of names if required. */
238 if (items)
239 {
240 for (i = *num_items; i < old_num_items; ++i) free((*items)[i]);
241 *items = (char**) realloc(*items, *num_items * sizeof(char**));
242 if (!*items) return;
243 for (i = old_num_items; i < *num_items; ++i) (*items)[i] = 0;
244 (void) get_items(dir_path, wildcard, match_files, match_dirs,
245 *num_items, *items);
246 qsort(*items, *num_items, sizeof(char*), name_cmp);
247 }
248 }
249
250
oskar_dir_leafname(const char * path)251 const char* oskar_dir_leafname(const char* path)
252 {
253 const char* leafname = strrchr(path, (int)oskar_dir_separator());
254 return !leafname ? path : leafname + 1;
255 }
256
257
oskar_dir_mkdir(const char * dir_path)258 int oskar_dir_mkdir(const char* dir_path)
259 {
260 #ifndef OSKAR_OS_WIN
261 struct stat s;
262 if (stat(dir_path, &s) != 0)
263 {
264 /* Item does not exist. Try to create the directory. */
265 if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) return 0;
266 }
267 else if (!S_ISDIR(s.st_mode))
268 {
269 return 0;
270 }
271 #else
272 int attrib;
273 if ((attrib = GetFileAttributes(dir_path)) == INVALID_FILE_ATTRIBUTES)
274 {
275 /* Item does not exist. Try to create the directory. */
276 if (!CreateDirectory(dir_path, 0) &&
277 GetLastError() != ERROR_ALREADY_EXISTS)
278 {
279 return 0;
280 }
281 }
282 else if (!(attrib & FILE_ATTRIBUTE_DIRECTORY))
283 {
284 return 0;
285 }
286 #endif
287 return 1;
288 }
289
290
oskar_dir_mkpath(const char * dir_path)291 int oskar_dir_mkpath(const char* dir_path)
292 {
293 char *start = 0, *sep = 0, *dir_path_p = 0;
294 int error = 0;
295
296 /* Copy the input path string so it can be modified. */
297 const size_t path_len = 1 + strlen(dir_path);
298 dir_path_p = (char*) malloc(path_len);
299 if (!dir_path_p) return 0;
300 memcpy(dir_path_p, dir_path, path_len);
301
302 /* Loop over directories in path to ensure they all exist. */
303 start = dir_path_p;
304 while (!error && (sep = strchr(start, oskar_dir_separator())))
305 {
306 if (sep != start && *(sep - 1) != ':')
307 {
308 *sep = '\0'; /* Temporarily truncate to ensure this dir exists. */
309 error = !oskar_dir_mkdir(dir_path_p);
310 *sep = oskar_dir_separator(); /* Restore full path. */
311 }
312 start = sep + 1;
313 }
314 free(dir_path_p);
315 return !error ? oskar_dir_mkdir(dir_path) : 0;
316 }
317
318
oskar_dir_remove(const char * dir_path)319 int oskar_dir_remove(const char* dir_path)
320 {
321 int error = 0, i = 0, num_items = 0;
322 char **items = 0, *path = 0;
323 if (!oskar_dir_exists(dir_path) ||
324 !strcmp(dir_path, ".") ||
325 !strcmp(dir_path, "./") ||
326 !strcmp(dir_path, ".\\"))
327 {
328 return 0;
329 }
330
331 /* Get names of all items in the directory. */
332 oskar_dir_items(dir_path, NULL, 1, 1, &num_items, &items);
333 for (i = 0; i < num_items; ++i)
334 {
335 /* Get full path of the item. */
336 path = oskar_dir_get_path(dir_path, items[i]);
337
338 /* Remove files and directories recursively. */
339 if (!oskar_dir_exists(path))
340 {
341 error = remove(path);
342 }
343 else
344 {
345 error = !oskar_dir_remove(path);
346 }
347 free(path);
348 if (error) break;
349 }
350 for (i = 0; i < num_items; ++i) free(items[i]);
351 free(items);
352 if (error) return 0;
353
354 /* Remove the empty directory. */
355 #ifdef OSKAR_OS_WIN
356 return RemoveDirectory(dir_path);
357 #else
358 return remove(dir_path) ? 0 : 1;
359 #endif
360 }
361
362
oskar_dir_separator(void)363 char oskar_dir_separator(void)
364 {
365 #ifdef OSKAR_OS_WIN
366 return '\\';
367 #else
368 return '/';
369 #endif
370 }
371
372
373 #ifdef __cplusplus
374 }
375 #endif
376