LMMS
Loading...
Searching...
No Matches
filesystem.c
Go to the documentation of this file.
1/*
2 Copyright 2007-2021 David Robillard <d@drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*/
16
17#define _POSIX_C_SOURCE 200809L /* for fileno */
18#define _BSD_SOURCE 1 /* for realpath, symlink */
19#define _DEFAULT_SOURCE 1 /* for realpath, symlink */
20
21#ifdef __APPLE__
22# define _DARWIN_C_SOURCE 1 /* for flock */
23#endif
24
25#include "filesystem.h"
26#include "lilv_config.h"
27#include "lilv_internal.h"
28
29#ifdef _WIN32
30# include <direct.h>
31# include <io.h>
32# include <windows.h>
33# define F_OK 0
34# define mkdir(path, flags) _mkdir(path)
35# define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
36#else
37# include <dirent.h>
38# include <unistd.h>
39#endif
40
41#if USE_FLOCK && USE_FILENO
42# include <sys/file.h>
43#endif
44
45#include <sys/stat.h>
46
47#include <errno.h>
48#include <stdbool.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52
53#ifndef PAGE_SIZE
54# define PAGE_SIZE 4096
55#endif
56
57static bool
58lilv_is_dir_sep(const char c)
59{
60 return c == '/' || c == LILV_DIR_SEP[0];
61}
62
63#ifdef _WIN32
64static inline bool
65is_windows_path(const char* path)
66{
67 return (isalpha(path[0]) && (path[1] == ':' || path[1] == '|') &&
68 (path[2] == '/' || path[2] == '\\'));
69}
70#endif
71
72char*
74{
75#ifdef _WIN32
76 DWORD len = GetTempPath(0, NULL);
77 char* buf = (char*)calloc(len, 1);
78 if (GetTempPath(len, buf) == 0) {
79 free(buf);
80 return NULL;
81 }
82
83 return buf;
84#else
85 const char* const tmpdir = getenv("TMPDIR");
86
87 return tmpdir ? lilv_strdup(tmpdir) : lilv_strdup("/tmp");
88#endif
89}
90
91bool
92lilv_path_is_absolute(const char* path)
93{
94 if (lilv_is_dir_sep(path[0])) {
95 return true;
96 }
97
98#ifdef _WIN32
99 if (is_windows_path(path)) {
100 return true;
101 }
102#endif
103
104 return false;
105}
106
107bool
108lilv_path_is_child(const char* path, const char* dir)
109{
110 if (path && dir) {
111 const size_t path_len = strlen(path);
112 const size_t dir_len = strlen(dir);
113 return dir && path_len >= dir_len && !strncmp(path, dir, dir_len);
114 }
115 return false;
116}
117
118char*
120{
121 return getcwd(NULL, 0);
122}
123
124char*
125lilv_path_absolute(const char* path)
126{
127 if (lilv_path_is_absolute(path)) {
128 return lilv_strdup(path);
129 }
130
131 char* cwd = getcwd(NULL, 0);
132 char* abs_path = lilv_path_join(cwd, path);
133 free(cwd);
134 return abs_path;
135}
136
137char*
138lilv_path_absolute_child(const char* path, const char* parent)
139{
140 if (lilv_path_is_absolute(path)) {
141 return lilv_strdup(path);
142 }
143
144 return lilv_path_join(parent, path);
145}
146
147char*
148lilv_path_relative_to(const char* path, const char* base)
149{
150 const size_t path_len = strlen(path);
151 const size_t base_len = strlen(base);
152 const size_t min_len = (path_len < base_len) ? path_len : base_len;
153
154 // Find the last separator common to both paths
155 size_t last_shared_sep = 0;
156 for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) {
157 if (lilv_is_dir_sep(path[i])) {
158 last_shared_sep = i;
159 }
160 }
161
162 if (last_shared_sep == 0) {
163 // No common components, return path
164 return lilv_strdup(path);
165 }
166
167 // Find the number of up references ("..") required
168 size_t up = 0;
169 for (size_t i = last_shared_sep + 1; i < base_len; ++i) {
170 if (lilv_is_dir_sep(base[i])) {
171 ++up;
172 }
173 }
174
175#ifdef _WIN32
176 const bool use_slash = strchr(path, '/');
177#else
178 static const bool use_slash = true;
179#endif
180
181 // Write up references
182 const size_t suffix_len = path_len - last_shared_sep;
183 char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1);
184 for (size_t i = 0; i < up; ++i) {
185 if (use_slash) {
186 memcpy(rel + (i * 3), "../", 3);
187 } else {
188 memcpy(rel + (i * 3), "..\\", 3);
189 }
190 }
191
192 // Write suffix
193 memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len);
194 return rel;
195}
196
197char*
198lilv_path_parent(const char* path)
199{
200 const char* s = path + strlen(path) - 1; // Last character
201
202 // Last non-slash
203 for (; s > path && lilv_is_dir_sep(*s); --s) {
204 }
205
206 // Last internal slash
207 for (; s > path && !lilv_is_dir_sep(*s); --s) {
208 }
209
210 // Skip duplicates
211 for (; s > path && lilv_is_dir_sep(*s); --s) {
212 }
213
214 if (s == path) { // Hit beginning
215 return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup(".");
216 }
217
218 // Pointing to the last character of the result (inclusive)
219 char* dirname = (char*)malloc(s - path + 2);
220 memcpy(dirname, path, s - path + 1);
221 dirname[s - path + 1] = '\0';
222 return dirname;
223}
224
225char*
226lilv_path_filename(const char* path)
227{
228 const size_t path_len = strlen(path);
229 size_t last_sep = path_len;
230 for (size_t i = 0; i < path_len; ++i) {
231 if (lilv_is_dir_sep(path[i])) {
232 last_sep = i;
233 }
234 }
235
236 if (last_sep >= path_len) {
237 return lilv_strdup(path);
238 }
239
240 const size_t ret_len = path_len - last_sep;
241 char* const ret = (char*)calloc(ret_len + 1, 1);
242
243 strncpy(ret, path + last_sep + 1, ret_len);
244 return ret;
245}
246
247char*
248lilv_path_join(const char* a, const char* b)
249{
250 if (!a) {
251 return (b && b[0]) ? lilv_strdup(b) : NULL;
252 }
253
254 const size_t a_len = strlen(a);
255 const size_t b_len = b ? strlen(b) : 0;
256 const bool a_end_is_sep = a_len > 0 && lilv_is_dir_sep(a[a_len - 1]);
257 const size_t pre_len = a_len - (a_end_is_sep ? 1 : 0);
258 char* path = (char*)calloc(1, a_len + b_len + 2);
259 memcpy(path, a, pre_len);
260
261#ifdef _WIN32
262 // Use forward slash if it seems that the input paths do
263 const bool a_has_slash = strchr(a, '/');
264 const bool b_has_slash = b && strchr(b, '/');
265 if (a_has_slash || b_has_slash) {
266 path[pre_len] = '/';
267 } else {
268 path[pre_len] = '\\';
269 }
270#else
271 path[pre_len] = '/';
272#endif
273
274 if (b) {
275 memcpy(path + pre_len + 1,
276 b + (lilv_is_dir_sep(b[0]) ? 1 : 0),
277 lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len);
278 }
279 return path;
280}
281
282char*
283lilv_path_canonical(const char* path)
284{
285 if (!path) {
286 return NULL;
287 }
288
289#if defined(_WIN32)
290 char* out = (char*)malloc(MAX_PATH);
291 GetFullPathName(path, MAX_PATH, out, NULL);
292 return out;
293#else
294 char* real_path = realpath(path, NULL);
295 return real_path ? real_path : lilv_strdup(path);
296#endif
297}
298
299bool
300lilv_path_exists(const char* path)
301{
302#if USE_LSTAT
303 struct stat st;
304 return !lstat(path, &st);
305#else
306 return !access(path, F_OK);
307#endif
308}
309
310bool
311lilv_is_directory(const char* path)
312{
313 struct stat st;
314 return !stat(path, &st) && S_ISDIR(st.st_mode);
315}
316
317int
318lilv_copy_file(const char* src, const char* dst)
319{
320 FILE* in = fopen(src, "r");
321 if (!in) {
322 return errno;
323 }
324
325 FILE* out = fopen(dst, "w");
326 if (!out) {
327 fclose(in);
328 return errno;
329 }
330
331 char* page = (char*)malloc(PAGE_SIZE);
332 size_t n_read = 0;
333 int st = 0;
334 while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) {
335 if (fwrite(page, 1, n_read, out) != n_read) {
336 st = errno;
337 break;
338 }
339 }
340
341 if (!st && fflush(out)) {
342 st = errno;
343 }
344
345 if (!st && (ferror(in) || ferror(out))) {
346 st = EBADF;
347 }
348
349 free(page);
350 fclose(in);
351 fclose(out);
352
353 return st;
354}
355
356int
357lilv_symlink(const char* oldpath, const char* newpath)
358{
359 int ret = 0;
360 if (strcmp(oldpath, newpath)) {
361#ifdef _WIN32
362 ret = !CreateHardLink(newpath, oldpath, 0);
363#else
364 char* target = lilv_path_relative_to(oldpath, newpath);
365
366 ret = symlink(target, newpath);
367
368 free(target);
369#endif
370 }
371 return ret;
372}
373
374int
375lilv_flock(FILE* file, bool lock, bool block)
376{
377#ifdef _WIN32
378 HANDLE handle = (HANDLE)_get_osfhandle(fileno(file));
379 OVERLAPPED overlapped = {0};
380
381 if (lock) {
382 const DWORD flags =
383 (LOCKFILE_EXCLUSIVE_LOCK | (block ? 0 : LOCKFILE_FAIL_IMMEDIATELY));
384
385 return !LockFileEx(handle, flags, 0, UINT32_MAX, UINT32_MAX, &overlapped);
386 } else {
387 return !UnlockFileEx(handle, 0, UINT32_MAX, UINT32_MAX, &overlapped);
388 }
389#elif USE_FLOCK && USE_FILENO
390 return flock(fileno(file),
391 (lock ? LOCK_EX : LOCK_UN) | (block ? 0 : LOCK_NB));
392#else
393 return 0;
394#endif
395}
396
397void
398lilv_dir_for_each(const char* path,
399 void* data,
400 void (*f)(const char* path, const char* name, void* data))
401{
402#ifdef _WIN32
403 char* pat = lilv_path_join(path, "*");
404 WIN32_FIND_DATA fd;
405 HANDLE fh = FindFirstFile(pat, &fd);
406 if (fh != INVALID_HANDLE_VALUE) {
407 do {
408 if (strcmp(fd.cFileName, ".") && strcmp(fd.cFileName, "..")) {
409 f(path, fd.cFileName, data);
410 }
411 } while (FindNextFile(fh, &fd));
412 }
413 FindClose(fh);
414 free(pat);
415#else
416 DIR* dir = opendir(path);
417 if (dir) {
418 for (struct dirent* entry = NULL; (entry = readdir(dir));) {
419 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
420 f(path, entry->d_name, data);
421 }
422 }
423 closedir(dir);
424 }
425#endif
426}
427
428char*
430{
431#ifdef _WIN32
432 static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
433 static const int n_chars = sizeof(chars) - 1;
434
435 const size_t pattern_len = strlen(pattern);
436 if (pattern_len < 7 || strcmp(pattern + pattern_len - 6, "XXXXXX")) {
437 errno = EINVAL;
438 return NULL;
439 }
440
441 char* const path_pattern = lilv_path_join(parent, pattern);
442 const size_t path_pattern_len = strlen(path_pattern);
443 char* const suffix = path_pattern + path_pattern_len - 6;
444
445 for (unsigned attempt = 0; attempt < 128; ++attempt) {
446 for (unsigned i = 0; i < 6; ++i) {
447 suffix[i] = chars[rand() % n_chars];
448 }
449
450 if (!mkdir(path_pattern, 0700)) {
451 return path_pattern;
452 }
453 }
454
455 return NULL;
456#else
457 char* const path_pattern = lilv_path_join(parent, pattern);
458
459 return mkdtemp(path_pattern); // NOLINT (not a leak)
460#endif
461}
462
463char*
465{
466 char* const tmpdir = lilv_temp_directory_path();
468
469 free(tmpdir);
470
471 return result;
472}
473
474int
475lilv_create_directories(const char* dir_path)
476{
477 char* path = lilv_strdup(dir_path);
478 const size_t path_len = strlen(path);
479 size_t i = 1;
480
481#ifdef _WIN32
482 if (is_windows_path(dir_path)) {
483 i = 3;
484 }
485#endif
486
487 for (; i <= path_len; ++i) {
488 const char c = path[i];
489 if (c == LILV_DIR_SEP[0] || c == '/' || c == '\0') {
490 path[i] = '\0';
491 if (mkdir(path, 0755) && (errno != EEXIST || !lilv_is_directory(path))) {
492 free(path);
493 return errno;
494 }
495 path[i] = c;
496 }
497 }
498
499 free(path);
500 return 0;
501}
502
503static off_t
504lilv_file_size(const char* path)
505{
506 struct stat buf;
507 if (stat(path, &buf)) {
508 return 0;
509 }
510 return buf.st_size;
511}
512
513int
514lilv_remove(const char* path)
515{
516#ifdef _WIN32
517 if (lilv_is_directory(path)) {
518 return !RemoveDirectory(path);
519 }
520#endif
521
522 return remove(path);
523}
524
525bool
526lilv_file_equals(const char* a_path, const char* b_path)
527{
528 if (!strcmp(a_path, b_path)) {
529 return true; // Paths match
530 }
531
532 bool match = false;
533 FILE* a_file = NULL;
534 FILE* b_file = NULL;
535 char* const a_real = lilv_path_canonical(a_path);
536 char* const b_real = lilv_path_canonical(b_path);
537 if (!strcmp(a_real, b_real)) {
538 match = true; // Real paths match
539 } else if (lilv_file_size(a_path) != lilv_file_size(b_path)) {
540 match = false; // Sizes differ
541 } else if (!(a_file = fopen(a_real, "rb")) ||
542 !(b_file = fopen(b_real, "rb"))) {
543 match = false; // Missing file matches nothing
544 } else {
545 // TODO: Improve performance by reading chunks
546 match = true;
547 while (!feof(a_file) && !feof(b_file)) {
548 if (fgetc(a_file) != fgetc(b_file)) {
549 match = false;
550 break;
551 }
552 }
553 }
554
555 if (a_file) {
556 fclose(a_file);
557 }
558 if (b_file) {
559 fclose(b_file);
560 }
561 free(a_real);
562 free(b_real);
563 return match;
564}
#define NULL
Definition CarlaBridgeFormat.cpp:30
uint8_t a
Definition Spc_Cpu.h:141
register unsigned i
Definition inflate.c:1575
unsigned s
Definition inflate.c:1555
unsigned f
Definition inflate.c:1572
char * lilv_path_canonical(const char *path)
Definition filesystem.c:283
char * lilv_path_current(void)
Return the current working directory.
Definition filesystem.c:119
static bool lilv_is_dir_sep(const char c)
Definition filesystem.c:58
char * lilv_create_temporary_directory(const char *pattern)
Definition filesystem.c:464
char * lilv_path_absolute_child(const char *path, const char *parent)
Definition filesystem.c:138
char * lilv_path_parent(const char *path)
Definition filesystem.c:198
char * lilv_temp_directory_path(void)
Return the path to a directory suitable for making temporary files.
Definition filesystem.c:73
bool lilv_path_is_absolute(const char *path)
Return true iff path is an absolute path.
Definition filesystem.c:92
int lilv_create_directories(const char *dir_path)
Definition filesystem.c:475
char * lilv_path_relative_to(const char *path, const char *base)
Definition filesystem.c:148
int lilv_flock(FILE *file, bool lock, bool block)
Definition filesystem.c:375
bool lilv_is_directory(const char *path)
Return true iff path points to an existing directory.
Definition filesystem.c:311
#define PAGE_SIZE
Definition filesystem.c:54
char * lilv_path_absolute(const char *path)
Definition filesystem.c:125
void lilv_dir_for_each(const char *path, void *data, void(*f)(const char *path, const char *name, void *data))
Definition filesystem.c:398
int lilv_copy_file(const char *src, const char *dst)
Definition filesystem.c:318
int lilv_remove(const char *path)
Remove the file or empty directory at path.
Definition filesystem.c:514
static off_t lilv_file_size(const char *path)
Definition filesystem.c:504
bool lilv_path_is_child(const char *path, const char *dir)
Return true iff path is a child of dir.
Definition filesystem.c:108
char * lilv_path_join(const char *a, const char *b)
Join path a and path b with a single directory separator between them.
Definition filesystem.c:248
char * lilv_create_temporary_directory_in(const char *pattern, const char *parent)
Definition filesystem.c:429
bool lilv_file_equals(const char *a_path, const char *b_path)
Return true iff the given paths point to files with identical contents.
Definition filesystem.c:526
bool lilv_path_exists(const char *path)
Return true iff path points to an existing file system entry.
Definition filesystem.c:300
int lilv_symlink(const char *oldpath, const char *newpath)
Definition filesystem.c:357
char * lilv_path_filename(const char *path)
Definition filesystem.c:226
static const char * name
Definition pugl.h:1582
static uintptr_t parent
Definition pugl.h:1644
JSAMPIMAGE data
Definition jpeglib.h:945
#define is_windows_path
char * lilv_strdup(const char *str)
Definition util.c:78
#define LILV_DIR_SEP
Definition juce_lv2_config.h:54
static SerdStatus page(SerdReader *reader)
Definition reader.c:112
float in
Definition lilv_test.c:1460
float out
Definition lilv_test.c:1461
#define MAX_PATH
unsigned int DWORD
Definition swell-types.h:164
void * HANDLE
Definition swell-types.h:212
void GetTempPath(int bufsz, char *buf)
Definition swell.cpp:1063
return c
Definition crypt.c:175
memcpy(hh, h, RAND_HEAD_LEN)
b
Definition crypt.c:628
int * pattern
Definition match.c:126
int match(string, pattern, ignore_case __WDL) ZCONST char *string
int result
Definition process.c:1455
#define dirent
Definition unix.c:70
closedir((DIR *) G.wild_dir)
char * getenv()
#define S_ISDIR(m)
Definition unzpriv.h:1322
char * malloc()
struct zdirent * file
Definition win32.c:1500