1 /*
2 Copyright 2007-2016 David Robillard <http://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 <ctype.h>
26 #include <errno.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stddef.h>
32
33 #ifdef _WIN32
34 #ifndef _WIN32_WINNT
35 # define _WIN32_WINNT 0x0600 /* for CreateSymbolicLink */
36 #endif
37 # include <windows.h>
38 # include <direct.h>
39 # include <io.h>
40 # define F_OK 0
41 # define mkdir(path, flags) _mkdir(path)
42 /** Implement 'CreateSymbolicLink()' for MSVC 8 or earlier */
43 BOOLEAN WINAPI
CreateSymbolicLink(LPCTSTR linkpath,LPCTSTR targetpath,DWORD flags)44 CreateSymbolicLink(LPCTSTR linkpath, LPCTSTR targetpath, DWORD flags)
45 {
46 typedef BOOLEAN (WINAPI* PFUNC)(LPCTSTR, LPCTSTR, DWORD);
47
48 PFUNC pfn = (PFUNC)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
49 "CreateSymbolicLinkA");
50 return pfn ? pfn(linkpath, targetpath, flags) : 0;
51 }
52 #else
53 # include <dirent.h>
54 # include <limits.h>
55 # include <unistd.h>
56 #endif
57
58 #include <sys/stat.h>
59 #include <sys/types.h>
60
61 #include "lilv_internal.h"
62
63 #if defined(HAVE_FLOCK) && defined(HAVE_FILENO)
64 # include <sys/file.h>
65 #endif
66
67 #ifndef PAGE_SIZE
68 # define PAGE_SIZE 4096
69 #endif
70
71 void
lilv_free(void * ptr)72 lilv_free(void* ptr)
73 {
74 free(ptr);
75 }
76
77 char*
lilv_strjoin(const char * first,...)78 lilv_strjoin(const char* first, ...)
79 {
80 size_t len = strlen(first);
81 char* result = (char*)malloc(len + 1);
82
83 memcpy(result, first, len);
84
85 va_list args;
86 va_start(args, first);
87 while (1) {
88 const char* const s = va_arg(args, const char *);
89 if (s == NULL)
90 break;
91
92 const size_t this_len = strlen(s);
93 char* new_result = (char*)realloc(result, len + this_len + 1);
94 if (!new_result) {
95 free(result);
96 return NULL;
97 }
98
99 result = new_result;
100 memcpy(result + len, s, this_len);
101 len += this_len;
102 }
103 va_end(args);
104
105 result[len] = '\0';
106
107 return result;
108 }
109
110 char*
lilv_strdup(const char * str)111 lilv_strdup(const char* str)
112 {
113 if (!str) {
114 return NULL;
115 }
116
117 const size_t len = strlen(str);
118 char* copy = (char*)malloc(len + 1);
119 memcpy(copy, str, len + 1);
120 return copy;
121 }
122
123 const char*
lilv_uri_to_path(const char * uri)124 lilv_uri_to_path(const char* uri)
125 {
126 return (const char*)serd_uri_to_path((const uint8_t*)uri);
127 }
128
129 char*
lilv_file_uri_parse(const char * uri,char ** hostname)130 lilv_file_uri_parse(const char* uri, char** hostname)
131 {
132 return (char*)serd_file_uri_parse((const uint8_t*)uri, (uint8_t**)hostname);
133 }
134
135 /** Return the current LANG converted to Turtle (i.e. RFC3066) style.
136 * For example, if LANG is set to "en_CA.utf-8", this returns "en-ca".
137 */
138 char*
lilv_get_lang(void)139 lilv_get_lang(void)
140 {
141 const char* const env_lang = getenv("LANG");
142 if (!env_lang || !strcmp(env_lang, "")
143 || !strcmp(env_lang, "C") || !strcmp(env_lang, "POSIX")) {
144 return NULL;
145 }
146
147 const size_t env_lang_len = strlen(env_lang);
148 char* const lang = (char*)malloc(env_lang_len + 1);
149 for (size_t i = 0; i < env_lang_len + 1; ++i) {
150 if (env_lang[i] == '_') {
151 lang[i] = '-'; // Convert _ to -
152 } else if (env_lang[i] >= 'A' && env_lang[i] <= 'Z') {
153 lang[i] = env_lang[i] + ('a' - 'A'); // Convert to lowercase
154 } else if (env_lang[i] >= 'a' && env_lang[i] <= 'z') {
155 lang[i] = env_lang[i]; // Lowercase letter, copy verbatim
156 } else if (env_lang[i] >= '0' && env_lang[i] <= '9') {
157 lang[i] = env_lang[i]; // Digit, copy verbatim
158 } else if (env_lang[i] == '\0' || env_lang[i] == '.') {
159 // End, or start of suffix (e.g. en_CA.utf-8), finished
160 lang[i] = '\0';
161 break;
162 } else {
163 LILV_ERRORF("Illegal LANG `%s' ignored\n", env_lang);
164 free(lang);
165 return NULL;
166 }
167 }
168
169 return lang;
170 }
171
172 /** Append suffix to dst, update dst_len, and return the realloc'd result. */
173 static char*
strappend(char * dst,size_t * dst_len,const char * suffix,size_t suffix_len)174 strappend(char* dst, size_t* dst_len, const char* suffix, size_t suffix_len)
175 {
176 dst = (char*)realloc(dst, *dst_len + suffix_len + 1);
177 memcpy(dst + *dst_len, suffix, suffix_len);
178 dst[(*dst_len += suffix_len)] = '\0';
179 return dst;
180 }
181
182 /** Append the value of the environment variable var to dst. */
183 static char*
append_var(char * dst,size_t * dst_len,const char * var)184 append_var(char* dst, size_t* dst_len, const char* var)
185 {
186 // Get value from environment
187 const char* val = getenv(var);
188 if (val) { // Value found, append it
189 return strappend(dst, dst_len, val, strlen(val));
190 } else { // No value found, append variable reference as-is
191 return strappend(strappend(dst, dst_len, "$", 1),
192 dst_len, var, strlen(var));
193 }
194 }
195
196 /** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in `path`. */
197 char*
lilv_expand(const char * path)198 lilv_expand(const char* path)
199 {
200 #ifdef _WIN32
201 char* out = (char*)malloc(MAX_PATH);
202 ExpandEnvironmentStrings(path, out, MAX_PATH);
203 #else
204 char* out = NULL;
205 size_t len = 0;
206
207 const char* start = path; // Start of current chunk to copy
208 for (const char* s = path; *s;) {
209 if (*s == '$') {
210 // Hit $ (variable reference, e.g. $VAR_NAME)
211 for (const char* t = s + 1; ; ++t) {
212 if (!*t || (!isupper(*t) && !isdigit(*t) && *t != '_')) {
213 // Append preceding chunk
214 out = strappend(out, &len, start, s - start);
215
216 // Append variable value (or $VAR_NAME if not found)
217 char* var = (char*)calloc(t - s, 1);
218 memcpy(var, s + 1, t - s - 1);
219 out = append_var(out, &len, var);
220 free(var);
221
222 // Continue after variable reference
223 start = s = t;
224 break;
225 }
226 }
227 } else if (*s == '~' && (*(s + 1) == '/' || !*(s + 1))) {
228 // Hit ~ before slash or end of string (home directory reference)
229 out = strappend(out, &len, start, s - start);
230 out = append_var(out, &len, "HOME");
231 start = ++s;
232 } else {
233 ++s;
234 }
235 }
236
237 if (*start) {
238 out = strappend(out, &len, start, strlen(start));
239 }
240 #endif
241
242 return out;
243 }
244
245 static bool
lilv_is_dir_sep(const char c)246 lilv_is_dir_sep(const char c)
247 {
248 return c == '/' || c == LILV_DIR_SEP[0];
249 }
250
251 char*
lilv_dirname(const char * path)252 lilv_dirname(const char* path)
253 {
254 const char* s = path + strlen(path) - 1; // Last character
255 for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash
256 for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash
257 for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates
258
259 if (s == path) { // Hit beginning
260 return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup(".");
261 } else { // Pointing to the last character of the result (inclusive)
262 char* dirname = (char*)malloc(s - path + 2);
263 memcpy(dirname, path, s - path + 1);
264 dirname[s - path + 1] = '\0';
265 return dirname;
266 }
267 }
268
269 bool
lilv_path_exists(const char * path,void * ignored)270 lilv_path_exists(const char* path, void* ignored)
271 {
272 return !access(path, F_OK);
273 }
274
275 char*
lilv_find_free_path(const char * in_path,bool (* exists)(const char *,void *),void * user_data)276 lilv_find_free_path(const char* in_path,
277 bool (*exists)(const char*, void*), void* user_data)
278 {
279 const size_t in_path_len = strlen(in_path);
280 char* path = (char*)malloc(in_path_len + 7);
281 memcpy(path, in_path, in_path_len + 1);
282
283 for (int i = 2; i < 1000000; ++i) {
284 if (!exists(path, user_data)) {
285 return path;
286 }
287 snprintf(path, in_path_len + 7, "%s.%u", in_path, i);
288 }
289
290 return NULL;
291 }
292
293 int
lilv_copy_file(const char * src,const char * dst)294 lilv_copy_file(const char* src, const char* dst)
295 {
296 FILE* in = fopen(src, "r");
297 if (!in) {
298 return errno;
299 }
300
301 FILE* out = fopen(dst, "w");
302 if (!out) {
303 fclose(in);
304 return errno;
305 }
306
307 char* page = (char*)malloc(PAGE_SIZE);
308 size_t n_read = 0;
309 int st = 0;
310 while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) {
311 if (fwrite(page, 1, n_read, out) != n_read) {
312 st = errno;
313 break;
314 }
315 }
316
317 if (!st && (ferror(in) || ferror(out))) {
318 st = EBADF;
319 }
320
321 free(page);
322 fclose(in);
323 fclose(out);
324
325 return st;
326 }
327
328 bool
lilv_path_is_absolute(const char * path)329 lilv_path_is_absolute(const char* path)
330 {
331 if (lilv_is_dir_sep(path[0])) {
332 return true;
333 }
334
335 #ifdef _WIN32
336 if (isalpha(path[0]) && path[1] == ':' && lilv_is_dir_sep(path[2])) {
337 return true;
338 }
339 #endif
340
341 return false;
342 }
343
344 char*
lilv_path_absolute(const char * path)345 lilv_path_absolute(const char* path)
346 {
347 if (lilv_path_is_absolute(path)) {
348 return lilv_strdup(path);
349 } else {
350 char* cwd = getcwd(NULL, 0);
351 char* abs_path = lilv_path_join(cwd, path);
352 free(cwd);
353 return abs_path;
354 }
355 }
356
357 char*
lilv_path_join(const char * a,const char * b)358 lilv_path_join(const char* a, const char* b)
359 {
360 if (!a) {
361 return lilv_strdup(b);
362 }
363
364 const size_t a_len = strlen(a);
365 const size_t b_len = b ? strlen(b) : 0;
366 const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0);
367 char* path = (char*)calloc(1, a_len + b_len + 2);
368 memcpy(path, a, pre_len);
369 path[pre_len] = '/';
370 if (b) {
371 memcpy(path + pre_len + 1,
372 b + (lilv_is_dir_sep(b[0]) ? 1 : 0),
373 lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len);
374 }
375 return path;
376 }
377
378 typedef struct {
379 char* pattern;
380 time_t time;
381 char* latest;
382 } Latest;
383
384 static void
update_latest(const char * path,const char * name,void * data)385 update_latest(const char* path, const char* name, void* data)
386 {
387 Latest* latest = (Latest*)data;
388 char* entry_path = lilv_path_join(path, name);
389 unsigned num;
390 if (sscanf(entry_path, latest->pattern, &num) == 1) {
391 struct stat st;
392 if (!stat(entry_path, &st)) {
393 if (st.st_mtime >= latest->time) {
394 free(latest->latest);
395 latest->latest = entry_path;
396 }
397 } else {
398 LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
399 }
400 }
401 if (entry_path != latest->latest) {
402 free(entry_path);
403 }
404 }
405
406 /** Return the latest copy of the file at `path` that is newer. */
407 char*
lilv_get_latest_copy(const char * path,const char * copy_path)408 lilv_get_latest_copy(const char* path, const char* copy_path)
409 {
410 char* copy_dir = lilv_dirname(copy_path);
411 Latest latest = { lilv_strjoin(copy_path, ".%u", NULL), 0, NULL };
412
413 struct stat st;
414 if (!stat(path, &st)) {
415 latest.time = st.st_mtime;
416 } else {
417 LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
418 }
419
420 lilv_dir_for_each(copy_dir, &latest, update_latest);
421
422 free(latest.pattern);
423 free(copy_dir);
424 return latest.latest;
425 }
426
427 char*
lilv_realpath(const char * path)428 lilv_realpath(const char* path)
429 {
430 #if defined(_WIN32)
431 char* out = (char*)malloc(MAX_PATH);
432 GetFullPathName(path, MAX_PATH, out, NULL);
433 return out;
434 #elif _POSIX_VERSION >= 200809L
435 char* real_path = realpath(path, NULL);
436 return real_path ? real_path : lilv_strdup(path);
437 #else
438 // OSX <= 10.5, if anyone cares. I sure don't.
439 char* out = (char*)malloc(PATH_MAX);
440 char* real_path = realpath(path, out);
441 if (!real_path) {
442 free(out);
443 return lilv_strdup(path);
444 } else {
445 return real_path;
446 }
447 #endif
448 }
449
450 int
lilv_symlink(const char * oldpath,const char * newpath)451 lilv_symlink(const char* oldpath, const char* newpath)
452 {
453 int ret = 0;
454 if (strcmp(oldpath, newpath)) {
455 #ifdef _WIN32
456 ret = 1;
457 #else
458 ret = symlink(oldpath, newpath);
459 #endif
460 }
461 if (ret) {
462 LILV_ERRORF("Failed to link %s => %s (%s)\n",
463 newpath, oldpath, strerror(errno));
464 }
465 return ret;
466 }
467
468 char*
lilv_path_relative_to(const char * path,const char * base)469 lilv_path_relative_to(const char* path, const char* base)
470 {
471 const size_t path_len = strlen(path);
472 const size_t base_len = strlen(base);
473 const size_t min_len = (path_len < base_len) ? path_len : base_len;
474
475 // Find the last separator common to both paths
476 size_t last_shared_sep = 0;
477 for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) {
478 if (lilv_is_dir_sep(path[i])) {
479 last_shared_sep = i;
480 }
481 }
482
483 if (last_shared_sep == 0) {
484 // No common components, return path
485 return lilv_strdup(path);
486 }
487
488 // Find the number of up references ("..") required
489 size_t up = 0;
490 for (size_t i = last_shared_sep + 1; i < base_len; ++i) {
491 if (lilv_is_dir_sep(base[i])) {
492 ++up;
493 }
494 }
495
496 // Write up references
497 const size_t suffix_len = path_len - last_shared_sep;
498 char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1);
499 for (size_t i = 0; i < up; ++i) {
500 memcpy(rel + (i * 3), "../", 3);
501 }
502
503 // Write suffix
504 memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len);
505 return rel;
506 }
507
508 bool
lilv_path_is_child(const char * path,const char * dir)509 lilv_path_is_child(const char* path, const char* dir)
510 {
511 if (path && dir) {
512 const size_t path_len = strlen(path);
513 const size_t dir_len = strlen(dir);
514 return dir && path_len >= dir_len && !strncmp(path, dir, dir_len);
515 }
516 return false;
517 }
518
519 int
lilv_flock(FILE * file,bool lock)520 lilv_flock(FILE* file, bool lock)
521 {
522 #if defined(HAVE_FLOCK) && defined(HAVE_FILENO)
523 return flock(fileno(file), lock ? LOCK_EX : LOCK_UN);
524 #else
525 return 0;
526 #endif
527 }
528
529 void
lilv_dir_for_each(const char * path,void * data,void (* f)(const char * path,const char * name,void * data))530 lilv_dir_for_each(const char* path,
531 void* data,
532 void (*f)(const char* path, const char* name, void* data))
533 {
534 #ifdef _WIN32
535 char* pat = lilv_path_join(path, "*");
536 WIN32_FIND_DATA fd;
537 HANDLE fh = FindFirstFile(pat, &fd);
538 if (fh != INVALID_HANDLE_VALUE) {
539 do {
540 f(path, fd.cFileName, data);
541 } while (FindNextFile(fh, &fd));
542 }
543 free(pat);
544 #else
545 DIR* dir = opendir(path);
546 if (dir) {
547 long name_max = pathconf(path, _PC_NAME_MAX);
548 if (name_max == -1) {
549 name_max = 255; // Limit not defined, or error
550 }
551
552 const size_t len = offsetof(struct dirent, d_name) + name_max + 1;
553 struct dirent* entry = (struct dirent*)malloc(len);
554 struct dirent* result;
555 while (!readdir_r(dir, entry, &result) && result) {
556 f(path, entry->d_name, data);
557 }
558 free(entry);
559 closedir(dir);
560 }
561 #endif
562 }
563
564 int
lilv_mkdir_p(const char * dir_path)565 lilv_mkdir_p(const char* dir_path)
566 {
567 char* path = lilv_strdup(dir_path);
568 const size_t path_len = strlen(path);
569 for (size_t i = 1; i <= path_len; ++i) {
570 if (path[i] == LILV_DIR_SEP[0] || path[i] == '\0') {
571 path[i] = '\0';
572 if (mkdir(path, 0755) && errno != EEXIST) {
573 free(path);
574 return errno;
575 }
576 path[i] = LILV_DIR_SEP[0];
577 }
578 }
579
580 free(path);
581 return 0;
582 }
583
584 static off_t
lilv_file_size(const char * path)585 lilv_file_size(const char* path)
586 {
587 struct stat buf;
588 if (stat(path, &buf)) {
589 LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
590 return 0;
591 }
592 return buf.st_size;
593 }
594
595 bool
lilv_file_equals(const char * a_path,const char * b_path)596 lilv_file_equals(const char* a_path, const char* b_path)
597 {
598 if (!strcmp(a_path, b_path)) {
599 return true; // Paths match
600 }
601
602 bool match = false;
603 FILE* a_file = NULL;
604 FILE* b_file = NULL;
605 char* const a_real = lilv_realpath(a_path);
606 char* const b_real = lilv_realpath(b_path);
607 if (!strcmp(a_real, b_real)) {
608 match = true; // Real paths match
609 } else if (lilv_file_size(a_path) != lilv_file_size(b_path)) {
610 match = false; // Sizes differ
611 } else if (!(a_file = fopen(a_real, "rb")) ||
612 !(b_file = fopen(b_real, "rb"))) {
613 match = false; // Missing file matches nothing
614 } else {
615 // TODO: Improve performance by reading chunks
616 match = true;
617 while (!feof(a_file) && !feof(b_file)) {
618 if (fgetc(a_file) != fgetc(b_file)) {
619 match = false;
620 break;
621 }
622 }
623 }
624
625 if (a_file) {
626 fclose(a_file);
627 }
628 if (b_file) {
629 fclose(b_file);
630 }
631 free(a_real);
632 free(b_real);
633 return match;
634 }
635