Collection of tools useful for audio production
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

605 lines
14KB

  1. /*
  2. Copyright 2007-2011 David Robillard <http://drobilla.net>
  3. Permission to use, copy, modify, and/or distribute this software for any
  4. purpose with or without fee is hereby granted, provided that the above
  5. copyright notice and this permission notice appear in all copies.
  6. THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  7. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  8. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  9. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  10. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  11. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  12. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  13. */
  14. #define _POSIX_C_SOURCE 1 /* for fileno */
  15. #define _BSD_SOURCE 1 /* for realpath, symlink */
  16. #ifdef __APPLE__
  17. # define _DARWIN_C_SOURCE 1 /* for flock */
  18. #endif
  19. #include <assert.h>
  20. #include <ctype.h>
  21. #include <errno.h>
  22. #include <stdarg.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #ifdef _WIN32
  26. # include <windows.h>
  27. # include <direct.h>
  28. # include <io.h>
  29. # define F_OK 0
  30. # define mkdir(path, flags) _mkdir(path)
  31. #else
  32. # include <dirent.h>
  33. # include <unistd.h>
  34. #endif
  35. #include <sys/stat.h>
  36. #include <sys/types.h>
  37. #include "lilv_internal.h"
  38. #if defined(HAVE_FLOCK) && defined(HAVE_FILENO)
  39. # include <sys/file.h>
  40. #endif
  41. #ifndef PAGE_SIZE
  42. # define PAGE_SIZE 4096
  43. #endif
  44. char*
  45. lilv_strjoin(const char* first, ...)
  46. {
  47. size_t len = strlen(first);
  48. char* result = (char*)malloc(len + 1);
  49. memcpy(result, first, len);
  50. va_list args;
  51. va_start(args, first);
  52. while (1) {
  53. const char* const s = va_arg(args, const char *);
  54. if (s == NULL)
  55. break;
  56. const size_t this_len = strlen(s);
  57. if (!(result = (char*)realloc(result, len + this_len + 1))) {
  58. free(result);
  59. LILV_ERROR("realloc() failed\n");
  60. return NULL;
  61. }
  62. memcpy(result + len, s, this_len);
  63. len += this_len;
  64. }
  65. va_end(args);
  66. result[len] = '\0';
  67. return result;
  68. }
  69. char*
  70. lilv_strdup(const char* str)
  71. {
  72. if (!str) {
  73. return NULL;
  74. }
  75. const size_t len = strlen(str);
  76. char* dup = (char*)malloc(len + 1);
  77. memcpy(dup, str, len + 1);
  78. return dup;
  79. }
  80. const char*
  81. lilv_uri_to_path(const char* uri)
  82. {
  83. return (const char*)serd_uri_to_path((const uint8_t*)uri);
  84. }
  85. /** Return the current LANG converted to Turtle (i.e. RFC3066) style.
  86. * For example, if LANG is set to "en_CA.utf-8", this returns "en-ca".
  87. */
  88. char*
  89. lilv_get_lang(void)
  90. {
  91. const char* const env_lang = getenv("LANG");
  92. if (!env_lang || !strcmp(env_lang, "")
  93. || !strcmp(env_lang, "C") || !strcmp(env_lang, "POSIX")) {
  94. return NULL;
  95. }
  96. const size_t env_lang_len = strlen(env_lang);
  97. char* const lang = (char*)malloc(env_lang_len + 1);
  98. for (size_t i = 0; i < env_lang_len + 1; ++i) {
  99. if (env_lang[i] == '_') {
  100. lang[i] = '-'; // Convert _ to -
  101. } else if (env_lang[i] >= 'A' && env_lang[i] <= 'Z') {
  102. lang[i] = env_lang[i] + ('a' - 'A'); // Convert to lowercase
  103. } else if (env_lang[i] >= 'a' && env_lang[i] <= 'z') {
  104. lang[i] = env_lang[i]; // Lowercase letter, copy verbatim
  105. } else if (env_lang[i] >= '0' && env_lang[i] <= '9') {
  106. lang[i] = env_lang[i]; // Digit, copy verbatim
  107. } else if (env_lang[i] == '\0' || env_lang[i] == '.') {
  108. // End, or start of suffix (e.g. en_CA.utf-8), finished
  109. lang[i] = '\0';
  110. break;
  111. } else {
  112. LILV_ERRORF("Illegal LANG `%s' ignored\n", env_lang);
  113. free(lang);
  114. return NULL;
  115. }
  116. }
  117. return lang;
  118. }
  119. /** Append suffix to dst, update dst_len, and return the realloc'd result. */
  120. static char*
  121. strappend(char* dst, size_t* dst_len, const char* suffix, size_t suffix_len)
  122. {
  123. dst = (char*)realloc(dst, *dst_len + suffix_len + 1);
  124. memcpy(dst + *dst_len, suffix, suffix_len);
  125. dst[(*dst_len += suffix_len)] = '\0';
  126. return dst;
  127. }
  128. /** Append the value of the environment variable var to dst. */
  129. static char*
  130. append_var(char* dst, size_t* dst_len, const char* var)
  131. {
  132. // Get value from environment
  133. const char* val = getenv(var);
  134. if (val) { // Value found, append it
  135. return strappend(dst, dst_len, val, strlen(val));
  136. } else { // No value found, append variable reference as-is
  137. return strappend(strappend(dst, dst_len, "$", 1),
  138. dst_len, var, strlen(var));
  139. }
  140. }
  141. /** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in @a path. */
  142. char*
  143. lilv_expand(const char* path)
  144. {
  145. #ifdef _WIN32
  146. char* out = (char*)malloc(MAX_PATH);
  147. ExpandEnvironmentStrings(path, out, MAX_PATH);
  148. #else
  149. char* out = NULL;
  150. size_t len = 0;
  151. const char* start = path; // Start of current chunk to copy
  152. for (const char* s = path; *s;) {
  153. if (*s == '$') {
  154. // Hit $ (variable reference, e.g. $VAR_NAME)
  155. for (const char* t = s + 1; ; ++t) {
  156. if (!*t || (!isupper(*t) && !isdigit(*t) && *t != '_')) {
  157. // Append preceding chunk
  158. out = strappend(out, &len, start, s - start);
  159. // Append variable value (or $VAR_NAME if not found)
  160. char* var = (char*)calloc(t - s, 1);
  161. memcpy(var, s + 1, t - s - 1);
  162. out = append_var(out, &len, var);
  163. free(var);
  164. // Continue after variable reference
  165. start = s = t;
  166. break;
  167. }
  168. }
  169. } else if (*s == '~' && (*(s + 1) == '/' || !*(s + 1))) {
  170. // Hit ~ before slash or end of string (home directory reference)
  171. out = strappend(out, &len, start, s - start);
  172. out = append_var(out, &len, "HOME");
  173. start = ++s;
  174. } else {
  175. ++s;
  176. }
  177. }
  178. if (*start) {
  179. out = strappend(out, &len, start, strlen(start));
  180. }
  181. #endif
  182. return out;
  183. }
  184. static bool
  185. lilv_is_dir_sep(const char c)
  186. {
  187. return c == '/' || c == LILV_DIR_SEP[0];
  188. }
  189. char*
  190. lilv_dirname(const char* path)
  191. {
  192. const char* s = path + strlen(path) - 1; // Last character
  193. for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash
  194. for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash
  195. for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates
  196. if (s == path) { // Hit beginning
  197. return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup(".");
  198. } else { // Pointing to the last character of the result (inclusive)
  199. char* dirname = (char*)malloc(s - path + 2);
  200. memcpy(dirname, path, s - path + 1);
  201. dirname[s - path + 1] = '\0';
  202. return dirname;
  203. }
  204. }
  205. bool
  206. lilv_path_exists(const char* path, void* ignored)
  207. {
  208. return !access(path, F_OK);
  209. }
  210. char*
  211. lilv_find_free_path(const char* in_path,
  212. bool (*exists)(const char*, void*), void* user_data)
  213. {
  214. const size_t in_path_len = strlen(in_path);
  215. char* path = (char*)malloc(in_path_len + 7);
  216. memcpy(path, in_path, in_path_len + 1);
  217. for (int i = 2; i < 1000000; ++i) {
  218. if (!exists(path, user_data)) {
  219. return path;
  220. }
  221. snprintf(path, in_path_len + 7, "%s.%u", in_path, i);
  222. }
  223. return NULL;
  224. }
  225. int
  226. lilv_copy_file(const char* src, const char* dst)
  227. {
  228. FILE* in = fopen(src, "r");
  229. if (!in) {
  230. LILV_ERRORF("error opening %s (%s)\n", src, strerror(errno));
  231. return 1;
  232. }
  233. FILE* out = fopen(dst, "w");
  234. if (!out) {
  235. LILV_ERRORF("error opening %s (%s)\n", dst, strerror(errno));
  236. fclose(in);
  237. return 2;
  238. }
  239. char* page = (char*)malloc(PAGE_SIZE);
  240. size_t n_read = 0;
  241. while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) {
  242. if (fwrite(page, 1, n_read, out) != n_read) {
  243. LILV_ERRORF("write to %s failed (%s)\n", dst, strerror(errno));
  244. break;
  245. }
  246. }
  247. const int ret = ferror(in) || ferror(out);
  248. if (ferror(in)) {
  249. LILV_ERRORF("read from %s failed (%s)\n", src, strerror(errno));
  250. }
  251. free(page);
  252. fclose(in);
  253. fclose(out);
  254. return ret;
  255. }
  256. bool
  257. lilv_path_is_absolute(const char* path)
  258. {
  259. if (lilv_is_dir_sep(path[0])) {
  260. return true;
  261. }
  262. #ifdef _WIN32
  263. if (isalpha(path[0]) && path[1] == ':' && lilv_is_dir_sep(path[2])) {
  264. return true;
  265. }
  266. #endif
  267. return false;
  268. }
  269. char*
  270. lilv_path_absolute(const char* path)
  271. {
  272. if (lilv_path_is_absolute(path)) {
  273. return lilv_strdup(path);
  274. } else {
  275. char* cwd = getcwd(NULL, 0);
  276. char* abs_path = lilv_path_join(cwd, path);
  277. free(cwd);
  278. return abs_path;
  279. }
  280. }
  281. char*
  282. lilv_path_join(const char* a, const char* b)
  283. {
  284. if (!a) {
  285. return lilv_strdup(b);
  286. }
  287. const size_t a_len = strlen(a);
  288. const size_t b_len = b ? strlen(b) : 0;
  289. const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0);
  290. char* path = (char*)calloc(1, a_len + b_len + 2);
  291. memcpy(path, a, pre_len);
  292. path[pre_len] = '/';
  293. if (b) {
  294. memcpy(path + pre_len + 1,
  295. b + (lilv_is_dir_sep(b[0]) ? 1 : 0),
  296. lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len);
  297. }
  298. return path;
  299. }
  300. static void
  301. lilv_size_mtime(const char* path, off_t* size, time_t* time)
  302. {
  303. struct stat buf;
  304. if (stat(path, &buf)) {
  305. LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
  306. }
  307. if (size) {
  308. *size = buf.st_size;
  309. }
  310. if (time) {
  311. *time = buf.st_mtime;
  312. }
  313. }
  314. typedef struct {
  315. char* pattern;
  316. off_t orig_size;
  317. time_t time;
  318. char* latest;
  319. } Latest;
  320. static void
  321. update_latest(const char* path, const char* name, void* data)
  322. {
  323. Latest* latest = (Latest*)data;
  324. char* entry_path = lilv_path_join(path, name);
  325. unsigned num;
  326. if (sscanf(entry_path, latest->pattern, &num) == 1) {
  327. off_t entry_size = 0;
  328. time_t entry_time = 0;
  329. lilv_size_mtime(entry_path, &entry_size, &entry_time);
  330. if (entry_size == latest->orig_size && entry_time >= latest->time) {
  331. free(latest->latest);
  332. latest->latest = entry_path;
  333. }
  334. }
  335. if (entry_path != latest->latest) {
  336. free(entry_path);
  337. }
  338. }
  339. /** Return the latest copy of the file at @c path that is newer. */
  340. char*
  341. lilv_get_latest_copy(const char* path, const char* copy_path)
  342. {
  343. char* copy_dir = lilv_dirname(copy_path);
  344. Latest latest = { lilv_strjoin(copy_path, "%u", NULL), 0, 0, NULL };
  345. lilv_size_mtime(path, &latest.orig_size, &latest.time);
  346. lilv_dir_for_each(copy_dir, &latest, update_latest);
  347. free(latest.pattern);
  348. free(copy_dir);
  349. return latest.latest;
  350. }
  351. char*
  352. lilv_realpath(const char* path)
  353. {
  354. #ifdef _WIN32
  355. char* out = (char*)malloc(MAX_PATH);
  356. GetFullPathName(path, MAX_PATH, out, NULL);
  357. return out;
  358. #else
  359. char* real_path = realpath(path, NULL);
  360. return real_path ? real_path : lilv_strdup(path);
  361. #endif
  362. }
  363. int
  364. lilv_symlink(const char* oldpath, const char* newpath)
  365. {
  366. int ret = 0;
  367. if (strcmp(oldpath, newpath)) {
  368. #ifdef _WIN32
  369. ret = 0;
  370. #else
  371. ret = symlink(oldpath, newpath);
  372. #endif
  373. }
  374. if (ret) {
  375. LILV_ERRORF("Failed to link %s => %s (%s)\n",
  376. newpath, oldpath, strerror(errno));
  377. }
  378. return ret;
  379. }
  380. char*
  381. lilv_path_relative_to(const char* path, const char* base)
  382. {
  383. const size_t path_len = strlen(path);
  384. const size_t base_len = strlen(base);
  385. const size_t min_len = (path_len < base_len) ? path_len : base_len;
  386. // Find the last separator common to both paths
  387. size_t last_shared_sep = 0;
  388. for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) {
  389. if (lilv_is_dir_sep(path[i])) {
  390. last_shared_sep = i;
  391. }
  392. }
  393. if (last_shared_sep == 0) {
  394. // No common components, return path
  395. return lilv_strdup(path);
  396. }
  397. // Find the number of up references ("..") required
  398. size_t up = 0;
  399. for (size_t i = last_shared_sep + 1; i < base_len; ++i) {
  400. if (lilv_is_dir_sep(base[i])) {
  401. ++up;
  402. }
  403. }
  404. // Write up references
  405. const size_t suffix_len = path_len - last_shared_sep;
  406. char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1);
  407. for (size_t i = 0; i < up; ++i) {
  408. memcpy(rel + (i * 3), "../", 3);
  409. }
  410. // Write suffix
  411. memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len);
  412. return rel;
  413. }
  414. bool
  415. lilv_path_is_child(const char* path, const char* dir)
  416. {
  417. if (path && dir) {
  418. const size_t path_len = strlen(path);
  419. const size_t dir_len = strlen(dir);
  420. return dir && path_len >= dir_len && !strncmp(path, dir, dir_len);
  421. }
  422. return false;
  423. }
  424. int
  425. lilv_flock(FILE* file, bool lock)
  426. {
  427. #if defined(HAVE_FLOCK) && defined(HAVE_FILENO)
  428. return flock(fileno(file), lock ? LOCK_EX : LOCK_UN);
  429. #else
  430. return 0;
  431. #endif
  432. }
  433. void
  434. lilv_dir_for_each(const char* path,
  435. void* data,
  436. void (*f)(const char* path, const char* name, void* data))
  437. {
  438. #ifdef _WIN32
  439. char* pat = lilv_path_join(path, "*");
  440. WIN32_FIND_DATA fd;
  441. HANDLE fh = FindFirstFile(pat, &fd);
  442. if (fh != INVALID_HANDLE_VALUE) {
  443. do {
  444. f(path, fd.cFileName, data);
  445. } while (FindNextFile(fh, &fd));
  446. }
  447. free(pat);
  448. #else
  449. DIR* dir = opendir(path);
  450. if (dir) {
  451. struct dirent entry;
  452. struct dirent* result;
  453. while (!readdir_r(dir, &entry, &result) && result) {
  454. f(path, entry.d_name, data);
  455. }
  456. closedir(dir);
  457. }
  458. #endif
  459. }
  460. int
  461. lilv_mkdir_p(const char* dir_path)
  462. {
  463. char* path = lilv_strdup(dir_path);
  464. const size_t path_len = strlen(path);
  465. for (size_t i = 1; i <= path_len; ++i) {
  466. if (path[i] == LILV_DIR_SEP[0] || path[i] == '\0') {
  467. path[i] = '\0';
  468. if (mkdir(path, 0755) && errno != EEXIST) {
  469. LILV_ERRORF("Failed to create %s (%s)\n",
  470. path, strerror(errno));
  471. free(path);
  472. return 1;
  473. }
  474. path[i] = LILV_DIR_SEP[0];
  475. }
  476. }
  477. free(path);
  478. return 0;
  479. }
  480. static off_t
  481. lilv_file_size(const char* path)
  482. {
  483. struct stat buf;
  484. if (stat(path, &buf)) {
  485. LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
  486. return 0;
  487. }
  488. return buf.st_size;
  489. }
  490. bool
  491. lilv_file_equals(const char* a_path, const char* b_path)
  492. {
  493. if (!strcmp(a_path, b_path)) {
  494. return true; // Paths match
  495. }
  496. bool match = false;
  497. FILE* a_file = NULL;
  498. FILE* b_file = NULL;
  499. char* const a_real = lilv_realpath(a_path);
  500. char* const b_real = lilv_realpath(b_path);
  501. if (!a_real || !b_real) {
  502. match = false; // Missing file matches nothing
  503. } else if (!strcmp(a_real, b_real)) {
  504. match = true; // Real paths match
  505. } else if (lilv_file_size(a_path) != lilv_file_size(b_path)) {
  506. match = false; // Sizes differ
  507. } else if (!(a_file = fopen(a_real, "rb"))) {
  508. match = false; // Missing file matches nothing
  509. } else if (!(b_file = fopen(b_real, "rb"))) {
  510. match = false; // Missing file matches nothing
  511. } else {
  512. match = true;
  513. // TODO: Improve performance by reading chunks
  514. while (!feof(a_file) && !feof(b_file)) {
  515. if (fgetc(a_file) != fgetc(b_file)) {
  516. match = false;
  517. break;
  518. }
  519. }
  520. }
  521. if (a_file) {
  522. fclose(a_file);
  523. }
  524. if (b_file) {
  525. fclose(b_file);
  526. }
  527. free(a_real);
  528. free(b_real);
  529. return match;
  530. }