comparison mupdf-source/source/fitz/directory.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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
23 #include "mupdf/fitz.h"
24
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/stat.h>
28
29 #ifdef _WIN32
30 #include <windows.h>
31 #include <errno.h>
32 #define stat _stat
33 #else
34 #include <dirent.h>
35 #endif
36
37 typedef struct
38 {
39 fz_archive super;
40
41 char *path;
42
43 int max_entries;
44 int num_entries;
45 char **entries;
46 } fz_directory;
47
48 static void drop_directory(fz_context *ctx, fz_archive *arch)
49 {
50 fz_directory *dir = (fz_directory *) arch;
51 int i;
52
53 fz_free(ctx, dir->path);
54 for (i = 0; i < dir->num_entries; i++)
55 fz_free(ctx, dir->entries[i]);
56 fz_free(ctx, dir->entries);
57 }
58
59 static void make_dir_path(char *output, fz_archive *arch, const char *tail, size_t size)
60 {
61 /* Skip any leading ../ path segments, so we don't look outside the
62 * directory itself. The paths coming here have already been
63 * canonicalized with fz_cleanname so any remaining ".." parts are
64 * guaranteed to be at the start of the path.
65 */
66 fz_directory *dir = (fz_directory *) arch;
67 while (tail[0] == '.' && tail[1] == '.' && tail[2] == '/')
68 tail += 3;
69 fz_strlcpy(output, dir->path, size);
70 fz_strlcat(output, "/", size);
71 fz_strlcat(output, tail, size);
72 }
73
74 static fz_stream *open_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
75 {
76 char path[PATH_MAX];
77 make_dir_path(path, arch, name, sizeof path);
78 return fz_try_open_file(ctx, path);
79 }
80
81 static fz_buffer *read_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
82 {
83 char path[PATH_MAX];
84 make_dir_path(path, arch, name, sizeof path);
85 return fz_try_read_file(ctx, path);
86 }
87
88 static int has_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
89 {
90 char path[PATH_MAX];
91 make_dir_path(path, arch, name, sizeof path);
92 return fz_file_exists(ctx, path);
93 }
94
95 int
96 fz_is_directory(fz_context *ctx, const char *path)
97 {
98 #ifdef _WIN32
99 wchar_t *wpath = fz_wchar_from_utf8(ctx, path);
100 struct stat info;
101 int ret;
102
103 ret = _wstat(wpath, &info);
104 fz_free(ctx, wpath);
105 if (ret < 0)
106 return 0;
107
108 return S_ISDIR(info.st_mode);
109 #else
110 struct stat info;
111
112 if (stat(path, &info) < 0)
113 return 0;
114
115 return S_ISDIR(info.st_mode);
116 #endif
117 }
118
119 static int
120 count_dir_entries(fz_context *ctx, fz_archive *arch)
121 {
122 fz_directory *dir = (fz_directory *) arch;
123
124 return dir->num_entries;
125 }
126
127 const char *
128 list_dir_entry(fz_context *ctx, fz_archive *arch, int n)
129 {
130 fz_directory *dir = (fz_directory *) arch;
131
132 if (n < 0 || n >= dir->num_entries)
133 return NULL;
134
135 return dir->entries[n];
136 }
137
138 fz_archive *
139 fz_open_directory(fz_context *ctx, const char *path)
140 {
141 fz_directory *dir;
142 #ifdef _WIN32
143 WCHAR *wpath = NULL;
144 size_t z = 3;
145 HANDLE h = NULL;
146 WIN32_FIND_DATAW dw;
147
148 fz_var(wpath);
149 fz_var(h);
150 #else
151 DIR *dp;
152 struct dirent *ep;
153
154 fz_var(dp);
155 #endif
156
157 if (!fz_is_directory(ctx, path))
158 fz_throw(ctx, FZ_ERROR_FORMAT, "'%s' is not a directory", path);
159
160 dir = fz_new_derived_archive(ctx, NULL, fz_directory);
161 dir->super.format = "dir";
162 dir->super.count_entries = count_dir_entries;
163 dir->super.list_entry = list_dir_entry;
164 dir->super.has_entry = has_dir_entry;
165 dir->super.read_entry = read_dir_entry;
166 dir->super.open_entry = open_dir_entry;
167 dir->super.drop_archive = drop_directory;
168
169 fz_try(ctx)
170 {
171 #ifdef _WIN32
172 char const *p = path;
173 WCHAR *w;
174 while (*p)
175 {
176 int rune;
177 p += fz_chartorune(&rune, p);
178 if (rune >= 0x10000 || rune < 0)
179 {
180 errno = EINVAL;
181 fz_throw(ctx, FZ_ERROR_SYSTEM, "Unrepresentable UTF-8 char in directory name");
182 }
183 z++;
184 }
185 w = wpath = fz_malloc(ctx, z * sizeof(WCHAR));
186 p = path;
187 while (*p)
188 {
189 int rune;
190 p += fz_chartorune(&rune, p);
191 *w++ = rune;
192 }
193 w[0] = '\\';
194 w[1] = '*';
195 w[2] = 0;
196
197 /* Now enumerate the paths. */
198 h = FindFirstFileW(wpath, &dw);
199 if (h == INVALID_HANDLE_VALUE)
200 break;
201
202 do
203 {
204 char *u;
205
206 if (dir->max_entries == dir->num_entries)
207 {
208 int newmax = dir->max_entries * 2;
209 if (newmax == 0)
210 newmax = 32;
211
212 dir->entries = fz_realloc(ctx, dir->entries, sizeof(*dir->entries) * newmax);
213 dir->max_entries = newmax;
214 }
215
216 /* Count the len as utf-8. */
217 w = dw.cFileName;
218 z = 1;
219 while (*w)
220 z += fz_runelen(*w++);
221
222 u = dir->entries[dir->num_entries] = fz_malloc(ctx, z);
223 dir->num_entries++;
224
225 /* Copy the name across. */
226 w = dw.cFileName;
227 while (*w)
228 u += fz_runetochar(u, *w++);
229 *u = 0;
230 }
231 while (FindNextFileW(h, &dw));
232 #else
233 dp = opendir(path);
234 if (dp == NULL)
235 break;
236
237 while ((ep = readdir(dp)) != NULL)
238 {
239 if (dir->max_entries == dir->num_entries)
240 {
241 int newmax = dir->max_entries * 2;
242 if (newmax == 0)
243 newmax = 32;
244
245 dir->entries = fz_realloc(ctx, dir->entries, sizeof(*dir->entries) * newmax);
246 dir->max_entries = newmax;
247 }
248
249 dir->entries[dir->num_entries] = fz_strdup(ctx, ep->d_name);
250 dir->num_entries++;
251 }
252 #endif
253 dir->path = fz_strdup(ctx, path);
254 }
255 fz_always(ctx)
256 {
257 #ifdef _WIN32
258 fz_free(ctx, wpath);
259 if (h)
260 (void)FindClose(h);
261 #else
262 if (dp)
263 (void)closedir(dp);
264 #endif
265 }
266 fz_catch(ctx)
267 {
268 fz_drop_archive(ctx, &dir->super);
269 fz_rethrow(ctx);
270 }
271
272 return &dir->super;
273 }