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
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2013, 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. free(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, path->given.size - 1) == '/') {
  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 its absolute, and should replace the
  146. * 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. free(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. free(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. free(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. }