/src/mupdf/source/fitz/directory.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (C) 2004-2021 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 _MSC_VER |
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 | 0 | { |
50 | 0 | fz_directory *dir = (fz_directory *) arch; |
51 | 0 | int i; |
52 | |
|
53 | 0 | fz_free(ctx, dir->path); |
54 | 0 | for (i = 0; i < dir->num_entries; i++) |
55 | 0 | fz_free(ctx, dir->entries[i]); |
56 | 0 | fz_free(ctx, dir->entries); |
57 | 0 | } |
58 | | |
59 | | static void make_dir_path(char *output, fz_archive *arch, const char *tail, size_t size) |
60 | 0 | { |
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 | 0 | fz_directory *dir = (fz_directory *) arch; |
67 | 0 | while (tail[0] == '.' && tail[1] == '.' && tail[2] == '/') |
68 | 0 | tail += 3; |
69 | 0 | fz_strlcpy(output, dir->path, size); |
70 | 0 | fz_strlcat(output, "/", size); |
71 | 0 | fz_strlcat(output, tail, size); |
72 | 0 | } |
73 | | |
74 | | static fz_stream *open_dir_entry(fz_context *ctx, fz_archive *arch, const char *name) |
75 | 0 | { |
76 | 0 | char path[PATH_MAX]; |
77 | 0 | make_dir_path(path, arch, name, sizeof path); |
78 | 0 | return fz_try_open_file(ctx, path); |
79 | 0 | } |
80 | | |
81 | | static fz_buffer *read_dir_entry(fz_context *ctx, fz_archive *arch, const char *name) |
82 | 0 | { |
83 | 0 | char path[PATH_MAX]; |
84 | 0 | make_dir_path(path, arch, name, sizeof path); |
85 | 0 | return fz_try_read_file(ctx, path); |
86 | 0 | } |
87 | | |
88 | | static int has_dir_entry(fz_context *ctx, fz_archive *arch, const char *name) |
89 | 0 | { |
90 | 0 | char path[PATH_MAX]; |
91 | 0 | make_dir_path(path, arch, name, sizeof path); |
92 | 0 | return fz_file_exists(ctx, path); |
93 | 0 | } |
94 | | |
95 | | int |
96 | | fz_is_directory(fz_context *ctx, const char *path) |
97 | 0 | { |
98 | 0 | struct stat info; |
99 | |
|
100 | 0 | if (stat(path, &info) < 0) |
101 | 0 | return 0; |
102 | | |
103 | 0 | return S_ISDIR(info.st_mode); |
104 | 0 | } |
105 | | |
106 | | static int |
107 | | count_dir_entries(fz_context *ctx, fz_archive *arch) |
108 | 0 | { |
109 | 0 | fz_directory *dir = (fz_directory *) arch; |
110 | |
|
111 | 0 | return dir->num_entries; |
112 | 0 | } |
113 | | |
114 | | const char * |
115 | | list_dir_entry(fz_context *ctx, fz_archive *arch, int n) |
116 | 0 | { |
117 | 0 | fz_directory *dir = (fz_directory *) arch; |
118 | |
|
119 | 0 | if (n < 0 || n >= dir->num_entries) |
120 | 0 | return NULL; |
121 | | |
122 | 0 | return dir->entries[n]; |
123 | 0 | } |
124 | | |
125 | | fz_archive * |
126 | | fz_open_directory(fz_context *ctx, const char *path) |
127 | 0 | { |
128 | 0 | fz_directory *dir; |
129 | | #ifdef _MSC_VER |
130 | | WCHAR *wpath = NULL; |
131 | | size_t z = 3; |
132 | | HANDLE h = NULL; |
133 | | WIN32_FIND_DATAW dw; |
134 | | |
135 | | fz_var(wpath); |
136 | | fz_var(h); |
137 | | #else |
138 | 0 | DIR *dp; |
139 | 0 | struct dirent *ep; |
140 | |
|
141 | 0 | fz_var(dp); |
142 | 0 | #endif |
143 | |
|
144 | 0 | if (!fz_is_directory(ctx, path)) |
145 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "'%s' is not a directory", path); |
146 | | |
147 | 0 | dir = fz_new_derived_archive(ctx, NULL, fz_directory); |
148 | 0 | dir->super.format = "dir"; |
149 | 0 | dir->super.count_entries = count_dir_entries; |
150 | 0 | dir->super.list_entry = list_dir_entry; |
151 | 0 | dir->super.has_entry = has_dir_entry; |
152 | 0 | dir->super.read_entry = read_dir_entry; |
153 | 0 | dir->super.open_entry = open_dir_entry; |
154 | 0 | dir->super.drop_archive = drop_directory; |
155 | |
|
156 | 0 | fz_try(ctx) |
157 | 0 | { |
158 | | #ifdef _MSC_VER |
159 | | char const *p = path; |
160 | | WCHAR *w; |
161 | | while (*p) |
162 | | { |
163 | | int rune; |
164 | | p += fz_chartorune(&rune, p); |
165 | | if (rune >= 0x10000 || rune < 0) |
166 | | { |
167 | | errno = EINVAL; |
168 | | fz_throw(ctx, FZ_ERROR_SYSTEM, "Unrepresentable UTF-8 char in directory name"); |
169 | | } |
170 | | z++; |
171 | | } |
172 | | w = wpath = fz_malloc(ctx, z * sizeof(WCHAR)); |
173 | | p = path; |
174 | | while (*p) |
175 | | { |
176 | | int rune; |
177 | | p += fz_chartorune(&rune, p); |
178 | | *w++ = rune; |
179 | | } |
180 | | w[0] = '\\'; |
181 | | w[1] = '*'; |
182 | | w[2] = 0; |
183 | | |
184 | | /* Now enumerate the paths. */ |
185 | | h = FindFirstFileW(wpath, &dw); |
186 | | if (h == INVALID_HANDLE_VALUE) |
187 | | break; |
188 | | |
189 | | do |
190 | | { |
191 | | char *u; |
192 | | |
193 | | if (dir->max_entries == dir->num_entries) |
194 | | { |
195 | | int newmax = dir->max_entries * 2; |
196 | | if (newmax == 0) |
197 | | newmax = 32; |
198 | | |
199 | | dir->entries = fz_realloc(ctx, dir->entries, sizeof(*dir->entries) * newmax); |
200 | | dir->max_entries = newmax; |
201 | | } |
202 | | |
203 | | /* Count the len as utf-8. */ |
204 | | w = dw.cFileName; |
205 | | z = 1; |
206 | | while (*w) |
207 | | z += fz_runelen(*w++); |
208 | | |
209 | | u = dir->entries[dir->num_entries] = fz_malloc(ctx, z); |
210 | | dir->num_entries++; |
211 | | |
212 | | /* Copy the name across. */ |
213 | | w = dw.cFileName; |
214 | | while (*w) |
215 | | u += fz_runetochar(u, *w++); |
216 | | *u = 0; |
217 | | } |
218 | | while (FindNextFileW(h, &dw)); |
219 | | #else |
220 | 0 | dp = opendir(path); |
221 | 0 | if (dp == NULL) |
222 | 0 | break; |
223 | | |
224 | 0 | while ((ep = readdir(dp)) != NULL) |
225 | 0 | { |
226 | 0 | if (dir->max_entries == dir->num_entries) |
227 | 0 | { |
228 | 0 | int newmax = dir->max_entries * 2; |
229 | 0 | if (newmax == 0) |
230 | 0 | newmax = 32; |
231 | |
|
232 | 0 | dir->entries = fz_realloc(ctx, dir->entries, sizeof(*dir->entries) * newmax); |
233 | 0 | dir->max_entries = newmax; |
234 | 0 | } |
235 | |
|
236 | 0 | dir->entries[dir->num_entries] = fz_strdup(ctx, ep->d_name); |
237 | 0 | dir->num_entries++; |
238 | 0 | } |
239 | 0 | #endif |
240 | 0 | dir->path = fz_strdup(ctx, path); |
241 | 0 | } |
242 | 0 | fz_always(ctx) |
243 | 0 | { |
244 | | #ifdef _MSC_VER |
245 | | fz_free(ctx, wpath); |
246 | | if (h) |
247 | | (void)FindClose(h); |
248 | | #else |
249 | 0 | if (dp) |
250 | 0 | (void)closedir(dp); |
251 | 0 | #endif |
252 | 0 | } |
253 | 0 | fz_catch(ctx) |
254 | 0 | { |
255 | 0 | fz_drop_archive(ctx, &dir->super); |
256 | 0 | fz_rethrow(ctx); |
257 | 0 | } |
258 | | |
259 | 0 | return &dir->super; |
260 | 0 | } |