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.

872 lines
22 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2013-2014, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the COPYING file in this distribution for license details.
  7. * ----------------------------------------------------------------------
  8. */
  9. #include <assert.h>
  10. #include <dirent.h>
  11. #include <errno.h>
  12. #include <fcntl.h>
  13. #include <string.h>
  14. #include <sys/stat.h>
  15. #include <sys/types.h>
  16. #include <unistd.h>
  17. #include "libcork/core/attributes.h"
  18. #include "libcork/core/error.h"
  19. #include "libcork/core/types.h"
  20. #include "libcork/ds/array.h"
  21. #include "libcork/ds/buffer.h"
  22. #include "libcork/helpers/errors.h"
  23. #include "libcork/helpers/posix.h"
  24. #include "libcork/os/files.h"
  25. #include "libcork/os/subprocess.h"
  26. #if !defined(CORK_DEBUG_FILES)
  27. #define CORK_DEBUG_FILES 0
  28. #endif
  29. #if CORK_DEBUG_FILES
  30. #include <stdio.h>
  31. #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
  32. #else
  33. #define DEBUG(...) /* no debug messages */
  34. #endif
  35. /*-----------------------------------------------------------------------
  36. * Paths
  37. */
  38. struct cork_path {
  39. struct cork_buffer given;
  40. };
  41. static struct cork_path *
  42. cork_path_new_internal(const char *str, size_t length)
  43. {
  44. struct cork_path *path = cork_new(struct cork_path);
  45. cork_buffer_init(&path->given);
  46. if (length == 0) {
  47. cork_buffer_ensure_size(&path->given, 16);
  48. cork_buffer_set(&path->given, "", 0);
  49. } else {
  50. cork_buffer_set(&path->given, str, length);
  51. }
  52. return path;
  53. }
  54. struct cork_path *
  55. cork_path_new(const char *source)
  56. {
  57. return cork_path_new_internal(source, source == NULL? 0: strlen(source));
  58. }
  59. struct cork_path *
  60. cork_path_clone(const struct cork_path *other)
  61. {
  62. return cork_path_new_internal(other->given.buf, other->given.size);
  63. }
  64. void
  65. cork_path_free(struct cork_path *path)
  66. {
  67. cork_buffer_done(&path->given);
  68. cork_delete(struct cork_path, path);
  69. }
  70. void
  71. cork_path_set(struct cork_path *path, const char *content)
  72. {
  73. if (content == NULL) {
  74. cork_buffer_clear(&path->given);
  75. } else {
  76. cork_buffer_set_string(&path->given, content);
  77. }
  78. }
  79. const char *
  80. cork_path_get(const struct cork_path *path)
  81. {
  82. return path->given.buf;
  83. }
  84. #define cork_path_get(path) ((const char *) (path)->given.buf)
  85. #define cork_path_size(path) ((path)->given.size)
  86. #define cork_path_truncate(path, size) \
  87. (cork_buffer_truncate(&(path)->given, (size)))
  88. int
  89. cork_path_set_cwd(struct cork_path *path)
  90. {
  91. cork_buffer_ensure_size(&path->given, PATH_MAX);
  92. rip_check_posix(getcwd(path->given.buf, PATH_MAX));
  93. path->given.size = strlen(path->given.buf);
  94. return 0;
  95. }
  96. struct cork_path *
  97. cork_path_cwd(void)
  98. {
  99. struct cork_path *path = cork_path_new(NULL);
  100. ei_check(cork_path_set_cwd(path));
  101. return path;
  102. error:
  103. cork_path_free(path);
  104. return NULL;
  105. }
  106. int
  107. cork_path_set_absolute(struct cork_path *path)
  108. {
  109. struct cork_buffer buf;
  110. if (path->given.size > 0 &&
  111. cork_buffer_char(&path->given, 0) == '/') {
  112. /* The path is already absolute. */
  113. return 0;
  114. }
  115. cork_buffer_init(&buf);
  116. cork_buffer_ensure_size(&buf, PATH_MAX);
  117. ep_check_posix(getcwd(buf.buf, PATH_MAX));
  118. buf.size = strlen(buf.buf);
  119. cork_buffer_append(&buf, "/", 1);
  120. cork_buffer_append_copy(&buf, &path->given);
  121. cork_buffer_done(&path->given);
  122. path->given = buf;
  123. return 0;
  124. error:
  125. cork_buffer_done(&buf);
  126. return -1;
  127. }
  128. struct cork_path *
  129. cork_path_absolute(const struct cork_path *other)
  130. {
  131. struct cork_path *path = cork_path_clone(other);
  132. ei_check(cork_path_set_absolute(path));
  133. return path;
  134. error:
  135. cork_path_free(path);
  136. return NULL;
  137. }
  138. void
  139. cork_path_append(struct cork_path *path, const char *more)
  140. {
  141. if (more == NULL || more[0] == '\0') {
  142. return;
  143. }
  144. if (more[0] == '/') {
  145. /* If more starts with a "/", then it's absolute, and should replace
  146. * the contents of the current path. */
  147. cork_buffer_set_string(&path->given, more);
  148. } else {
  149. /* Otherwise, more is relative, and should be appended to the current
  150. * path. If the current given path doesn't end in a "/", then we need
  151. * to add one to keep the path well-formed. */
  152. if (path->given.size > 0 &&
  153. cork_buffer_char(&path->given, path->given.size - 1) != '/') {
  154. cork_buffer_append(&path->given, "/", 1);
  155. }
  156. cork_buffer_append_string(&path->given, more);
  157. }
  158. }
  159. struct cork_path *
  160. cork_path_join(const struct cork_path *other, const char *more)
  161. {
  162. struct cork_path *path = cork_path_clone(other);
  163. cork_path_append(path, more);
  164. return path;
  165. }
  166. void
  167. cork_path_append_path(struct cork_path *path, const struct cork_path *more)
  168. {
  169. cork_path_append(path, more->given.buf);
  170. }
  171. struct cork_path *
  172. cork_path_join_path(const struct cork_path *other, const struct cork_path *more)
  173. {
  174. struct cork_path *path = cork_path_clone(other);
  175. cork_path_append_path(path, more);
  176. return path;
  177. }
  178. void
  179. cork_path_set_basename(struct cork_path *path)
  180. {
  181. char *given = path->given.buf;
  182. const char *last_slash = strrchr(given, '/');
  183. if (last_slash != NULL) {
  184. size_t offset = last_slash - given;
  185. size_t basename_length = path->given.size - offset - 1;
  186. memmove(given, last_slash + 1, basename_length);
  187. given[basename_length] = '\0';
  188. path->given.size = basename_length;
  189. }
  190. }
  191. struct cork_path *
  192. cork_path_basename(const struct cork_path *other)
  193. {
  194. struct cork_path *path = cork_path_clone(other);
  195. cork_path_set_basename(path);
  196. return path;
  197. }
  198. void
  199. cork_path_set_dirname(struct cork_path *path)
  200. {
  201. const char *given = path->given.buf;
  202. const char *last_slash = strrchr(given, '/');
  203. if (last_slash == NULL) {
  204. cork_buffer_clear(&path->given);
  205. } else {
  206. size_t offset = last_slash - given;
  207. if (offset == 0) {
  208. /* A special case for the immediate subdirectories of "/" */
  209. cork_buffer_truncate(&path->given, 1);
  210. } else {
  211. cork_buffer_truncate(&path->given, offset);
  212. }
  213. }
  214. }
  215. struct cork_path *
  216. cork_path_dirname(const struct cork_path *other)
  217. {
  218. struct cork_path *path = cork_path_clone(other);
  219. cork_path_set_dirname(path);
  220. return path;
  221. }
  222. /*-----------------------------------------------------------------------
  223. * Lists of paths
  224. */
  225. struct cork_path_list {
  226. cork_array(struct cork_path *) array;
  227. struct cork_buffer string;
  228. };
  229. struct cork_path_list *
  230. cork_path_list_new_empty(void)
  231. {
  232. struct cork_path_list *list = cork_new(struct cork_path_list);
  233. cork_array_init(&list->array);
  234. cork_buffer_init(&list->string);
  235. return list;
  236. }
  237. void
  238. cork_path_list_free(struct cork_path_list *list)
  239. {
  240. size_t i;
  241. for (i = 0; i < cork_array_size(&list->array); i++) {
  242. struct cork_path *path = cork_array_at(&list->array, i);
  243. cork_path_free(path);
  244. }
  245. cork_array_done(&list->array);
  246. cork_buffer_done(&list->string);
  247. cork_delete(struct cork_path_list, list);
  248. }
  249. const char *
  250. cork_path_list_to_string(const struct cork_path_list *list)
  251. {
  252. return list->string.buf;
  253. }
  254. void
  255. cork_path_list_add(struct cork_path_list *list, struct cork_path *path)
  256. {
  257. cork_array_append(&list->array, path);
  258. if (cork_array_size(&list->array) > 1) {
  259. cork_buffer_append(&list->string, ":", 1);
  260. }
  261. cork_buffer_append_string(&list->string, cork_path_get(path));
  262. }
  263. size_t
  264. cork_path_list_size(const struct cork_path_list *list)
  265. {
  266. return cork_array_size(&list->array);
  267. }
  268. const struct cork_path *
  269. cork_path_list_get(const struct cork_path_list *list, size_t index)
  270. {
  271. return cork_array_at(&list->array, index);
  272. }
  273. static void
  274. cork_path_list_append_string(struct cork_path_list *list, const char *str)
  275. {
  276. struct cork_path *path;
  277. const char *curr = str;
  278. const char *next;
  279. while ((next = strchr(curr, ':')) != NULL) {
  280. size_t size = next - curr;
  281. path = cork_path_new_internal(curr, size);
  282. cork_path_list_add(list, path);
  283. curr = next + 1;
  284. }
  285. path = cork_path_new(curr);
  286. cork_path_list_add(list, path);
  287. }
  288. struct cork_path_list *
  289. cork_path_list_new(const char *str)
  290. {
  291. struct cork_path_list *list = cork_path_list_new_empty();
  292. cork_path_list_append_string(list, str);
  293. return list;
  294. }
  295. /*-----------------------------------------------------------------------
  296. * Files
  297. */
  298. struct cork_file {
  299. struct cork_path *path;
  300. struct stat stat;
  301. enum cork_file_type type;
  302. bool has_stat;
  303. };
  304. static void
  305. cork_file_init(struct cork_file *file, struct cork_path *path)
  306. {
  307. file->path = path;
  308. file->has_stat = false;
  309. }
  310. struct cork_file *
  311. cork_file_new(const char *path)
  312. {
  313. return cork_file_new_from_path(cork_path_new(path));
  314. }
  315. struct cork_file *
  316. cork_file_new_from_path(struct cork_path *path)
  317. {
  318. struct cork_file *file = cork_new(struct cork_file);
  319. cork_file_init(file, path);
  320. return file;
  321. }
  322. static void
  323. cork_file_reset(struct cork_file *file)
  324. {
  325. file->has_stat = false;
  326. }
  327. static void
  328. cork_file_done(struct cork_file *file)
  329. {
  330. cork_path_free(file->path);
  331. }
  332. void
  333. cork_file_free(struct cork_file *file)
  334. {
  335. cork_file_done(file);
  336. cork_delete(struct cork_file, file);
  337. }
  338. const struct cork_path *
  339. cork_file_path(struct cork_file *file)
  340. {
  341. return file->path;
  342. }
  343. static int
  344. cork_file_stat(struct cork_file *file)
  345. {
  346. if (file->has_stat) {
  347. return 0;
  348. } else {
  349. int rc;
  350. rc = stat(cork_path_get(file->path), &file->stat);
  351. if (rc == -1) {
  352. if (errno == ENOENT || errno == ENOTDIR) {
  353. file->type = CORK_FILE_MISSING;
  354. file->has_stat = true;
  355. return 0;
  356. } else {
  357. cork_system_error_set();
  358. return -1;
  359. }
  360. }
  361. if (S_ISREG(file->stat.st_mode)) {
  362. file->type = CORK_FILE_REGULAR;
  363. } else if (S_ISDIR(file->stat.st_mode)) {
  364. file->type = CORK_FILE_DIRECTORY;
  365. } else if (S_ISLNK(file->stat.st_mode)) {
  366. file->type = CORK_FILE_SYMLINK;
  367. } else {
  368. file->type = CORK_FILE_UNKNOWN;
  369. }
  370. file->has_stat = true;
  371. return 0;
  372. }
  373. }
  374. int
  375. cork_file_exists(struct cork_file *file, bool *exists)
  376. {
  377. rii_check(cork_file_stat(file));
  378. *exists = (file->type != CORK_FILE_MISSING);
  379. return 0;
  380. }
  381. int
  382. cork_file_type(struct cork_file *file, enum cork_file_type *type)
  383. {
  384. rii_check(cork_file_stat(file));
  385. *type = file->type;
  386. return 0;
  387. }
  388. struct cork_file *
  389. cork_path_list_find_file(const struct cork_path_list *list,
  390. const char *rel_path)
  391. {
  392. size_t i;
  393. size_t count = cork_path_list_size(list);
  394. struct cork_file *file;
  395. for (i = 0; i < count; i++) {
  396. const struct cork_path *path = cork_path_list_get(list, i);
  397. struct cork_path *joined = cork_path_join(path, rel_path);
  398. bool exists;
  399. file = cork_file_new_from_path(joined);
  400. ei_check(cork_file_exists(file, &exists));
  401. if (exists) {
  402. return file;
  403. } else {
  404. cork_file_free(file);
  405. }
  406. }
  407. cork_error_set_printf
  408. (ENOENT, "%s not found in %s",
  409. rel_path, cork_path_list_to_string(list));
  410. return NULL;
  411. error:
  412. cork_file_free(file);
  413. return NULL;
  414. }
  415. /*-----------------------------------------------------------------------
  416. * Directories
  417. */
  418. int
  419. cork_file_iterate_directory(struct cork_file *file,
  420. cork_file_directory_iterator iterator,
  421. void *user_data)
  422. {
  423. DIR *dir = NULL;
  424. struct dirent *entry;
  425. size_t dir_path_size;
  426. struct cork_path *child_path;
  427. struct cork_file child_file;
  428. rip_check_posix(dir = opendir(cork_path_get(file->path)));
  429. child_path = cork_path_clone(file->path);
  430. cork_file_init(&child_file, child_path);
  431. dir_path_size = cork_path_size(child_path);
  432. errno = 0;
  433. while ((entry = readdir(dir)) != NULL) {
  434. /* Skip the "." and ".." entries */
  435. if (strcmp(entry->d_name, ".") == 0 ||
  436. strcmp(entry->d_name, "..") == 0) {
  437. continue;
  438. }
  439. cork_path_append(child_path, entry->d_name);
  440. ei_check(cork_file_stat(&child_file));
  441. /* If the entry is a subdirectory, recurse into it. */
  442. ei_check(iterator(&child_file, entry->d_name, user_data));
  443. /* Remove this entry name from the path buffer. */
  444. cork_path_truncate(child_path, dir_path_size);
  445. cork_file_reset(&child_file);
  446. /* We have to reset errno to 0 because of the ambiguous way readdir uses
  447. * a return value of NULL. Other functions may return normally yet set
  448. * errno to a non-zero value. dlopen on Mac OS X is an ogreish example.
  449. * Since an error readdir is indicated by returning NULL and setting
  450. * errno to indicate the error, then we need to reset it to zero before
  451. * each call. We shall assume, perhaps to our great misery, that
  452. * functions within this loop do proper error checking and act
  453. * accordingly. */
  454. errno = 0;
  455. }
  456. /* Check errno immediately after the while loop terminates */
  457. if (CORK_UNLIKELY(errno != 0)) {
  458. cork_system_error_set();
  459. goto error;
  460. }
  461. cork_file_done(&child_file);
  462. rii_check_posix(closedir(dir));
  463. return 0;
  464. error:
  465. cork_file_done(&child_file);
  466. rii_check_posix(closedir(dir));
  467. return -1;
  468. }
  469. static int
  470. cork_file_mkdir_one(struct cork_file *file, cork_file_mode mode,
  471. unsigned int flags)
  472. {
  473. DEBUG("mkdir %s\n", cork_path_get(file->path));
  474. /* First check if the directory already exists. */
  475. rii_check(cork_file_stat(file));
  476. if (file->type == CORK_FILE_DIRECTORY) {
  477. DEBUG(" Already exists!\n");
  478. if (!(flags & CORK_FILE_PERMISSIVE)) {
  479. cork_system_error_set_explicit(EEXIST);
  480. return -1;
  481. } else {
  482. return 0;
  483. }
  484. } else if (file->type != CORK_FILE_MISSING) {
  485. DEBUG(" Exists and not a directory!\n");
  486. cork_system_error_set_explicit(EEXIST);
  487. return -1;
  488. }
  489. /* If the caller asked for a recursive mkdir, then make sure the parent
  490. * directory exists. */
  491. if (flags & CORK_FILE_RECURSIVE) {
  492. struct cork_path *parent = cork_path_dirname(file->path);
  493. DEBUG(" Checking parent %s\n", cork_path_get(parent));
  494. if (parent->given.size == 0) {
  495. /* There is no parent; we're either at the filesystem root (for an
  496. * absolute path) or the current directory (for a relative one).
  497. * Either way, we can assume it already exists. */
  498. cork_path_free(parent);
  499. } else {
  500. int rc;
  501. struct cork_file parent_file;
  502. cork_file_init(&parent_file, parent);
  503. rc = cork_file_mkdir_one
  504. (&parent_file, mode, flags | CORK_FILE_PERMISSIVE);
  505. cork_file_done(&parent_file);
  506. rii_check(rc);
  507. }
  508. }
  509. /* Create the directory already! */
  510. DEBUG(" Creating %s\n", cork_path_get(file->path));
  511. rii_check_posix(mkdir(cork_path_get(file->path), mode));
  512. return 0;
  513. }
  514. int
  515. cork_file_mkdir(struct cork_file *file, cork_file_mode mode,
  516. unsigned int flags)
  517. {
  518. return cork_file_mkdir_one(file, mode, flags);
  519. }
  520. static int
  521. cork_file_remove_iterator(struct cork_file *file, const char *rel_name,
  522. void *user_data)
  523. {
  524. unsigned int *flags = user_data;
  525. return cork_file_remove(file, *flags);
  526. }
  527. int
  528. cork_file_remove(struct cork_file *file, unsigned int flags)
  529. {
  530. DEBUG("rm %s\n", cork_path_get(file->path));
  531. rii_check(cork_file_stat(file));
  532. if (file->type == CORK_FILE_MISSING) {
  533. if (flags & CORK_FILE_PERMISSIVE) {
  534. return 0;
  535. } else {
  536. cork_system_error_set_explicit(ENOENT);
  537. return -1;
  538. }
  539. } else if (file->type == CORK_FILE_DIRECTORY) {
  540. if (flags & CORK_FILE_RECURSIVE) {
  541. /* The user asked that we delete the contents of the directory
  542. * first. */
  543. rii_check(cork_file_iterate_directory
  544. (file, cork_file_remove_iterator, &flags));
  545. }
  546. rii_check_posix(rmdir(cork_path_get(file->path)));
  547. return 0;
  548. } else {
  549. rii_check(unlink(cork_path_get(file->path)));
  550. return 0;
  551. }
  552. }
  553. /*-----------------------------------------------------------------------
  554. * Lists of files
  555. */
  556. struct cork_file_list {
  557. cork_array(struct cork_file *) array;
  558. };
  559. struct cork_file_list *
  560. cork_file_list_new_empty(void)
  561. {
  562. struct cork_file_list *list = cork_new(struct cork_file_list);
  563. cork_array_init(&list->array);
  564. return list;
  565. }
  566. void
  567. cork_file_list_free(struct cork_file_list *list)
  568. {
  569. size_t i;
  570. for (i = 0; i < cork_array_size(&list->array); i++) {
  571. struct cork_file *file = cork_array_at(&list->array, i);
  572. cork_file_free(file);
  573. }
  574. cork_array_done(&list->array);
  575. cork_delete(struct cork_file_list, list);
  576. }
  577. void
  578. cork_file_list_add(struct cork_file_list *list, struct cork_file *file)
  579. {
  580. cork_array_append(&list->array, file);
  581. }
  582. size_t
  583. cork_file_list_size(struct cork_file_list *list)
  584. {
  585. return cork_array_size(&list->array);
  586. }
  587. struct cork_file *
  588. cork_file_list_get(struct cork_file_list *list, size_t index)
  589. {
  590. return cork_array_at(&list->array, index);
  591. }
  592. struct cork_file_list *
  593. cork_file_list_new(struct cork_path_list *path_list)
  594. {
  595. struct cork_file_list *list = cork_file_list_new_empty();
  596. size_t count = cork_path_list_size(path_list);
  597. size_t i;
  598. for (i = 0; i < count; i++) {
  599. const struct cork_path *path = cork_path_list_get(path_list, i);
  600. struct cork_file *file = cork_file_new(cork_path_get(path));
  601. cork_array_append(&list->array, file);
  602. }
  603. return list;
  604. }
  605. struct cork_file_list *
  606. cork_path_list_find_files(const struct cork_path_list *path_list,
  607. const char *rel_path)
  608. {
  609. size_t i;
  610. size_t count = cork_path_list_size(path_list);
  611. struct cork_file_list *list = cork_file_list_new_empty();
  612. struct cork_file *file;
  613. for (i = 0; i < count; i++) {
  614. const struct cork_path *path = cork_path_list_get(path_list, i);
  615. struct cork_path *joined = cork_path_join(path, rel_path);
  616. bool exists;
  617. file = cork_file_new_from_path(joined);
  618. ei_check(cork_file_exists(file, &exists));
  619. if (exists) {
  620. cork_file_list_add(list, file);
  621. } else {
  622. cork_file_free(file);
  623. }
  624. }
  625. return list;
  626. error:
  627. cork_file_list_free(list);
  628. cork_file_free(file);
  629. return NULL;
  630. }
  631. /*-----------------------------------------------------------------------
  632. * Standard paths and path lists
  633. */
  634. #define empty_string(str) ((str) == NULL || (str)[0] == '\0')
  635. struct cork_path *
  636. cork_path_home(void)
  637. {
  638. const char *path = cork_env_get(NULL, "HOME");
  639. if (empty_string(path)) {
  640. cork_undefined("Cannot determine home directory");
  641. return NULL;
  642. } else {
  643. return cork_path_new(path);
  644. }
  645. }
  646. struct cork_path_list *
  647. cork_path_config_paths(void)
  648. {
  649. struct cork_path_list *list = cork_path_list_new_empty();
  650. const char *var;
  651. struct cork_path *path;
  652. /* The first entry should be the user's configuration directory. This is
  653. * specified by $XDG_CONFIG_HOME, with $HOME/.config as the default. */
  654. var = cork_env_get(NULL, "XDG_CONFIG_HOME");
  655. if (empty_string(var)) {
  656. ep_check(path = cork_path_home());
  657. cork_path_append(path, ".config");
  658. cork_path_list_add(list, path);
  659. } else {
  660. path = cork_path_new(var);
  661. cork_path_list_add(list, path);
  662. }
  663. /* The remaining entries should be the system-wide configuration
  664. * directories. These are specified by $XDG_CONFIG_DIRS, with /etc/xdg as
  665. * the default. */
  666. var = cork_env_get(NULL, "XDG_CONFIG_DIRS");
  667. if (empty_string(var)) {
  668. path = cork_path_new("/etc/xdg");
  669. cork_path_list_add(list, path);
  670. } else {
  671. cork_path_list_append_string(list, var);
  672. }
  673. return list;
  674. error:
  675. cork_path_list_free(list);
  676. return NULL;
  677. }
  678. struct cork_path_list *
  679. cork_path_data_paths(void)
  680. {
  681. struct cork_path_list *list = cork_path_list_new_empty();
  682. const char *var;
  683. struct cork_path *path;
  684. /* The first entry should be the user's data directory. This is specified
  685. * by $XDG_DATA_HOME, with $HOME/.local/share as the default. */
  686. var = cork_env_get(NULL, "XDG_DATA_HOME");
  687. if (empty_string(var)) {
  688. ep_check(path = cork_path_home());
  689. cork_path_append(path, ".local/share");
  690. cork_path_list_add(list, path);
  691. } else {
  692. path = cork_path_new(var);
  693. cork_path_list_add(list, path);
  694. }
  695. /* The remaining entries should be the system-wide configuration
  696. * directories. These are specified by $XDG_DATA_DIRS, with
  697. * /usr/local/share:/usr/share as the the default. */
  698. var = cork_env_get(NULL, "XDG_DATA_DIRS");
  699. if (empty_string(var)) {
  700. path = cork_path_new("/usr/local/share");
  701. cork_path_list_add(list, path);
  702. path = cork_path_new("/usr/share");
  703. cork_path_list_add(list, path);
  704. } else {
  705. cork_path_list_append_string(list, var);
  706. }
  707. return list;
  708. error:
  709. cork_path_list_free(list);
  710. return NULL;
  711. }
  712. struct cork_path *
  713. cork_path_user_cache_path(void)
  714. {
  715. const char *var;
  716. struct cork_path *path;
  717. /* The user's cache directory is specified by $XDG_CACHE_HOME, with
  718. * $HOME/.cache as the default. */
  719. var = cork_env_get(NULL, "XDG_CACHE_HOME");
  720. if (empty_string(var)) {
  721. rpp_check(path = cork_path_home());
  722. cork_path_append(path, ".cache");
  723. return path;
  724. } else {
  725. return cork_path_new(var);
  726. }
  727. }
  728. struct cork_path *
  729. cork_path_user_runtime_path(void)
  730. {
  731. const char *var;
  732. /* The user's cache directory is specified by $XDG_RUNTIME_DIR, with
  733. * no default given by the spec. */
  734. var = cork_env_get(NULL, "XDG_RUNTIME_DIR");
  735. if (empty_string(var)) {
  736. cork_undefined("Cannot determine user-specific runtime directory");
  737. return NULL;
  738. } else {
  739. return cork_path_new(var);
  740. }
  741. }