fs.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /** @file fs.c
  2. * @brief Enchanced base filesystem operations.
  3. */
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6. #include <assert.h>
  7. #include <errno.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <dirent.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <fcntl.h>
  14. #include <libgen.h>
  15. #include "faux/str.h"
  16. /** @brief Reports size of file or directory.
  17. *
  18. * Function works recursively so directory size is a sum of all file size
  19. * inside it and size of subdirs.
  20. *
  21. * @param [in] path Filesystem path.
  22. * @return Size of filesystem object or < 0 on error.
  23. */
  24. ssize_t faux_filesize(const char *path)
  25. {
  26. struct stat statbuf = {};
  27. DIR *dir = NULL;
  28. struct dirent *entry = NULL;
  29. ssize_t sum = 0;
  30. assert(path);
  31. if (!path)
  32. return -1;
  33. if (stat(path, &statbuf) < 0)
  34. return -1;
  35. // Regular file
  36. if (!S_ISDIR(statbuf.st_mode))
  37. return statbuf.st_size;
  38. // Directory
  39. dir = opendir(path);
  40. if (!dir)
  41. return -1;
  42. // Get each file from 'path' directory
  43. for (entry = readdir(dir); entry; entry = readdir(dir)) {
  44. char *fn = NULL;
  45. ssize_t r = 0;
  46. // Ignore "." and ".."
  47. if ((faux_str_casecmp(entry->d_name, ".") == 0) ||
  48. (faux_str_casecmp(entry->d_name, "..") == 0))
  49. continue;
  50. // Construct filename
  51. fn = faux_str_sprintf("%s/%s", path, entry->d_name);
  52. r = faux_filesize(fn);
  53. faux_str_free(fn);
  54. if (r < 0)
  55. continue;
  56. sum += r;
  57. }
  58. closedir(dir);
  59. return sum;
  60. }
  61. /** @brief If given path is directory.
  62. *
  63. * @param [in] path Filesystem path.
  64. * @return 0 - success, < 0 on error.
  65. */
  66. bool_t faux_isdir(const char *path)
  67. {
  68. struct stat statbuf = {};
  69. assert(path);
  70. if (!path)
  71. return BOOL_FALSE;
  72. if (stat(path, &statbuf) < 0)
  73. return BOOL_FALSE;
  74. if (S_ISDIR(statbuf.st_mode))
  75. return BOOL_TRUE;
  76. return BOOL_FALSE;
  77. }
  78. /** @brief If given path is regular file.
  79. *
  80. * @param [in] path Filesystem path.
  81. * @return 0 - success, < 0 on error.
  82. */
  83. bool_t faux_isfile(const char *path)
  84. {
  85. struct stat statbuf = {};
  86. assert(path);
  87. if (!path)
  88. return BOOL_FALSE;
  89. if (stat(path, &statbuf) < 0)
  90. return BOOL_FALSE;
  91. if (S_ISREG(statbuf.st_mode))
  92. return BOOL_TRUE;
  93. return BOOL_FALSE;
  94. }
  95. /** @brief Removes filesystem objects recursively.
  96. *
  97. * Function can remove file or directory (recursively).
  98. *
  99. * @param [in] path File/directory name.
  100. * @return BOOL_TRUE - success, BOOL_FALSE on error.
  101. */
  102. bool_t faux_rm(const char *path)
  103. {
  104. DIR *dir = NULL;
  105. struct dirent *dir_entry = NULL;
  106. assert(path);
  107. if (!path)
  108. return BOOL_FALSE;
  109. // Common file (not dir)
  110. if (!faux_isdir(path)) {
  111. if (unlink(path) < 0)
  112. return BOOL_FALSE;
  113. return BOOL_TRUE;
  114. }
  115. // Directory
  116. if ((dir = opendir(path)) == NULL)
  117. return BOOL_FALSE;
  118. while ((dir_entry = readdir(dir))) {
  119. if (!strcmp(dir_entry->d_name, ".") ||
  120. !strcmp(dir_entry->d_name, ".."))
  121. continue;
  122. faux_rm(dir_entry->d_name);
  123. }
  124. closedir(dir);
  125. if (rmdir(path) < 0)
  126. return BOOL_FALSE;
  127. return BOOL_TRUE;
  128. }
  129. /** @brief Make dir with parents.
  130. *
  131. * This is the same as mkdir -p.
  132. *
  133. * @param [in] path Directory to create.
  134. * @param [in] mode Mode of newly created dir.
  135. * @return BOOL_TRUE - success, BOOL_FALSE on error.
  136. */
  137. bool_t faux_mkdir_p(const char *path, mode_t mode)
  138. {
  139. char *dir_d = NULL;
  140. char *dname = NULL;
  141. assert(path);
  142. if (!path)
  143. return BOOL_FALSE;
  144. // Already exists
  145. if (faux_isdir(path))
  146. return BOOL_TRUE;
  147. // Get dirname
  148. dir_d = faux_str_dup(path);
  149. dname = dirname(dir_d);
  150. if (!faux_mkdir_p(dname, mode)) {
  151. faux_str_free(dir_d);
  152. return BOOL_FALSE;
  153. }
  154. faux_str_free(dir_d);
  155. if (mkdir(path, mode) < 0)
  156. return BOOL_FALSE;
  157. return BOOL_TRUE;
  158. }
  159. /** @brief Expand tilde within path due to HOME env var.
  160. *
  161. * If first character of path is tilde then expand it to value of
  162. * environment variable HOME. If tilde is not the first character or
  163. * HOME is not defined then return copy of original path.
  164. *
  165. * @warning The resulting string must be freed by faux_str_free() later.
  166. *
  167. * @param [in] path Path to expand.
  168. * @return Expanded string or NULL on error.
  169. */
  170. char *faux_expand_tilde(const char *path)
  171. {
  172. char *home_dir = getenv("HOME");
  173. char *result = NULL;
  174. assert(path);
  175. if (!path)
  176. return NULL;
  177. // Tilde can be the first character only to be expanded
  178. if (home_dir && (path[0] == '~'))
  179. result = faux_str_sprintf("%s%s", home_dir, &path[1]);
  180. else
  181. result = faux_str_dup(path);
  182. return result;
  183. }