directory.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // Copyright (C) 2004-2024 Artifex Software, Inc.
  2. //
  3. // This file is part of MuPDF.
  4. //
  5. // MuPDF is free software: you can redistribute it and/or modify it under the
  6. // terms of the GNU Affero General Public License as published by the Free
  7. // Software Foundation, either version 3 of the License, or (at your option)
  8. // any later version.
  9. //
  10. // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
  11. // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. // details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
  17. //
  18. // Alternative licensing terms are available from the licensor.
  19. // For commercial licensing, see <https://www.artifex.com/> or contact
  20. // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  21. // CA 94129, USA, for further information.
  22. #include "mupdf/fitz.h"
  23. #include <string.h>
  24. #include <errno.h>
  25. #include <sys/stat.h>
  26. #ifdef _WIN32
  27. #include <windows.h>
  28. #include <errno.h>
  29. #define stat _stat
  30. #else
  31. #include <dirent.h>
  32. #endif
  33. typedef struct
  34. {
  35. fz_archive super;
  36. char *path;
  37. int max_entries;
  38. int num_entries;
  39. char **entries;
  40. } fz_directory;
  41. static void drop_directory(fz_context *ctx, fz_archive *arch)
  42. {
  43. fz_directory *dir = (fz_directory *) arch;
  44. int i;
  45. fz_free(ctx, dir->path);
  46. for (i = 0; i < dir->num_entries; i++)
  47. fz_free(ctx, dir->entries[i]);
  48. fz_free(ctx, dir->entries);
  49. }
  50. static void make_dir_path(char *output, fz_archive *arch, const char *tail, size_t size)
  51. {
  52. /* Skip any leading ../ path segments, so we don't look outside the
  53. * directory itself. The paths coming here have already been
  54. * canonicalized with fz_cleanname so any remaining ".." parts are
  55. * guaranteed to be at the start of the path.
  56. */
  57. fz_directory *dir = (fz_directory *) arch;
  58. while (tail[0] == '.' && tail[1] == '.' && tail[2] == '/')
  59. tail += 3;
  60. fz_strlcpy(output, dir->path, size);
  61. fz_strlcat(output, "/", size);
  62. fz_strlcat(output, tail, size);
  63. }
  64. static fz_stream *open_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
  65. {
  66. char path[PATH_MAX];
  67. make_dir_path(path, arch, name, sizeof path);
  68. return fz_try_open_file(ctx, path);
  69. }
  70. static fz_buffer *read_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
  71. {
  72. char path[PATH_MAX];
  73. make_dir_path(path, arch, name, sizeof path);
  74. return fz_try_read_file(ctx, path);
  75. }
  76. static int has_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
  77. {
  78. char path[PATH_MAX];
  79. make_dir_path(path, arch, name, sizeof path);
  80. return fz_file_exists(ctx, path);
  81. }
  82. int
  83. fz_is_directory(fz_context *ctx, const char *path)
  84. {
  85. #ifdef _WIN32
  86. wchar_t *wpath = fz_wchar_from_utf8(ctx, path);
  87. struct stat info;
  88. int ret;
  89. ret = _wstat(wpath, &info);
  90. fz_free(ctx, wpath);
  91. if (ret < 0)
  92. return 0;
  93. return S_ISDIR(info.st_mode);
  94. #else
  95. struct stat info;
  96. if (stat(path, &info) < 0)
  97. return 0;
  98. return S_ISDIR(info.st_mode);
  99. #endif
  100. }
  101. static int
  102. count_dir_entries(fz_context *ctx, fz_archive *arch)
  103. {
  104. fz_directory *dir = (fz_directory *) arch;
  105. return dir->num_entries;
  106. }
  107. const char *
  108. list_dir_entry(fz_context *ctx, fz_archive *arch, int n)
  109. {
  110. fz_directory *dir = (fz_directory *) arch;
  111. if (n < 0 || n >= dir->num_entries)
  112. return NULL;
  113. return dir->entries[n];
  114. }
  115. fz_archive *
  116. fz_open_directory(fz_context *ctx, const char *path)
  117. {
  118. fz_directory *dir;
  119. #ifdef _WIN32
  120. WCHAR *wpath = NULL;
  121. size_t z = 3;
  122. HANDLE h = NULL;
  123. WIN32_FIND_DATAW dw;
  124. fz_var(wpath);
  125. fz_var(h);
  126. #else
  127. DIR *dp;
  128. struct dirent *ep;
  129. fz_var(dp);
  130. #endif
  131. if (!fz_is_directory(ctx, path))
  132. fz_throw(ctx, FZ_ERROR_FORMAT, "'%s' is not a directory", path);
  133. dir = fz_new_derived_archive(ctx, NULL, fz_directory);
  134. dir->super.format = "dir";
  135. dir->super.count_entries = count_dir_entries;
  136. dir->super.list_entry = list_dir_entry;
  137. dir->super.has_entry = has_dir_entry;
  138. dir->super.read_entry = read_dir_entry;
  139. dir->super.open_entry = open_dir_entry;
  140. dir->super.drop_archive = drop_directory;
  141. fz_try(ctx)
  142. {
  143. #ifdef _WIN32
  144. char const *p = path;
  145. WCHAR *w;
  146. while (*p)
  147. {
  148. int rune;
  149. p += fz_chartorune(&rune, p);
  150. if (rune >= 0x10000 || rune < 0)
  151. {
  152. errno = EINVAL;
  153. fz_throw(ctx, FZ_ERROR_SYSTEM, "Unrepresentable UTF-8 char in directory name");
  154. }
  155. z++;
  156. }
  157. w = wpath = fz_malloc(ctx, z * sizeof(WCHAR));
  158. p = path;
  159. while (*p)
  160. {
  161. int rune;
  162. p += fz_chartorune(&rune, p);
  163. *w++ = rune;
  164. }
  165. w[0] = '\\';
  166. w[1] = '*';
  167. w[2] = 0;
  168. /* Now enumerate the paths. */
  169. h = FindFirstFileW(wpath, &dw);
  170. if (h == INVALID_HANDLE_VALUE)
  171. break;
  172. do
  173. {
  174. char *u;
  175. if (dir->max_entries == dir->num_entries)
  176. {
  177. int newmax = dir->max_entries * 2;
  178. if (newmax == 0)
  179. newmax = 32;
  180. dir->entries = fz_realloc(ctx, dir->entries, sizeof(*dir->entries) * newmax);
  181. dir->max_entries = newmax;
  182. }
  183. /* Count the len as utf-8. */
  184. w = dw.cFileName;
  185. z = 1;
  186. while (*w)
  187. z += fz_runelen(*w++);
  188. u = dir->entries[dir->num_entries] = fz_malloc(ctx, z);
  189. dir->num_entries++;
  190. /* Copy the name across. */
  191. w = dw.cFileName;
  192. while (*w)
  193. u += fz_runetochar(u, *w++);
  194. *u = 0;
  195. }
  196. while (FindNextFileW(h, &dw));
  197. #else
  198. dp = opendir(path);
  199. if (dp == NULL)
  200. break;
  201. while ((ep = readdir(dp)) != NULL)
  202. {
  203. if (dir->max_entries == dir->num_entries)
  204. {
  205. int newmax = dir->max_entries * 2;
  206. if (newmax == 0)
  207. newmax = 32;
  208. dir->entries = fz_realloc(ctx, dir->entries, sizeof(*dir->entries) * newmax);
  209. dir->max_entries = newmax;
  210. }
  211. dir->entries[dir->num_entries] = fz_strdup(ctx, ep->d_name);
  212. dir->num_entries++;
  213. }
  214. #endif
  215. dir->path = fz_strdup(ctx, path);
  216. }
  217. fz_always(ctx)
  218. {
  219. #ifdef _WIN32
  220. fz_free(ctx, wpath);
  221. if (h)
  222. (void)FindClose(h);
  223. #else
  224. if (dp)
  225. (void)closedir(dp);
  226. #endif
  227. }
  228. fz_catch(ctx)
  229. {
  230. fz_drop_archive(ctx, &dir->super);
  231. fz_rethrow(ctx);
  232. }
  233. return &dir->super;
  234. }