Branch data Line data Source code
1 : : /* -*- mode: C -*-
2 : : *
3 : : * File: pdf-fsys-disk.c
4 : : * Date: Thu May 22 18:27:35 2008
5 : : *
6 : : * GNU PDF Library - Disk Filesystem Implementation
7 : : *
8 : : */
9 : :
10 : : /* Copyright (C) 2008-2011 Free Software Foundation, Inc. */
11 : :
12 : : /* This program is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU General Public License as published by
14 : : * the Free Software Foundation, either version 3 of the License, or
15 : : * (at your option) any later version.
16 : : *
17 : : * This program is distributed in the hope that it will be useful,
18 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : : * GNU General Public License for more details.
21 : : *
22 : : * You should have received a copy of the GNU General Public License
23 : : * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : : */
25 : :
26 : : #include <config.h>
27 : :
28 : : #include <errno.h>
29 : : #include <stdio.h>
30 : : #include <stdio-safer.h>
31 : : #include <string.h>
32 : : #include <sys/stat.h>
33 : :
34 : : #if defined PDF_HOST_WIN32 || defined PDF_HOST_BSD
35 : : # include <sys/param.h>
36 : : # if defined PDF_HOST_BSD
37 : : # include <sys/mount.h>
38 : : # endif /* PDF_HOST_BSD */
39 : : #else
40 : : # include <sys/statfs.h>
41 : : #endif /* PDF_HOST_WIN32 | PDF_HOST_BSD */
42 : :
43 : : #if defined PDF_HOST_WIN32
44 : : # include <windows.h>
45 : : # include <wchar.h>
46 : : #endif /* !PDF_HOST_WIN32 */
47 : :
48 : : #include <dirent.h>
49 : : #include <unistd.h>
50 : :
51 : : #include <pdf-types.h>
52 : : #include <pdf-error.h>
53 : : #include <pdf-fsys-disk.h>
54 : :
55 : : #if FILE_SYSTEM_BACKSLASH_IS_FILE_NAME_SEPARATOR
56 : : # define DIR_SEPARATOR_C '\\'
57 : : # define DIR_SEPARATOR_S "\\"
58 : : # define IS_DIR_SEPARATOR(c) (((c) == '/') || ((c) == '\\'))
59 : : #else
60 : : # define DIR_SEPARATOR_C '/'
61 : : # define DIR_SEPARATOR_S "/"
62 : : # define IS_DIR_SEPARATOR(c) ((c) == '/')
63 : : #endif /* FILE_SYSTEM_BACKSLASH_IS_FILE_NAME_SEPARATOR */
64 : :
65 : : /* Filesystem-specific implementation */
66 : : struct pdf_fsys_disk_s
67 : : {
68 : : /* The common parent struct */
69 : : struct pdf_fsys_s common;
70 : :
71 : : /* Precomputed filename separator */
72 : : pdf_text_t *filename_separator;
73 : : };
74 : :
75 : : /* Filesystem-specific implementation of open files */
76 : : struct pdf_fsys_disk_file_s
77 : : {
78 : : /* The common parent struct */
79 : : struct pdf_fsys_file_s common;
80 : :
81 : : /* Host-encoded path */
82 : : pdf_char_t *host_path;
83 : : pdf_size_t host_path_length;
84 : :
85 : : /* The descriptor of the open file */
86 : : FILE *file_descriptor;
87 : : };
88 : :
89 : : /* Private function declarations */
90 : :
91 : : static pdf_status_t get_status_from_errno (int _errno);
92 : :
93 : : static pdf_char_t *get_host_path (const pdf_text_t *path,
94 : : pdf_size_t *host_path_size,
95 : : pdf_error_t **error);
96 : :
97 : : static pdf_text_t *set_host_path (const pdf_char_t *host_path,
98 : : const pdf_size_t host_path_size,
99 : : pdf_error_t **error);
100 : :
101 : : static const pdf_char_t *get_mode_string (const enum pdf_fsys_file_mode_e mode);
102 : :
103 : : #ifdef PDF_HOST_WIN32
104 : : static pdf_bool_t win32_device_p (const pdf_text_t *path,
105 : : pdf_error_t **error);
106 : : #endif
107 : :
108 : : /*
109 : : * Filesystem Interface Implementation
110 : : */
111 : :
112 : : /* Private function to de-initialize and de-allocate base file data */
113 : : static void
114 : 26 : deinit_base_file_data (struct pdf_fsys_disk_file_s *file)
115 : : {
116 : 26 : pdf_fsys_impl_helper_file_deinit (&(file->common));
117 : :
118 [ + + ]: 26 : if (file->host_path)
119 : 25 : pdf_dealloc (file->host_path);
120 : 26 : }
121 : :
122 : : /* Private function to allocate and initialize base file data */
123 : : static pdf_bool_t
124 : 26 : init_base_file_data (struct pdf_fsys_disk_file_s *file,
125 : : const struct pdf_fsys_s *fsys,
126 : : const pdf_text_t *path,
127 : : enum pdf_fsys_file_mode_e mode,
128 : : pdf_error_t **error)
129 : : {
130 : 26 : memset (file, 0, sizeof (struct pdf_fsys_disk_file_s));
131 : :
132 : : /* Initialize common data */
133 [ - + ]: 26 : if (!pdf_fsys_impl_helper_file_init (&(file->common),
134 : : fsys,
135 : : path,
136 : : mode,
137 : : error))
138 : : {
139 : 0 : deinit_base_file_data (file);
140 : 0 : return PDF_FALSE;
141 : : }
142 : :
143 : : /* Get the host encoded path */
144 [ + + ]: 26 : if (file->common.unicode_path)
145 : : {
146 : 25 : file->host_path = get_host_path (file->common.unicode_path,
147 : : &(file->host_path_length),
148 : : error);
149 [ - + ]: 25 : if (!file->host_path)
150 : : {
151 : 0 : deinit_base_file_data (file);
152 : 0 : return PDF_FALSE;
153 : : }
154 : : }
155 : :
156 : 26 : return PDF_TRUE;
157 : : }
158 : :
159 : : static pdf_bool_t
160 : : is_absolute_path (const pdf_text_t *path,
161 : : pdf_error_t **error)
162 : : {
163 : : pdf_char_t *utf8;
164 : : pdf_size_t utf8_size;
165 : :
166 : 16 : utf8 = pdf_text_get_unicode (path,
167 : : PDF_TEXT_UTF8,
168 : : PDF_TEXT_UNICODE_WITH_NUL_SUFFIX,
169 : : &utf8_size,
170 : : error);
171 [ - + ]: 16 : if (!utf8)
172 : 0 : return PDF_FALSE;
173 : :
174 [ - + ]: 16 : if (utf8_size == 0)
175 : : {
176 : 0 : pdf_dealloc (utf8);
177 : 0 : return PDF_FALSE;
178 : : }
179 : :
180 [ + - ]: 16 : if (IS_DIR_SEPARATOR (utf8[0]))
181 : : {
182 : 16 : pdf_dealloc (utf8);
183 : 16 : return PDF_TRUE;
184 : : }
185 : :
186 : : #ifdef PDF_HOST_WIN32
187 : : /* Recognize drive letter in native windows */
188 : : if (utf8_size >= 3 &&
189 : : ((utf8[0] >= 'a' && utf8[0] <= 'z') ||
190 : : (utf8[0] >= 'A' && utf8[0] <= 'Z')) &&
191 : : utf8[1] == ':' &&
192 : : IS_DIR_SEPARATOR (utf8[2]))
193 : : {
194 : : pdf_dealloc (utf8);
195 : : return PDF_TRUE;
196 : : }
197 : : #endif /* PDF_HOST_WIN32 */
198 : :
199 : 0 : pdf_dealloc (utf8);
200 : 0 : return PDF_FALSE;
201 : : }
202 : :
203 : : static pdf_text_t *
204 : : get_current_path (pdf_error_t **error)
205 : : {
206 : : #ifdef PDF_HOST_WIN32
207 : : wchar_t dummy[2];
208 : : wchar_t *current;
209 : : pdf_size_t current_len;
210 : : pdf_text_t *current_text;
211 : :
212 : : /* Get number of wchar_t items that may get written when getting current
213 : : * directory (includes last NIL wchar_t).
214 : : * Note len here is given in wchar_ts not in bytes */
215 : : current_len = GetCurrentDirectoryW (2, dummy);
216 : :
217 : : /* Allocate buffer to store the current dir */
218 : : current = pdf_alloc (sizeof (wchar_t) * current_len);
219 : : if (!current)
220 : : {
221 : : pdf_set_error (error,
222 : : PDF_EDOMAIN_BASE_FSYS,
223 : : PDF_ENOMEM,
224 : : "cannot get current path: "
225 : : "couldn't allocate %lu bytes",
226 : : sizeof (wchar_t) * current_len);
227 : : return NULL;
228 : : }
229 : :
230 : : /* Fill the buffer with the current directory */
231 : : if (GetCurrentDirectoryW (current_len, current) != (current_len - 1))
232 : : {
233 : : pdf_dealloc (current);
234 : : pdf_set_error (error,
235 : : PDF_EDOMAIN_BASE_FSYS,
236 : : PDF_ENOMEM,
237 : : "cannot get current path: "
238 : : "couldn't query current directory");
239 : : return NULL;
240 : : }
241 : :
242 : : current_text = set_host_path ((const pdf_char_t *)current,
243 : : (current_len - 1) * sizeof (wchar_t),
244 : : error);
245 : :
246 : : pdf_dealloc (current);
247 : : return current_text;
248 : :
249 : : #else
250 : : pdf_char_t *current;
251 : : pdf_text_t *current_text;
252 : :
253 : : /* Rely on gnulib's getcwd module for portability issues */
254 : 0 : current = getcwd (NULL, 0);
255 [ # # ]: 0 : if (!current)
256 : : {
257 : 0 : pdf_set_error (error,
258 : : PDF_EDOMAIN_BASE_FSYS,
259 : : PDF_ENOMEM,
260 : : "cannot build absolute path: "
261 : : "couldn't get current directory");
262 : 0 : return NULL;
263 : : }
264 : :
265 : 0 : current_text = set_host_path (current,
266 : : strlen (current),
267 : : error);
268 : :
269 : 0 : pdf_dealloc (current);
270 : 0 : return current_text;
271 : :
272 : : #endif /* !PDF_HOST_WIN32 */
273 : : }
274 : :
275 : : static pdf_char_t *
276 : : skip_root_utf8 (const pdf_char_t *path_utf8)
277 : : {
278 : 16 : pdf_char_t *p = (pdf_char_t *)path_utf8;
279 : :
280 : : #ifdef PDF_HOST_WIN32
281 : : /* On windows, skip leading X:\ */
282 : : if (((p[0] >= 'a' && p[0] <= 'z') ||
283 : : (p[0] >= 'A' && p[0] <= 'Z')) &&
284 : : p[1] == ':' &&
285 : : IS_DIR_SEPARATOR (p[2]))
286 : : p = &p[3];
287 : : #else
288 : : /* Skip initial slash */
289 [ + - ]: 16 : if (IS_DIR_SEPARATOR (p[0]))
290 : 16 : p++;
291 : : #endif /* PDF_HOST_WIN32 */
292 : :
293 : 16 : return p;
294 : : }
295 : :
296 : : /* Returns UTF-8 encoded path */
297 : : static pdf_char_t *
298 : 16 : ensure_absolute_path (const pdf_fsys_t *fsys,
299 : : const pdf_text_t *path,
300 : : pdf_error_t **error)
301 : : {
302 : 16 : pdf_error_t *inner_error = NULL;
303 : : pdf_char_t *path_utf8;
304 : : pdf_size_t path_utf8_size;
305 : :
306 [ - + ]: 16 : if (!is_absolute_path (path, &inner_error))
307 : : {
308 : : pdf_text_t *current;
309 : : pdf_text_t *absolute;
310 : :
311 [ # # ]: 0 : if (inner_error)
312 : : {
313 : 0 : pdf_propagate_error (error, inner_error);
314 : 0 : return NULL;
315 : : }
316 : :
317 : : /* Get current path */
318 : 0 : current = get_current_path (error);
319 [ # # ]: 0 : if (!current)
320 : 0 : return NULL;
321 : :
322 : : /* Build path with current absolute plus relative */
323 : 0 : absolute = pdf_fsys_build_path (fsys,
324 : : error,
325 : : current,
326 : : path,
327 : : NULL);
328 : 0 : pdf_text_destroy (current);
329 : :
330 [ # # ]: 0 : if (!absolute)
331 : 0 : return NULL;
332 : :
333 : : /* Get UTF-8 of the resulting absolute path */
334 : 0 : path_utf8 = pdf_text_get_unicode (absolute,
335 : : PDF_TEXT_UTF8,
336 : : PDF_TEXT_UNICODE_WITH_NUL_SUFFIX,
337 : : &path_utf8_size,
338 : : error);
339 : 0 : pdf_text_destroy (absolute);
340 : : }
341 : : else
342 : : {
343 : : /* Get UTF-8 of the original absolute path */
344 : 16 : path_utf8 = pdf_text_get_unicode (path,
345 : : PDF_TEXT_UTF8,
346 : : PDF_TEXT_UNICODE_WITH_NUL_SUFFIX,
347 : : &path_utf8_size,
348 : : error);
349 : : }
350 : :
351 : 16 : return path_utf8;
352 : : }
353 : :
354 : : /* Based on GLib's canonicalize_filename(), in gio/glocalfile.c
355 : : * Copyright (C) 2006-2007 Red Hat, Inc.
356 : : * Author: Alexander Larsson <alexl@redhat.com>
357 : : *
358 : : * Returns either UTF-8 encoded path or pdf_text_t, or both
359 : : */
360 : : static pdf_bool_t
361 : 16 : canonicalize_path (const pdf_fsys_t *fsys,
362 : : const pdf_text_t *path,
363 : : pdf_text_t **canonicalized_path,
364 : : pdf_char_t **canonicalized_path_utf8,
365 : : pdf_error_t **error)
366 : : {
367 : : pdf_char_t *canon;
368 : : pdf_char_t *start;
369 : : pdf_char_t *p;
370 : : pdf_char_t *q;
371 : : int i;
372 : :
373 : : /* Get UTF-8 encoded path */
374 : 16 : canon = ensure_absolute_path (fsys, path, error);
375 [ - + ]: 16 : if (!canon)
376 : 0 : return PDF_FALSE;
377 : :
378 : : /* Skip root */
379 : 16 : start = skip_root_utf8 (canon);
380 : :
381 : : /* POSIX allows double slashes at the start to
382 : : * mean something special (as does windows too).
383 : : * So, "//" != "/", but more than two slashes
384 : : * is treated as "/".
385 : : */
386 : 16 : i = 0;
387 [ + + ][ + - ]: 32 : for (p = start - 1;
388 : : (p >= canon) &&
389 : 16 : IS_DIR_SEPARATOR (*p);
390 : 16 : p--)
391 : 16 : i++;
392 [ - + ]: 16 : if (i > 2)
393 : : {
394 : 0 : i -= 1;
395 : 0 : start -= i;
396 : 0 : memmove (start, start + i, strlen (start + i) + 1);
397 : : }
398 : :
399 : 16 : p = start;
400 [ + + ]: 72 : while (*p != 0)
401 : : {
402 [ + + ][ + + ]: 56 : if (p[0] == '.' && (p[1] == 0 || IS_DIR_SEPARATOR (p[1])))
403 : : {
404 : 2 : memmove (p, p + 1, strlen (p + 1) + 1);
405 : : }
406 [ + + ][ + - ]: 68 : else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || IS_DIR_SEPARATOR (p[2])))
[ + - ]
407 : : {
408 : 14 : q = p + 2;
409 : : /* Skip previous separator */
410 : 14 : p = p - 2;
411 [ - + ]: 14 : if (p < start)
412 : 0 : p = start;
413 [ + - ][ + + ]: 78 : while (p > start && !IS_DIR_SEPARATOR (*p))
414 : 64 : p--;
415 [ + - ]: 14 : if (IS_DIR_SEPARATOR (*p))
416 : 14 : *p++ = DIR_SEPARATOR_C;
417 : 14 : memmove (p, q, strlen (q)+1);
418 : : }
419 : : else
420 : : {
421 : : /* Skip until next separator */
422 [ + + ]: 212 : while (*p != 0 && !IS_DIR_SEPARATOR (*p))
423 : 172 : p++;
424 : :
425 [ + + ]: 40 : if (*p != 0)
426 : : {
427 : : /* Canonicalize one separator */
428 : 32 : *p++ = DIR_SEPARATOR_C;
429 : : }
430 : : }
431 : :
432 : : /* Remove additional separators */
433 : 56 : q = p;
434 [ + + ]: 110 : while (*q && IS_DIR_SEPARATOR (*q))
435 : 54 : q++;
436 : :
437 [ + + ]: 56 : if (p != q)
438 : 24 : memmove (p, q, strlen (q)+1);
439 : : }
440 : :
441 : : /* Remove trailing slashes */
442 [ + + ][ + + ]: 16 : if (p > start && IS_DIR_SEPARATOR (*(p-1)))
443 : 6 : *(p-1) = 0;
444 : :
445 : :
446 : : /* If length of the canonicalized path is zero, report error */
447 [ - + ]: 16 : if (canon [0] == '\0')
448 : : {
449 : 0 : pdf_set_error (error,
450 : : PDF_EDOMAIN_BASE_FSYS,
451 : : PDF_EBADDATA,
452 : : "empty canonicalized path built");
453 : 0 : pdf_dealloc (canon);
454 : 0 : return PDF_FALSE;
455 : : }
456 : :
457 : : /* Set output(s) */
458 : :
459 [ - + ]: 16 : if (canonicalized_path)
460 : 0 : *canonicalized_path = pdf_text_new_from_unicode (canon,
461 : : strlen (canon),
462 : : PDF_TEXT_UTF8,
463 : : error);
464 : :
465 [ + - ]: 16 : if (canonicalized_path_utf8)
466 : 16 : *canonicalized_path_utf8 = canon;
467 : : else
468 : 0 : pdf_dealloc (canon);
469 : :
470 : 16 : return PDF_TRUE;
471 : : }
472 : :
473 : : /* Host-dependent fopen() */
474 : : #ifdef PDF_HOST_WIN32
475 : : #define PDF_FOPEN(f,m) _wfopen((wchar_t *)f,(wchar_t *)m)
476 : : #else
477 : : #define PDF_FOPEN(f,m) fopen_safer(f,m)
478 : : #endif
479 : :
480 : : static pdf_fsys_file_t *
481 : 25 : file_open (const pdf_fsys_t *fsys,
482 : : const pdf_text_t *path,
483 : : enum pdf_fsys_file_mode_e mode,
484 : : pdf_error_t **error)
485 : : {
486 : : struct pdf_fsys_disk_file_s *file;
487 : :
488 [ - + ]: 25 : PDF_ASSERT_POINTER_RETURN_VAL (path, NULL);
489 : : PDF_ASSERT_RETURN_VAL (mode >= PDF_FSYS_OPEN_MODE_FIRST, NULL);
490 [ - + ]: 25 : PDF_ASSERT_RETURN_VAL (mode <= PDF_FSYS_OPEN_MODE_LAST, NULL);
491 : :
492 : : /* Allocate private data storage for the file */
493 : 25 : file = (struct pdf_fsys_disk_file_s *) pdf_alloc (sizeof (struct pdf_fsys_disk_file_s));
494 [ - + ]: 25 : if (!file)
495 : : {
496 : 0 : pdf_set_error (error,
497 : : PDF_EDOMAIN_BASE_FSYS,
498 : : PDF_ENOMEM,
499 : : "cannot create disk file object: "
500 : : "couldn't allocate %lu bytes",
501 : : (unsigned long) sizeof (struct pdf_fsys_disk_file_s));
502 : 0 : return NULL;
503 : : }
504 : :
505 : : /* Init base data */
506 [ - + ]: 25 : if (!init_base_file_data (file,
507 : : fsys,
508 : : path,
509 : : mode,
510 : : error))
511 : : {
512 : 0 : deinit_base_file_data (file);
513 : 0 : pdf_dealloc (file);
514 : 0 : return NULL;
515 : : }
516 : :
517 : : /* Open the file */
518 : 25 : file->file_descriptor = PDF_FOPEN (file->host_path,
519 : : get_mode_string (mode));
520 [ + + ]: 25 : if (!file->file_descriptor)
521 : : {
522 : 2 : pdf_set_error (error,
523 : : PDF_EDOMAIN_BASE_FSYS,
524 : : get_status_from_errno (errno),
525 : : "cannot open file: '%s'",
526 : : strerror (errno));
527 : 2 : deinit_base_file_data (file);
528 : 2 : pdf_dealloc (file);
529 : 2 : return NULL;
530 : : }
531 : :
532 : 25 : return (struct pdf_fsys_file_s *)file;
533 : : }
534 : :
535 : : static pdf_fsys_file_t *
536 : 1 : file_open_tmp (const pdf_fsys_t *fsys,
537 : : pdf_error_t **error)
538 : : {
539 : : struct pdf_fsys_disk_file_s *file;
540 : :
541 : : /* Allocate private data storage for the file */
542 : 1 : file = (struct pdf_fsys_disk_file_s *) pdf_alloc (sizeof (struct pdf_fsys_disk_file_s));
543 [ - + ]: 1 : if (!file)
544 : : {
545 : 0 : pdf_set_error (error,
546 : : PDF_EDOMAIN_BASE_FSYS,
547 : : PDF_ENOMEM,
548 : : "cannot create disk file object: "
549 : : "couldn't allocate %lu bytes",
550 : : (unsigned long) sizeof (struct pdf_fsys_disk_file_s));
551 : 0 : return NULL;
552 : : }
553 : :
554 : : /* Init base data */
555 [ - + ]: 1 : if (!init_base_file_data (file,
556 : : fsys,
557 : : NULL,
558 : : PDF_FSYS_OPEN_MODE_RW,
559 : : error))
560 : : {
561 : 0 : deinit_base_file_data (file);
562 : 0 : pdf_dealloc (file);
563 : 0 : return NULL;
564 : : }
565 : :
566 : : /* Open a temporary file. */
567 : 1 : file->file_descriptor = tmpfile_safer ();
568 [ - + ]: 1 : if (!file->file_descriptor)
569 : : {
570 : 0 : pdf_set_error (error,
571 : : PDF_EDOMAIN_BASE_FSYS,
572 : : get_status_from_errno (errno),
573 : : "cannot open temporary file: '%s'",
574 : : strerror (errno));
575 : 0 : deinit_base_file_data (file);
576 : 0 : pdf_dealloc (file);
577 : 0 : return NULL;
578 : : }
579 : :
580 : 1 : return (struct pdf_fsys_file_s *)file;
581 : : }
582 : :
583 : : static pdf_bool_t
584 : 24 : file_close (pdf_fsys_file_t *file,
585 : : pdf_error_t **error)
586 : : {
587 : 24 : struct pdf_fsys_disk_file_s *disk_file = (struct pdf_fsys_disk_file_s *)file;
588 : 24 : pdf_bool_t ret = PDF_TRUE;
589 : :
590 [ - + ]: 24 : PDF_ASSERT_POINTER_RETURN_VAL (file, PDF_FALSE);
591 [ - + ]: 24 : PDF_ASSERT_RETURN_VAL (disk_file->file_descriptor > 0, PDF_FALSE);
592 : :
593 : : /* Host path is optional, not available in tmp files */
594 : :
595 : : /* Close the I/O stream only if still open */
596 [ + - - + ]: 48 : if (disk_file->file_descriptor &&
597 : 24 : fclose (disk_file->file_descriptor) == EOF)
598 : : {
599 : : /* Note that even if an error is set, the file is fully closed
600 : : * and the object disposed */
601 : 0 : pdf_set_error (error,
602 : : PDF_EDOMAIN_BASE_FSYS,
603 : : get_status_from_errno (errno),
604 : : "cannot close temporary file: '%s'",
605 : : strerror (errno));
606 : 0 : ret = PDF_FALSE;
607 : : }
608 : :
609 : 24 : deinit_base_file_data (disk_file);
610 : 24 : pdf_dealloc (disk_file);
611 : 24 : return ret;
612 : : }
613 : :
614 : : /* Host-dependent mkdir() */
615 : : #ifdef PDF_HOST_WIN32
616 : : #define PDF_MKDIR(f,m) _wmkdir((const wchar_t *)f)
617 : : #else
618 : : #define PDF_MKDIR(f,m) mkdir(f,m)
619 : : #endif
620 : :
621 : : static pdf_bool_t
622 : 0 : create_folder (const pdf_fsys_t *fsys,
623 : : const pdf_text_t *path,
624 : : pdf_error_t **error)
625 : : {
626 : : pdf_char_t *host_path;
627 : :
628 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (path, PDF_FALSE);
629 : :
630 : : /* Get a host-encoded version of the path name */
631 : 0 : host_path = get_host_path (path, NULL, error);
632 [ # # ]: 0 : if (!host_path)
633 : 0 : return PDF_FALSE;
634 : :
635 : : /* Set the permissions of the new directory (posix-only):
636 : : * rwxr_xr_x
637 : : */
638 : :
639 : : /* Create the directory */
640 [ # # ]: 0 : if (PDF_MKDIR (host_path, \
641 : : (S_IRUSR | S_IWUSR | S_IXUSR | \
642 : : S_IRGRP | S_IXGRP | \
643 : : S_IROTH | S_IXOTH)) != 0)
644 : : {
645 : 0 : pdf_set_error (error,
646 : : PDF_EDOMAIN_BASE_FSYS,
647 : : get_status_from_errno (errno),
648 : : "cannot create directory: '%s'",
649 : : strerror (errno));
650 : 0 : pdf_dealloc (host_path);
651 : 0 : return PDF_FALSE;
652 : : }
653 : :
654 : 0 : pdf_dealloc (host_path);
655 : 0 : return PDF_TRUE;
656 : : }
657 : :
658 : : /* Host-dependent opendir(), closedir() and friends */
659 : : #ifdef PDF_HOST_WIN32
660 : : #define PDF_DIR _WDIR
661 : : #define pdf_dirent_s _wdirent
662 : : #define PDF_OPENDIR(f) _wopendir ((const wchar_t *)f)
663 : : #define PDF_READDIR(ds) _wreaddir ((PDF_DIR *)ds)
664 : : #define PDF_CLOSEDIR(ds) _wclosedir ((PDF_DIR *)ds)
665 : : /* In mingw dir_entry->d_namlen is an array of FILENAME_MAX
666 : : * octects long. The dir_entry->d_namlen contain the length of
667 : : * the name stored in d_name */
668 : : #define PDF_NAMELEN(de) (de->d_namlen)
669 : : #else
670 : : #define PDF_DIR DIR
671 : : #define pdf_dirent_s dirent
672 : : #define PDF_OPENDIR(f) opendir ((const char *)f)
673 : : #define PDF_READDIR(ds) readdir ((PDF_DIR *)ds)
674 : : #define PDF_CLOSEDIR(ds) closedir ((PDF_DIR *)ds)
675 : : /* In POSIX systems dir_entry->d_name is a NULL-terminated
676 : : * string */
677 : : #define PDF_NAMELEN(de) (strlen (de->d_name))
678 : :
679 : : #endif /* !PDF_HOST_WIN32 */
680 : :
681 : : static pdf_list_t *
682 : 0 : get_folder_contents (const pdf_fsys_t *fsys,
683 : : const pdf_text_t *path,
684 : : pdf_error_t **error)
685 : : {
686 : : PDF_DIR *dir_stream;
687 : : pdf_char_t *host_path;
688 : : struct pdf_dirent_s *dir_entry;
689 : : pdf_list_t *list;
690 : :
691 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (path, NULL);
692 : :
693 : : /* Get a host-encoded version of the path name */
694 : 0 : host_path = get_host_path (path, NULL, error);
695 [ # # ]: 0 : if (!host_path)
696 : 0 : return NULL;
697 : :
698 : : /* Open the directory stream */
699 : 0 : dir_stream = PDF_OPENDIR (host_path);
700 [ # # ]: 0 : if (!dir_stream)
701 : : {
702 : 0 : pdf_set_error (error,
703 : : PDF_EDOMAIN_BASE_FSYS,
704 : : get_status_from_errno (errno),
705 : : "cannot open directory: '%s'",
706 : : strerror (errno));
707 : 0 : pdf_dealloc (host_path);
708 : 0 : return NULL;
709 : : }
710 : :
711 : : /* Create the list to be returned.
712 : : * Note that we don't expect duplicates when listing files in
713 : : * a single directory, so there's no point in comparing the
714 : : * entry names and disallowing duplicates. */
715 : 0 : list = pdf_list_new (NULL,
716 : : (pdf_list_element_dispose_fn_t)pdf_text_destroy,
717 : : PDF_TRUE,
718 : : error);
719 [ # # ]: 0 : if (!list)
720 : : {
721 : 0 : PDF_CLOSEDIR (dir_stream);
722 : 0 : pdf_dealloc (host_path);
723 : 0 : return NULL;
724 : : }
725 : :
726 : : /* Scan directory contents */
727 [ # # ]: 0 : while ((dir_entry = PDF_READDIR (dir_stream)) != NULL)
728 : : {
729 : : pdf_text_t *entry_text;
730 : : pdf_u32_t name_length;
731 : :
732 : : /* Note that dir_entry is statically allocated and can be
733 : : * rewritten by a subsequent call. Also, there is not need to
734 : : * free that structure */
735 : :
736 : : /* Get the length of the entry name */
737 : 0 : name_length = PDF_NAMELEN (dir_entry);
738 : :
739 : : /* Create the text object containing the entry name */
740 : 0 : entry_text = set_host_path ((const pdf_char_t *)dir_entry->d_name,
741 : : name_length,
742 : : error);
743 [ # # ]: 0 : if (!entry_text)
744 : : {
745 : 0 : PDF_CLOSEDIR (dir_stream);
746 : 0 : pdf_dealloc (host_path);
747 [ # # ]: 0 : if (list)
748 : 0 : pdf_list_destroy (list);
749 : 0 : return NULL;
750 : : }
751 : :
752 [ # # ]: 0 : if (!pdf_list_add_last (list,
753 : : entry_text,
754 : : error))
755 : : {
756 : 0 : PDF_CLOSEDIR (dir_stream);
757 : 0 : PDF_CLOSEDIR (dir_stream);
758 : 0 : pdf_dealloc (host_path);
759 : 0 : return NULL;
760 : : }
761 : :
762 : : /* Go on to next item */
763 : : }
764 : :
765 : 0 : PDF_CLOSEDIR (dir_stream);
766 : 0 : return list;
767 : : }
768 : :
769 : : static pdf_bool_t
770 : 16 : get_directory_and_filename (const pdf_fsys_t *fsys,
771 : : const pdf_text_t *path,
772 : : pdf_text_t **directory,
773 : : pdf_text_t **filename,
774 : : pdf_char_t **filename_utf8,
775 : : pdf_error_t **error)
776 : : {
777 : : pdf_size_t utf8_size;
778 : : pdf_char_t *utf8;
779 : : pdf_char_t *p;
780 : : pdf_size_t filename_size;
781 : :
782 : : /* Canonicalize path and get output in UTF-8 */
783 [ - + ]: 16 : if (!canonicalize_path (fsys, path, NULL, &utf8, error))
784 : 0 : return PDF_FALSE;
785 : :
786 : : /* Move pointer to last character in the path */
787 : 16 : utf8_size = strlen (utf8);
788 : 16 : p = &utf8[utf8_size - 1];
789 : :
790 : : /* Skip possible separator at the end of the path */
791 [ + + ]: 16 : if (IS_DIR_SEPARATOR (*p))
792 : 2 : p--;
793 : :
794 : : /* Find previous directory separator */
795 [ + + ][ + + ]: 74 : while (p >= utf8 &&
796 : 72 : !IS_DIR_SEPARATOR (*p))
797 : 58 : p--;
798 : :
799 : : /* Directory separator found? */
800 [ + + ]: 16 : if (p < utf8)
801 : : {
802 : : /* No parent */
803 : 2 : pdf_dealloc (utf8);
804 : 2 : return NULL;
805 : : }
806 : :
807 : : /* Filename starts always after the dir separator */
808 : 14 : filename_size = strlen (&p[1]);
809 : :
810 : : /* Get filename if requested */
811 [ + + ]: 14 : if (filename)
812 : : {
813 : 7 : *filename = pdf_text_new_from_unicode (&p[1],
814 : : filename_size,
815 : : PDF_TEXT_UTF8,
816 : : error);
817 [ - + ]: 7 : if (!*filename)
818 : : {
819 : 0 : pdf_dealloc (utf8);
820 : 0 : return PDF_FALSE;
821 : : }
822 : : }
823 : :
824 : : /* Get filename in UTF-8 if requested */
825 [ - + ]: 14 : if (filename_utf8)
826 : : {
827 : 0 : *filename_utf8 = pdf_alloc (filename_size + 1);
828 [ # # ]: 0 : if (!*filename_utf8)
829 : : {
830 : 0 : pdf_set_error (error,
831 : : PDF_EDOMAIN_BASE_FSYS,
832 : : PDF_ENOMEM,
833 : : "cannot get filename: "
834 : : "couldn't allocate %lu bytes",
835 : : (unsigned long)(filename_size + 1));
836 [ # # ]: 0 : if (filename)
837 : 0 : pdf_text_destroy (*filename);
838 : 0 : return PDF_FALSE;
839 : : }
840 : 0 : memcpy (*filename_utf8, &p[1], filename_size);
841 : 0 : (*filename_utf8)[filename_size] = '\0';
842 : : }
843 : :
844 [ + + ]: 14 : if (directory)
845 : : {
846 : : /* Directory last (to be NUL-ed) depends on whether the directory is the root
847 : : * directory (we shouldn't remove the dir separator from the root directory)
848 : : */
849 [ + + ]: 7 : if (p == utf8)
850 : 3 : p[1] = '\0';
851 : : else
852 : 4 : p[0] = '\0';
853 : :
854 : 7 : *directory = pdf_text_new_from_unicode (utf8,
855 : : strlen (utf8),
856 : : PDF_TEXT_UTF8,
857 : : error);
858 [ - + ]: 7 : if (!*directory)
859 : : {
860 [ # # ]: 0 : if (filename)
861 : 0 : pdf_text_destroy (*filename);
862 [ # # ]: 0 : if (filename_utf8)
863 : 0 : pdf_dealloc (*filename_utf8);
864 : 0 : pdf_dealloc (utf8);
865 : 0 : return PDF_FALSE;
866 : : }
867 : : }
868 : :
869 : 14 : pdf_dealloc (utf8);
870 : 16 : return PDF_TRUE;
871 : : }
872 : :
873 : : static pdf_text_t *
874 : 8 : get_basename (const pdf_fsys_t *fsys,
875 : : const pdf_text_t *path,
876 : : pdf_error_t **error)
877 : : {
878 : : pdf_text_t *basename;
879 : :
880 [ - + ]: 8 : PDF_ASSERT_POINTER_RETURN_VAL (path, NULL);
881 : :
882 [ + + ]: 8 : if (!get_directory_and_filename (fsys,
883 : : path,
884 : : NULL,
885 : : &basename,
886 : : NULL,
887 : : error))
888 : : {
889 : 1 : pdf_prefix_error (error, "couldn't get basename: ");
890 : 1 : return NULL;
891 : : }
892 : :
893 : 8 : return basename;
894 : : }
895 : :
896 : : static pdf_text_t *
897 : 8 : get_parent (const pdf_fsys_t *fsys,
898 : : const pdf_text_t *path,
899 : : pdf_error_t **error)
900 : : {
901 : : pdf_text_t *parent;
902 : :
903 [ - + ]: 8 : PDF_ASSERT_POINTER_RETURN_VAL (path, NULL);
904 : :
905 [ + + ]: 8 : if (!get_directory_and_filename (fsys,
906 : : path,
907 : : &parent,
908 : : NULL,
909 : : NULL,
910 : : error))
911 : : {
912 : 1 : pdf_prefix_error (error, "couldn't get parent: ");
913 : 1 : return NULL;
914 : : }
915 : :
916 : 8 : return parent;
917 : : }
918 : :
919 : : /* Host-dependent rmdir() */
920 : : #ifdef PDF_HOST_WIN32
921 : : #define PDF_RMDIR(f) _wrmdir((wchar_t *)f)
922 : : #else
923 : : #define PDF_RMDIR(f) rmdir(f)
924 : : #endif
925 : :
926 : : static pdf_bool_t
927 : 0 : remove_folder (const pdf_fsys_t *fsys,
928 : : const pdf_text_t *path,
929 : : pdf_error_t **error)
930 : : {
931 : : pdf_char_t *host_path;
932 : :
933 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (path, PDF_FALSE);
934 : :
935 : : /* Get a host-encoded version of the path name */
936 : 0 : host_path = get_host_path (path, NULL, error);
937 [ # # ]: 0 : if (!host_path)
938 : 0 : return PDF_FALSE;
939 : :
940 : : /* Try to remove the directory */
941 [ # # ]: 0 : if (PDF_RMDIR (host_path) < 0)
942 : : {
943 : 0 : pdf_set_error (error,
944 : : PDF_EDOMAIN_BASE_FSYS,
945 : : get_status_from_errno (errno),
946 : : "cannot create directory: '%s'",
947 : : strerror (errno));
948 : 0 : pdf_dealloc (host_path);
949 : 0 : return PDF_FALSE;
950 : : }
951 : :
952 : 0 : pdf_dealloc (host_path);
953 : 0 : return PDF_TRUE;
954 : : }
955 : :
956 : : /* Host-dependent access() */
957 : : #ifdef PDF_HOST_WIN32
958 : : #define PDF_ACCESS(f,m) _waccess((wchar_t *)f,m)
959 : : #else
960 : : #define PDF_ACCESS(f,m) access(f,m)
961 : : #endif
962 : :
963 : : static pdf_bool_t
964 : 0 : is_readable_from_host_path (const pdf_char_t *host_path,
965 : : pdf_error_t **error)
966 : : {
967 [ # # ]: 0 : if (PDF_ACCESS (host_path, R_OK) < 0)
968 : : {
969 : : /* Now, either is not readable, or another error happened */
970 [ # # ]: 0 : if (errno != EACCES)
971 : 0 : pdf_set_error (error,
972 : : PDF_EDOMAIN_BASE_FSYS,
973 : : get_status_from_errno (errno),
974 : : "cannot check if file is readable: '%s'",
975 : : strerror (errno));
976 : 0 : return PDF_FALSE;
977 : : }
978 : 0 : return PDF_TRUE;
979 : : }
980 : :
981 : : static pdf_bool_t
982 : 0 : is_writable_from_host_path (const pdf_char_t *host_path,
983 : : pdf_error_t **error)
984 : : {
985 [ # # ]: 0 : if (PDF_ACCESS (host_path, W_OK) < 0)
986 : : {
987 : : /* Now, either is not readable, or another error happened */
988 [ # # ]: 0 : if (errno != EACCES)
989 : 0 : pdf_set_error (error,
990 : : PDF_EDOMAIN_BASE_FSYS,
991 : : get_status_from_errno (errno),
992 : : "cannot check if file is writable: '%s'",
993 : : strerror (errno));
994 : 0 : return PDF_FALSE;
995 : : }
996 : 0 : return PDF_TRUE;
997 : : }
998 : :
999 : : /* Host-dependent stat() */
1000 : : #ifdef PDF_HOST_WIN32
1001 : : #define PDF_STAT(f,s) _wstat((wchar_t *)f,s)
1002 : : typedef struct _stat pdf_stat_s;
1003 : : #else
1004 : : #define PDF_STAT(f,s) stat64(f,s)
1005 : : typedef struct stat64 pdf_stat_s;
1006 : : #endif
1007 : :
1008 : : static pdf_bool_t
1009 : 10 : get_stat_info_from_host_path (const pdf_char_t *host_path,
1010 : : pdf_off_t *size,
1011 : : pdf_time_t *ctime,
1012 : : pdf_time_t *mtime,
1013 : : pdf_error_t **error)
1014 : : {
1015 : : pdf_stat_s file_info;
1016 : :
1017 [ - + ]: 10 : if (PDF_STAT (host_path, &file_info) < 0)
1018 : : {
1019 : 0 : pdf_set_error (error,
1020 : : PDF_EDOMAIN_BASE_FSYS,
1021 : : get_status_from_errno (errno),
1022 : : "cannot get stat info: '%s'",
1023 : : strerror (errno));
1024 : 0 : return PDF_FALSE;
1025 : : }
1026 : :
1027 [ + - ]: 10 : if (size)
1028 : : {
1029 : 10 : *size = (pdf_off_t)file_info.st_size;
1030 : : }
1031 : :
1032 [ - + ]: 10 : if (ctime)
1033 : : {
1034 : 0 : pdf_time_init (ctime);
1035 : 0 : pdf_time_set_utc (ctime, file_info.st_ctime);
1036 : : }
1037 : :
1038 [ - + ]: 10 : if (mtime)
1039 : : {
1040 : 0 : pdf_time_init (mtime);
1041 : 0 : pdf_time_set_utc (mtime, file_info.st_mtime);
1042 : : }
1043 : :
1044 : 10 : return PDF_TRUE;
1045 : : }
1046 : :
1047 : : static pdf_bool_t
1048 : 0 : get_item_props (const pdf_fsys_t *fsys,
1049 : : const pdf_text_t *path,
1050 : : struct pdf_fsys_item_props_s *props,
1051 : : pdf_error_t **error)
1052 : : {
1053 : : pdf_char_t* host_path;
1054 : 0 : pdf_error_t *inner_error = NULL;
1055 : :
1056 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (path, PDF_FALSE);
1057 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (props, PDF_FALSE);
1058 : :
1059 : : /* Get a host-encoded version of the path name */
1060 : 0 : host_path = get_host_path (path, NULL, error);
1061 [ # # ]: 0 : if (!host_path)
1062 : 0 : return PDF_FALSE;
1063 : :
1064 : : /* Initialize the output to all zeros */
1065 : 0 : memset (props, 0, sizeof (struct pdf_fsys_item_props_s));
1066 : :
1067 : : /* Is readable? */
1068 : 0 : props->is_readable = is_readable_from_host_path (host_path, &inner_error);
1069 [ # # ]: 0 : if (inner_error)
1070 : : {
1071 : 0 : pdf_dealloc (host_path);
1072 : 0 : pdf_propagate_error (error, inner_error);
1073 : 0 : return PDF_FALSE;
1074 : : }
1075 : :
1076 : : /* Is writable? */
1077 : 0 : props->is_writable = is_writable_from_host_path (host_path, &inner_error);
1078 [ # # ]: 0 : if (inner_error)
1079 : : {
1080 : 0 : pdf_dealloc (host_path);
1081 : 0 : pdf_propagate_error (error, inner_error);
1082 : 0 : return PDF_FALSE;
1083 : : }
1084 : :
1085 : : /* Get file size, ctime and mtime */
1086 [ # # ]: 0 : if (!get_stat_info_from_host_path (host_path,
1087 : : &props->file_size,
1088 : : &props->creation_date,
1089 : : &props->modification_date,
1090 : : error))
1091 : : {
1092 : 0 : pdf_dealloc (host_path);
1093 : 0 : return PDF_FALSE;
1094 : : }
1095 : :
1096 : : #ifdef PDF_HOST_WIN32
1097 : : {
1098 : : DWORD attrs;
1099 : :
1100 : : attrs = GetFileAttributesW ((LPCWSTR) host_path);
1101 : : if (attrs == INVALID_FILE_ATTRIBUTES)
1102 : : {
1103 : : pdf_set_error (error,
1104 : : PDF_EDOMAIN_BASE_FSYS,
1105 : : PDF_ERROR,
1106 : : "couldn't get item properties: got error code %d",
1107 : : (pdf_i32_t)GetLastError ());
1108 : : pdf_dealloc (host_path);
1109 : : return PDF_FALSE;
1110 : : }
1111 : :
1112 : : props->is_hidden = (attrs & FILE_ATTRIBUTE_HIDDEN ? PDF_TRUE : PDF_FALSE);
1113 : : }
1114 : : #else
1115 : : {
1116 : : pdf_char_t *filename_utf8;
1117 : :
1118 : : /* Get filename */
1119 : 0 : filename_utf8 = NULL;
1120 [ # # ]: 0 : if (!get_directory_and_filename (fsys,
1121 : : path,
1122 : : NULL,
1123 : : NULL,
1124 : : &filename_utf8,
1125 : : error))
1126 : : {
1127 : 0 : pdf_dealloc (host_path);
1128 : 0 : return PDF_FALSE;
1129 : : }
1130 : :
1131 : : /* Lets consider files starting with dot as hidden */
1132 : 0 : props->is_hidden = (filename_utf8[0] == '.' ? PDF_TRUE : PDF_FALSE);
1133 : 0 : pdf_dealloc (filename_utf8);
1134 : : }
1135 : : #endif /* PDF_HOST_WIN32 */
1136 : :
1137 : 0 : pdf_dealloc (host_path);
1138 : :
1139 : 0 : return PDF_TRUE;
1140 : : }
1141 : :
1142 : : static pdf_bool_t
1143 : 0 : item_p (const pdf_fsys_t *fsys,
1144 : : const pdf_text_t *path)
1145 : : {
1146 : : pdf_char_t *host_path;
1147 : : pdf_bool_t exists;
1148 : :
1149 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (path, PDF_FALSE);
1150 : :
1151 : : #ifdef PDF_HOST_WIN32
1152 : : if (win32_device_p (path, NULL))
1153 : : return PDF_TRUE;
1154 : : #endif
1155 : :
1156 : : /* We get the string in HOST-encoding (with NUL-suffix) */
1157 : 0 : host_path = get_host_path (path, NULL, NULL);
1158 [ # # ]: 0 : if (!host_path)
1159 : 0 : return PDF_FALSE;
1160 : :
1161 : : /* Get file size, ctime and mtime, but don't return any.
1162 : : * We just want to know if file exists or not */
1163 : 0 : exists = get_stat_info_from_host_path (host_path,
1164 : : NULL,
1165 : : NULL,
1166 : : NULL,
1167 : : NULL);
1168 : 0 : pdf_dealloc (host_path);
1169 : 0 : return exists;
1170 : : }
1171 : :
1172 : : static pdf_bool_t
1173 : 0 : item_readable_p (const pdf_fsys_t *fsys,
1174 : : const pdf_text_t *path)
1175 : : {
1176 : 0 : pdf_error_t *inner_error = NULL;
1177 : : pdf_char_t* host_path;
1178 : : pdf_bool_t readable;
1179 : :
1180 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (path, PDF_FALSE);
1181 : :
1182 : : /* We get the string in HOST-encoding (with NUL-suffix) */
1183 : 0 : host_path = get_host_path (path, NULL, NULL);
1184 [ # # ]: 0 : if (!host_path)
1185 : 0 : return PDF_FALSE;
1186 : :
1187 : 0 : readable = is_readable_from_host_path (host_path, &inner_error);
1188 [ # # ]: 0 : if (inner_error)
1189 : : {
1190 : 0 : pdf_dealloc (host_path);
1191 : 0 : pdf_error_destroy (inner_error);
1192 : 0 : return PDF_FALSE;
1193 : : }
1194 : :
1195 : 0 : pdf_dealloc (host_path);
1196 : 0 : return readable;
1197 : : }
1198 : :
1199 : : static pdf_bool_t
1200 : 0 : item_writable_p (const pdf_fsys_t *fsys,
1201 : : const pdf_text_t *path)
1202 : : {
1203 : 0 : pdf_error_t *inner_error = NULL;
1204 : : pdf_char_t* host_path;
1205 : : pdf_bool_t writable;
1206 : :
1207 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (path, PDF_FALSE);
1208 : :
1209 : : /* We get the string in HOST-encoding (with NUL-suffix) */
1210 : 0 : host_path = get_host_path (path, NULL, NULL);
1211 [ # # ]: 0 : if (!host_path)
1212 : 0 : return PDF_FALSE;
1213 : :
1214 : 0 : writable = is_writable_from_host_path (host_path, &inner_error);
1215 [ # # ]: 0 : if (inner_error)
1216 : : {
1217 : 0 : pdf_dealloc (host_path);
1218 : 0 : pdf_error_destroy (inner_error);
1219 : 0 : return PDF_FALSE;
1220 : : }
1221 : :
1222 : 0 : pdf_dealloc (host_path);
1223 : 0 : return writable;
1224 : : }
1225 : :
1226 : : #ifdef PDF_HOST_WIN32
1227 : :
1228 : : static pdf_i64_t
1229 : : get_free_space (const pdf_fsys_t *fsys,
1230 : : const pdf_text_t *path,
1231 : : pdf_error_t **error)
1232 : : {
1233 : : pdf_char_t *utf16le_path;
1234 : : pdf_u32_t utf16le_path_size = 0;
1235 : : ULARGE_INTEGER free_bytes;
1236 : :
1237 : : PDF_ASSERT_POINTER_RETURN_VAL (path, -1);
1238 : :
1239 : : /* Note that as we get the string as UTF-16LE with LAST NUL suffix,
1240 : : * it's equivalent to a wchar_t in windows environments */
1241 : : utf16le_path = pdf_text_get_unicode (path,
1242 : : PDF_TEXT_UTF16_LE,
1243 : : PDF_TEXT_UNICODE_WITH_NUL_SUFFIX,
1244 : : &utf16le_path_size,
1245 : : error);
1246 : : if (!utf16le_path)
1247 : : return -1;
1248 : :
1249 : : /* No need to get the drive letter of the specified path:
1250 : : * This parameter does not have to specify the root directory on a disk.
1251 : : * The function accepts any directory on a disk.
1252 : : **/
1253 : :
1254 : : /* Get the information from the filesystem
1255 : : *
1256 : : * BOOL WINAPI GetDiskFreeSpaceEx(
1257 : : * __in_opt LPCTSTR lpDirectoryName,
1258 : : * __out_opt PULARGE_INTEGER lpFreeBytesAvailable,
1259 : : * __out_opt PULARGE_INTEGER lpTotalNumberOfBytes,
1260 : : * __out_opt PULARGE_INTEGER lpTotalNumberOfFreeBytes
1261 : : * );
1262 : : *
1263 : : **/
1264 : : if (!GetDiskFreeSpaceExW ((LPCWSTR) utf16le_path,
1265 : : NULL,
1266 : : NULL,
1267 : : &free_bytes))
1268 : : {
1269 : : /* Propagate error */
1270 : : pdf_set_error (error,
1271 : : PDF_EDOMAIN_BASE_FSYS,
1272 : : get_status_from_errno (errno),
1273 : : "cannot get free disk space: got error code %d",
1274 : : (pdf_i32_t)GetLastError ());
1275 : : pdf_dealloc (utf16le_path);
1276 : : return -1;
1277 : : }
1278 : :
1279 : : pdf_dealloc (utf16le_path);
1280 : :
1281 : : return (pdf_i64_t)free_bytes.QuadPart;
1282 : : }
1283 : :
1284 : : #else
1285 : :
1286 : : static pdf_i64_t
1287 : 2 : get_free_space (const pdf_fsys_t *fsys,
1288 : : const pdf_text_t *path,
1289 : : pdf_error_t **error)
1290 : : {
1291 : : struct statfs fs_stats;
1292 : : pdf_char_t *host_path;
1293 : :
1294 [ - + ]: 2 : PDF_ASSERT_POINTER_RETURN_VAL (path, -1);
1295 : :
1296 : : /* We get the string in HOST-encoding (with NUL-suffix) */
1297 : 2 : host_path = get_host_path (path, NULL, error);
1298 [ - + ]: 2 : if (!host_path)
1299 : 0 : return -1;
1300 : :
1301 : : PDF_DEBUG_BASE ("Getting free bytes of FS at path '%s'...",
1302 : : host_path);
1303 : :
1304 [ + + ]: 2 : if (statfs (host_path, &fs_stats) < 0)
1305 : : {
1306 : 1 : pdf_set_error (error,
1307 : : PDF_EDOMAIN_BASE_FSYS,
1308 : : get_status_from_errno (errno),
1309 : : "cannot get free disk space: %s",
1310 : : strerror (errno));
1311 : 1 : pdf_dealloc (host_path);
1312 : 1 : return -1;
1313 : : }
1314 : :
1315 : 1 : pdf_dealloc (host_path);
1316 : :
1317 : : /* Return the free space in octects */
1318 : 2 : return (pdf_i64_t)(fs_stats.f_bfree * fs_stats.f_bsize);
1319 : : }
1320 : :
1321 : : #endif /* !PDF_HOST_WIN32 */
1322 : :
1323 : : static pdf_bool_t
1324 : 0 : compare_path_p (const pdf_fsys_t *fsys,
1325 : : const pdf_text_t *first,
1326 : : const pdf_text_t *second,
1327 : : pdf_error_t **error)
1328 : : {
1329 : 0 : pdf_error_t *inner_error = NULL;
1330 : : pdf_i32_t ret;
1331 : : pdf_bool_t case_sensitive;
1332 : 0 : pdf_text_t *canonicalized_first = NULL;
1333 : 0 : pdf_text_t *canonicalized_second = NULL;
1334 : :
1335 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (first, PDF_FALSE);
1336 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (second, PDF_FALSE);
1337 : :
1338 : : /* TODO: Mac OS X should have a method in CoreFoundation libs to
1339 : : * check if a the HFS+ filesystem is case-sensitive or not */
1340 : :
1341 : : /* Unix-like systems have case sensitive paths, Windows doesn't. */
1342 : : #ifndef PDF_HOST_WIN32
1343 : 0 : case_sensitive = PDF_TRUE;
1344 : : #else
1345 : : case_sensitive = PDF_FALSE;
1346 : : #endif
1347 : :
1348 : : /* Canonicalize paths before comparing */
1349 [ # # ][ # # ]: 0 : if (!canonicalize_path (fsys,
1350 : : first,
1351 : : &canonicalized_first,
1352 : : NULL,
1353 : : error) ||
1354 : : !canonicalize_path (fsys,
1355 : : second,
1356 : : &canonicalized_second,
1357 : : NULL,
1358 : : error))
1359 : : {
1360 : 0 : pdf_prefix_error (error, "couldn't check if same files: ");
1361 : :
1362 [ # # ]: 0 : if (canonicalized_first)
1363 : 0 : pdf_text_destroy (canonicalized_first);
1364 : :
1365 [ # # ]: 0 : if (canonicalized_second)
1366 : 0 : pdf_text_destroy (canonicalized_second);
1367 : :
1368 : 0 : return PDF_FALSE;
1369 : : }
1370 : :
1371 : : /* Compare canonicalized paths */
1372 : 0 : ret = pdf_text_cmp (canonicalized_first,
1373 : : canonicalized_second,
1374 : : case_sensitive,
1375 : : &inner_error);
1376 : :
1377 : 0 : pdf_text_destroy (canonicalized_first);
1378 : 0 : pdf_text_destroy (canonicalized_second);
1379 : :
1380 [ # # ]: 0 : if (inner_error)
1381 : : {
1382 : 0 : pdf_propagate_error (error, inner_error);
1383 : 0 : pdf_prefix_error (error, "couldn't check if same files: ");
1384 : 0 : return PDF_FALSE;
1385 : : }
1386 : :
1387 : 0 : return (ret == 0 ? PDF_TRUE : PDF_FALSE);
1388 : : }
1389 : :
1390 : : static pdf_text_t *
1391 : 1 : build_path (const pdf_fsys_t *fsys,
1392 : : pdf_error_t **error,
1393 : : const pdf_text_t *first_element,
1394 : : ...)
1395 : : {
1396 : 1 : const struct pdf_fsys_disk_s *disk_fsys = (const struct pdf_fsys_disk_s *)fsys;
1397 : : pdf_text_t *output;
1398 : : pdf_text_t *next;
1399 : : va_list args;
1400 : :
1401 [ - + ]: 1 : PDF_ASSERT_POINTER_RETURN_VAL (first_element, NULL);
1402 : :
1403 : 1 : output = pdf_text_dup (first_element, error);
1404 [ - + ]: 1 : if (!output)
1405 : 0 : return NULL;
1406 : :
1407 : 1 : va_start (args, first_element);
1408 [ + - ]: 1 : next = va_arg (args, pdf_text_t *);
1409 [ + + ]: 3 : while (next != NULL)
1410 : : {
1411 [ + - ][ - + ]: 2 : if (!pdf_text_concat (output,
1412 : 2 : disk_fsys->filename_separator,
1413 : : PDF_TRUE,
1414 : : error) ||
1415 : : !pdf_text_concat (output,
1416 : : next,
1417 : : PDF_TRUE,
1418 : : error))
1419 : : {
1420 : 0 : pdf_text_destroy (output);
1421 : 0 : va_end (args);
1422 : 0 : return NULL;
1423 : : }
1424 [ + - ]: 2 : next = va_arg (args, pdf_text_t *);
1425 : : }
1426 : 1 : va_end (args);
1427 : :
1428 : 1 : return output;
1429 : : }
1430 : :
1431 : : static pdf_char_t *
1432 : 0 : get_url_from_path (const pdf_fsys_t *fsys,
1433 : : const pdf_text_t *path,
1434 : : pdf_error_t **error)
1435 : : {
1436 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (path, NULL);
1437 : :
1438 : : /* TODO. See FS#126 */
1439 : :
1440 : 0 : return NULL;
1441 : : }
1442 : :
1443 : : static pdf_text_t *
1444 : 0 : get_path_from_url (const pdf_fsys_t *fsys,
1445 : : const pdf_char_t *url,
1446 : : pdf_error_t **error)
1447 : : {
1448 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (url, NULL);
1449 : :
1450 : : /* TODO. See FS#126 */
1451 : :
1452 : 0 : return NULL;
1453 : : }
1454 : :
1455 : : /*
1456 : : * File Interface Implementation
1457 : : */
1458 : :
1459 : : static pdf_off_t
1460 : 46 : file_get_pos (const pdf_fsys_file_t *file,
1461 : : pdf_error_t **error)
1462 : : {
1463 : : struct pdf_fsys_disk_file_s *disk_file;
1464 : : pdf_off_t cpos;
1465 : :
1466 [ - + ]: 46 : PDF_ASSERT_POINTER_RETURN_VAL (file, (pdf_off_t)-1);
1467 : :
1468 : 46 : disk_file = (struct pdf_fsys_disk_file_s *)file;
1469 : 46 : cpos = ftello (disk_file->file_descriptor);
1470 [ - + ]: 46 : if (cpos == (pdf_off_t)-1)
1471 : : {
1472 : 0 : pdf_set_error (error,
1473 : : PDF_EDOMAIN_BASE_FSYS,
1474 : : get_status_from_errno (errno),
1475 : : "cannot get position from file: '%s'",
1476 : : strerror (errno));
1477 : 0 : return (pdf_off_t)-1;
1478 : : }
1479 : :
1480 : 46 : return cpos;
1481 : : }
1482 : :
1483 : : static pdf_bool_t
1484 : 22 : file_set_pos (pdf_fsys_file_t *file,
1485 : : pdf_off_t new_pos,
1486 : : pdf_error_t **error)
1487 : : {
1488 : : struct pdf_fsys_disk_file_s *disk_file;
1489 : :
1490 [ - + ]: 22 : PDF_ASSERT_POINTER_RETURN_VAL (file, PDF_FALSE);
1491 [ - + ]: 22 : PDF_ASSERT_RETURN_VAL (new_pos >= 0, PDF_FALSE);
1492 : :
1493 : 22 : disk_file = (struct pdf_fsys_disk_file_s *)file;
1494 [ - + ]: 22 : if (fseeko (disk_file->file_descriptor,
1495 : : new_pos,
1496 : : SEEK_SET) == (pdf_off_t)-1)
1497 : : {
1498 : 0 : pdf_set_error (error,
1499 : : PDF_EDOMAIN_BASE_FSYS,
1500 : : get_status_from_errno (errno),
1501 : : "cannot set position in file: '%s'",
1502 : : strerror (errno));
1503 : 0 : return PDF_FALSE;
1504 : : }
1505 : :
1506 : 22 : return PDF_TRUE;
1507 : : }
1508 : :
1509 : : static pdf_bool_t
1510 : 0 : file_can_set_size_p (const pdf_fsys_file_t *file,
1511 : : pdf_off_t size)
1512 : : {
1513 : : struct pdf_fsys_disk_file_s *disk_file;
1514 : :
1515 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (file, PDF_FALSE);
1516 : :
1517 : : /* We'll assume that we can set any new size of the file if it is writable */
1518 : 0 : disk_file = (struct pdf_fsys_disk_file_s *)file;
1519 : 0 : return is_writable_from_host_path (disk_file->host_path, NULL);
1520 : : }
1521 : :
1522 : : static pdf_off_t
1523 : 10 : file_get_size (const pdf_fsys_file_t *file,
1524 : : pdf_error_t **error)
1525 : : {
1526 : : struct pdf_fsys_disk_file_s *disk_file;
1527 : : pdf_off_t size;
1528 : :
1529 [ - + ]: 10 : PDF_ASSERT_POINTER_RETURN_VAL (file, (pdf_off_t)-1);
1530 : :
1531 : 10 : disk_file = (struct pdf_fsys_disk_file_s *)file;
1532 [ + - ]: 10 : return (get_stat_info_from_host_path (disk_file->host_path,
1533 : : &size,
1534 : : NULL,
1535 : : NULL,
1536 : : error) ?
1537 : : size :
1538 : : (pdf_off_t)-1);
1539 : : }
1540 : :
1541 : : static pdf_bool_t
1542 : 0 : file_set_size (pdf_fsys_file_t *file,
1543 : : pdf_off_t size,
1544 : : pdf_error_t **error)
1545 : : {
1546 : 0 : struct pdf_fsys_disk_file_s *disk_file = (struct pdf_fsys_disk_file_s *)file;
1547 : :
1548 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (file, PDF_FALSE);
1549 [ # # ]: 0 : PDF_ASSERT_RETURN_VAL (disk_file->file_descriptor > 0, PDF_FALSE);
1550 : :
1551 : : /* Setting size of the file is just seeking to the given size offset and
1552 : : * writing an EOF... isn't it? */
1553 [ # # ][ # # ]: 0 : if (!file_set_pos (file, (size > 0 ? size - 1 : 0), error))
1554 : : {
1555 : 0 : pdf_prefix_error (error, "cannot set size: ");
1556 : 0 : return PDF_FALSE;
1557 : : }
1558 : :
1559 : : /* putc() returns exactly the same character written, or EOF on error,
1560 : : * so if we try to write EOF we should indeed get EOF */
1561 : 0 : putc (EOF, disk_file->file_descriptor);
1562 : :
1563 : 0 : return PDF_TRUE;
1564 : : }
1565 : :
1566 : : static pdf_bool_t
1567 : 46 : file_read (pdf_fsys_file_t *file,
1568 : : pdf_char_t *buf,
1569 : : pdf_size_t bytes,
1570 : : pdf_size_t *read_bytes,
1571 : : pdf_error_t **error)
1572 : : {
1573 : 46 : struct pdf_fsys_disk_file_s *disk_file = (struct pdf_fsys_disk_file_s *)file;
1574 : :
1575 [ - + ]: 46 : PDF_ASSERT_POINTER_RETURN_VAL (file, PDF_FALSE);
1576 [ - + ]: 46 : PDF_ASSERT_POINTER_RETURN_VAL (buf, PDF_FALSE);
1577 [ - + ]: 46 : PDF_ASSERT_POINTER_RETURN_VAL (read_bytes, PDF_FALSE);
1578 [ - + ]: 46 : PDF_ASSERT_RETURN_VAL (disk_file->file_descriptor > 0, PDF_FALSE);
1579 : :
1580 : 46 : *read_bytes = fread (buf,
1581 : : 1,
1582 : : bytes,
1583 : : disk_file->file_descriptor);
1584 : :
1585 : : /* On EOF, PDF_FALSE without error */
1586 [ + + ]: 46 : if (feof (disk_file->file_descriptor))
1587 : 44 : return PDF_FALSE;
1588 : :
1589 [ - + ]: 2 : if (ferror (disk_file->file_descriptor))
1590 : : {
1591 : 0 : pdf_set_error (error,
1592 : : PDF_EDOMAIN_BASE_FSYS,
1593 : : get_status_from_errno (errno),
1594 : : "cannot read from file: '%s'",
1595 : : strerror (errno));
1596 : 0 : return PDF_FALSE;
1597 : : }
1598 : :
1599 : 46 : return PDF_TRUE;
1600 : : }
1601 : :
1602 : : static pdf_bool_t
1603 : 10 : file_write (pdf_fsys_file_t *file,
1604 : : const pdf_char_t *buf,
1605 : : pdf_size_t bytes,
1606 : : pdf_size_t *written_bytes,
1607 : : pdf_error_t **error)
1608 : : {
1609 : 10 : struct pdf_fsys_disk_file_s *disk_file = (struct pdf_fsys_disk_file_s *)file;
1610 : :
1611 [ - + ]: 10 : PDF_ASSERT_POINTER_RETURN_VAL (file, PDF_FALSE);
1612 [ - + ]: 10 : PDF_ASSERT_POINTER_RETURN_VAL (buf, PDF_FALSE);
1613 [ - + ]: 10 : PDF_ASSERT_POINTER_RETURN_VAL (written_bytes, PDF_FALSE);
1614 [ - + ]: 10 : PDF_ASSERT_RETURN_VAL (disk_file->file_descriptor > 0, PDF_FALSE);
1615 : :
1616 : 10 : *written_bytes = fwrite (buf,
1617 : : 1,
1618 : : bytes,
1619 : : disk_file->file_descriptor);
1620 : :
1621 [ - + ]: 10 : if (ferror (disk_file->file_descriptor))
1622 : : {
1623 : 0 : pdf_set_error (error,
1624 : : PDF_EDOMAIN_BASE_FSYS,
1625 : : get_status_from_errno (errno),
1626 : : "cannot write to file: '%s'",
1627 : : strerror (errno));
1628 : 0 : return PDF_FALSE;
1629 : : }
1630 : :
1631 : 10 : return PDF_TRUE;
1632 : : }
1633 : :
1634 : : static pdf_bool_t
1635 : 0 : file_flush (pdf_fsys_file_t *file,
1636 : : pdf_error_t **error)
1637 : : {
1638 : 0 : struct pdf_fsys_disk_file_s *disk_file = (struct pdf_fsys_disk_file_s *)file;
1639 : :
1640 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (file, PDF_FALSE);
1641 [ # # ]: 0 : PDF_ASSERT_RETURN_VAL (disk_file->file_descriptor > 0, PDF_FALSE);
1642 : :
1643 [ # # ]: 0 : if (fflush (disk_file->file_descriptor) < 0)
1644 : : {
1645 : : /* On Windows platforms (excluding Cygwin), fflush does not
1646 : : * set errno upon failure. */
1647 : : #ifndef PDF_HOST_WIN32
1648 : 0 : pdf_set_error (error,
1649 : : PDF_EDOMAIN_BASE_FSYS,
1650 : : get_status_from_errno (errno),
1651 : : "cannot flush to file: '%s'",
1652 : : strerror (errno));
1653 : : #else
1654 : : pdf_set_error (error,
1655 : : PDF_EDOMAIN_BASE_FSYS,
1656 : : PDF_ERROR,
1657 : : "cannot flush to file: 'unknown error'",
1658 : : strerror (errno));
1659 : : #endif
1660 : 0 : return PDF_FALSE;
1661 : : }
1662 : :
1663 : 0 : return PDF_TRUE;
1664 : : }
1665 : :
1666 : : static pdf_u32_t
1667 : 0 : file_request_ria (pdf_fsys_file_t *file,
1668 : : pdf_off_t offset,
1669 : : pdf_size_t count,
1670 : : pdf_error_t **error)
1671 : : {
1672 : 0 : pdf_set_error (error,
1673 : : PDF_EDOMAIN_BASE_FSYS,
1674 : : PDF_EBADDATA,
1675 : : "can't request RIA: not provided by the disk filesystem");
1676 : 0 : return 0;
1677 : : }
1678 : :
1679 : : static pdf_bool_t
1680 : 0 : file_has_ria (pdf_fsys_file_t *file)
1681 : : {
1682 : : /* We can just safely return FALSE here, without error:
1683 : : * the file has definitely not an ongoing RIA operation */
1684 : 0 : return PDF_FALSE;
1685 : : }
1686 : :
1687 : : static pdf_bool_t
1688 : 0 : file_cancel_ria (pdf_fsys_file_t *file,
1689 : : pdf_u32_t ria_id,
1690 : : pdf_error_t **error)
1691 : : {
1692 : : /* We can just safely return FALSE here, without error:
1693 : : * the file has definitely not an ongoing RIA operation, so
1694 : : * canceling RIA always succeeds. */
1695 : 0 : return PDF_TRUE;
1696 : : }
1697 : :
1698 : :
1699 : : /* Host-dependent freopen() */
1700 : : #ifdef PDF_HOST_WIN32
1701 : : #define PDF_FREOPEN(f,m,s) _wfreopen((wchar_t *)f,(wchar_t *)m,s)
1702 : : #else
1703 : : #define PDF_FREOPEN(f,m,s) freopen_safer (f,m,s)
1704 : : #endif
1705 : :
1706 : : static pdf_bool_t
1707 : 0 : file_reopen (pdf_fsys_file_t *file,
1708 : : enum pdf_fsys_file_mode_e mode,
1709 : : pdf_error_t **error)
1710 : : {
1711 : : struct pdf_fsys_disk_file_s *disk_file;
1712 : : FILE *reopened;
1713 : :
1714 : 0 : disk_file = (struct pdf_fsys_disk_file_s *)file;
1715 : :
1716 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (file, PDF_FALSE);
1717 [ # # ]: 0 : PDF_ASSERT_RETURN_VAL (disk_file->file_descriptor > 0, PDF_FALSE);
1718 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (disk_file->host_path, PDF_FALSE);
1719 : :
1720 : : /* re-open the file */
1721 : 0 : reopened = PDF_FREOPEN (disk_file->host_path,
1722 : : get_mode_string (mode),
1723 : : disk_file->file_descriptor);
1724 [ # # ]: 0 : if (!reopened)
1725 : : {
1726 : 0 : pdf_set_error (error,
1727 : : PDF_EDOMAIN_BASE_FSYS,
1728 : : get_status_from_errno (errno),
1729 : : "cannot reopen file: '%s'",
1730 : : strerror (errno));
1731 : 0 : return PDF_FALSE;
1732 : : }
1733 : :
1734 : 0 : disk_file->file_descriptor = reopened;
1735 : 0 : file->mode = mode;
1736 : :
1737 : 0 : return PDF_TRUE;
1738 : : }
1739 : :
1740 : : /*
1741 : : * Private functions
1742 : : */
1743 : :
1744 : : static pdf_char_t *
1745 : 27 : get_host_path (const pdf_text_t *path,
1746 : : pdf_size_t *host_path_size,
1747 : : pdf_error_t **error)
1748 : : {
1749 : : #ifdef PDF_HOST_WIN32
1750 : : /* For W32, we will always use widechar functions, so Windows' wchar_t
1751 : : * implementation should be used (UTF-16LE) */
1752 : : return pdf_text_get_unicode (path,
1753 : : PDF_TEXT_UTF16_LE,
1754 : : PDF_TEXT_UNICODE_WITH_NUL_SUFFIX,
1755 : : host_path_size,
1756 : : error);
1757 : : #else
1758 : : /* Call the pdf_text module to get a host-encoded version of the
1759 : : * given path */
1760 : : pdf_char_t *str;
1761 : : pdf_char_t *nul_suffixed;
1762 : 27 : pdf_size_t length = 0;
1763 : :
1764 : 27 : str = pdf_text_get_host (path,
1765 : : pdf_text_get_host_encoding (),
1766 : : &length,
1767 : : error);
1768 [ - + ]: 27 : if (!str)
1769 : 0 : return NULL;
1770 : :
1771 : : /* Realloc to append last NUL byte */
1772 : 27 : length++;
1773 : 27 : nul_suffixed = pdf_realloc (str, length);
1774 [ - + ]: 27 : if (!nul_suffixed)
1775 : : {
1776 : 0 : pdf_dealloc (str);
1777 : 0 : pdf_set_error (error,
1778 : : PDF_EDOMAIN_BASE_FSYS,
1779 : : PDF_ENOMEM,
1780 : : "cannot get text contents host encoded: "
1781 : : "couldn't reallocate %lu bytes",
1782 : : (unsigned long)length);
1783 : 0 : return NULL;
1784 : : }
1785 : 27 : nul_suffixed[length - 1] = '\0';
1786 : :
1787 [ + + ]: 27 : if (host_path_size)
1788 : 25 : *host_path_size = length;
1789 : :
1790 : 27 : return nul_suffixed;
1791 : : #endif
1792 : : }
1793 : :
1794 : : static pdf_text_t *
1795 : : set_host_path (const pdf_char_t *host_path,
1796 : : const pdf_size_t host_path_size,
1797 : : pdf_error_t **error)
1798 : : {
1799 : : #ifdef PDF_HOST_WIN32
1800 : : /* For W32, we will always use widechar functions, so Windows' wchar_t
1801 : : * implementation should be used (UTF-16LE) */
1802 : : return pdf_text_new_from_unicode (host_path,
1803 : : host_path_size,
1804 : : PDF_TEXT_UTF16_LE,
1805 : : error);
1806 : : #else
1807 : : /* Call the pdf_text module to get a host-encoded version of the
1808 : : * given path */
1809 : 0 : return pdf_text_new_from_host (host_path,
1810 : : host_path_size,
1811 : : pdf_text_get_host_encoding (),
1812 : : error);
1813 : : #endif
1814 : : }
1815 : :
1816 : : #ifndef PDF_HOST_WIN32
1817 : : /* Posix-based open mode */
1818 : : static const pdf_char_t *open_mode_strings[PDF_FSYS_OPEN_MODE_MAX] = {
1819 : : /* PDF_FSYS_OPEN_MODE_INVALID */ (pdf_char_t *)"",
1820 : : /* PDF_FSYS_OPEN_MODE_READ */ (pdf_char_t *)"r",
1821 : : /* PDF_FSYS_OPEN_MODE_WRITE */ (pdf_char_t *)"w",
1822 : : /* PDF_FSYS_OPEN_MODE_RW */ (pdf_char_t *)"r+",
1823 : : };
1824 : : #else
1825 : : /* Windows portability note:
1826 : : *
1827 : : * Files are opened in "text mode" (with crlf translation) by
1828 : : * default.
1829 : : *
1830 : : * Although the "b" fopen option is supported by POSIX some old Unix
1831 : : * systems may not implement it, so we should use that option to
1832 : : * open files only while running in Windows.
1833 : : *
1834 : : * Also, note that the open modes are in UTF-16 for w32
1835 : : */
1836 : : static const pdf_char_t *open_mode_strings[PDF_FSYS_OPEN_MODE_MAX] = {
1837 : : /* PDF_FSYS_OPEN_MODE_INVALID */ (pdf_char_t *)"\x00\x00",
1838 : : /* PDF_FSYS_OPEN_MODE_READ */ (pdf_char_t *)"\x72\x00\x62\x00\x00\x00",
1839 : : /* PDF_FSYS_OPEN_MODE_WRITE */ (pdf_char_t *)"\x77\x00\x62\x00\x00\x00",
1840 : : /* PDF_FSYS_OPEN_MODE_RW */ (pdf_char_t *)"\x72\x00\x62\x00\x2B\x00\x00\x00",
1841 : : };
1842 : : #endif
1843 : :
1844 : : static const pdf_char_t *
1845 : : get_mode_string (const enum pdf_fsys_file_mode_e mode)
1846 : : {
1847 : 25 : return open_mode_strings[mode];
1848 : : }
1849 : :
1850 : : #ifdef PDF_HOST_WIN32
1851 : :
1852 : : #define PDF_MAX_W32_DEVICE_NAMES 32
1853 : : static const pdf_char_t *device_names[PDF_MAX_W32_DEVICE_NAMES] = {
1854 : : (pdf_char_t *)"NUL",
1855 : : (pdf_char_t *)"CON",
1856 : : (pdf_char_t *)"PRN",
1857 : : (pdf_char_t *)"AUX",
1858 : : (pdf_char_t *)"COM1",
1859 : : (pdf_char_t *)"COM2",
1860 : : (pdf_char_t *)"COM3",
1861 : : (pdf_char_t *)"COM4",
1862 : : (pdf_char_t *)"COM5",
1863 : : (pdf_char_t *)"COM6",
1864 : : (pdf_char_t *)"COM7",
1865 : : (pdf_char_t *)"COM8",
1866 : : (pdf_char_t *)"COM9",
1867 : : (pdf_char_t *)"LPT1",
1868 : : (pdf_char_t *)"LPT2",
1869 : : (pdf_char_t *)"LPT3",
1870 : : (pdf_char_t *)"LPT4",
1871 : : (pdf_char_t *)"LPT5",
1872 : : (pdf_char_t *)"LPT6",
1873 : : (pdf_char_t *)"LPT7",
1874 : : (pdf_char_t *)"LPT8",
1875 : : (pdf_char_t *)"LPT9"
1876 : : };
1877 : :
1878 : : static pdf_bool_t
1879 : : win32_device_p (const pdf_text_t *path,
1880 : : pdf_error_t **error)
1881 : : {
1882 : : /* The following special "files", which access devices, exist in all
1883 : : directories, case-insensitively, and with all possible endings
1884 : : after a period or colon, excpets in pathnames that start with
1885 : : \\?\: */
1886 : : int i;
1887 : : pdf_text_t *device_name;
1888 : :
1889 : : for (i = 0; i < PDF_MAX_W32_DEVICE_NAMES; ++i)
1890 : : {
1891 : : pdf_error_t *inner_error = NULL;
1892 : :
1893 : : device_name = pdf_text_new_from_unicode (device_names[i],
1894 : : strlen (device_names[i]),
1895 : : PDF_TEXT_UTF8,
1896 : : error);
1897 : : if (!device_name)
1898 : : return PDF_FALSE;
1899 : :
1900 : : if (pdf_text_cmp (path,
1901 : : device_name,
1902 : : PDF_FALSE,
1903 : : &inner_error) == 0)
1904 : : {
1905 : : pdf_text_destroy (device_name);
1906 : : return PDF_TRUE;
1907 : : }
1908 : :
1909 : : pdf_text_destroy (device_name);
1910 : :
1911 : : if (inner_error)
1912 : : {
1913 : : pdf_propagate_error (error, inner_error);
1914 : : return PDF_FALSE;
1915 : : }
1916 : : }
1917 : :
1918 : : return PDF_FALSE;
1919 : : }
1920 : :
1921 : : #endif /* !PDF_HOST_WIN32 */
1922 : :
1923 : : static pdf_status_t
1924 : 3 : get_status_from_errno (int _errno)
1925 : : {
1926 [ - + + - - : 3 : switch (_errno)
- - - - ]
1927 : : {
1928 : : case EINVAL:
1929 : : case ESPIPE:
1930 : : case EOVERFLOW:
1931 : : case EBADF:
1932 : : case EFAULT:
1933 : : case EFBIG:
1934 : : /* Bad data */
1935 : 0 : return PDF_EBADDATA;
1936 : : case EACCES:
1937 : : case EPERM:
1938 : : case EROFS:
1939 : : case ETXTBSY:
1940 : : /* Not enough permissions */
1941 : 1 : return PDF_EBADPERMS;
1942 : : case EISDIR:
1943 : : case ENAMETOOLONG:
1944 : : case ENOENT:
1945 : : case ENOTDIR:
1946 : : #ifdef ELOOP
1947 : : case ELOOP:
1948 : : #endif
1949 : : /* Invalid path name */
1950 : 2 : return PDF_EBADNAME;
1951 : : case ENOMEM:
1952 : : /* No memory */
1953 : 0 : return PDF_ENOMEM;
1954 : : case EEXIST:
1955 : : /* File Exists */
1956 : 0 : return PDF_EEXIST;
1957 : : case ENOTEMPTY:
1958 : : /* Not empty */
1959 : 0 : return PDF_ENOTEMPTY;
1960 : : case EAGAIN:
1961 : : /* non-blocking descriptor and blocking writing
1962 : : requested */
1963 : 0 : return PDF_EAGAIN;
1964 : : case ENOSPC:
1965 : : /* Not enough room in disk */
1966 : 0 : return PDF_ENOSPC;
1967 : : default:
1968 : : /* Other error */
1969 : 3 : return PDF_ERROR;
1970 : : }
1971 : : }
1972 : :
1973 : : /* Setup the disk filesystem implementation, using named initializers */
1974 : : static struct pdf_fsys_disk_s pdf_fsys_disk_implementation =
1975 : : {
1976 : : .common.get_free_space_fn = get_free_space,
1977 : : .common.create_folder_fn = create_folder,
1978 : : .common.get_folder_contents_fn = get_folder_contents,
1979 : : .common.remove_folder_fn = remove_folder,
1980 : : .common.get_parent_fn = get_parent,
1981 : : .common.get_basename_fn = get_basename,
1982 : : .common.compare_path_p_fn = compare_path_p,
1983 : : .common.build_path_fn = build_path,
1984 : : .common.get_url_from_path_fn = get_url_from_path,
1985 : : .common.get_path_from_url_fn = get_path_from_url,
1986 : : .common.item_p_fn = item_p,
1987 : : .common.get_item_props_fn = get_item_props,
1988 : : .common.item_readable_p_fn = item_readable_p,
1989 : : .common.item_writable_p_fn = item_writable_p,
1990 : : .common.file_open_fn = file_open,
1991 : : .common.file_open_tmp_fn = file_open_tmp,
1992 : : .common.file_reopen_fn = file_reopen,
1993 : : .common.file_close_fn = file_close,
1994 : : .common.file_get_size_fn = file_get_size,
1995 : : .common.file_can_set_size_p_fn = file_can_set_size_p,
1996 : : .common.file_set_size_fn = file_set_size,
1997 : : .common.file_get_pos_fn = file_get_pos,
1998 : : .common.file_set_pos_fn = file_set_pos,
1999 : : .common.file_read_fn = file_read,
2000 : : .common.file_write_fn = file_write,
2001 : : .common.file_flush_fn = file_flush,
2002 : : .common.file_request_ria_fn = file_request_ria,
2003 : : .common.file_has_ria_fn = file_has_ria,
2004 : : .common.file_cancel_ria_fn = file_cancel_ria,
2005 : : };
2006 : :
2007 : : static void
2008 : 745 : pdf_fsys_disk_deinit_cb (struct pdf_fsys_s *impl)
2009 : : {
2010 : 745 : struct pdf_fsys_disk_s *disk_fsys = (struct pdf_fsys_disk_s *)impl;
2011 : :
2012 [ + - ]: 745 : if (disk_fsys->filename_separator)
2013 : : {
2014 : 745 : pdf_text_destroy (disk_fsys->filename_separator);
2015 : 745 : disk_fsys->filename_separator = NULL;
2016 : : }
2017 : 745 : }
2018 : :
2019 : : pdf_bool_t
2020 : 745 : pdf_fsys_disk_init (pdf_error_t **error)
2021 : : {
2022 : : /* Disk filesystem implementation specific stuff */
2023 : 745 : pdf_fsys_disk_implementation.filename_separator =
2024 : 745 : pdf_text_new_from_unicode ((pdf_char_t*)DIR_SEPARATOR_S,
2025 : : 1,
2026 : : PDF_TEXT_UTF8,
2027 : : error);
2028 [ - + ]: 745 : if (!pdf_fsys_disk_implementation.filename_separator)
2029 : : {
2030 : 0 : pdf_prefix_error (error, "couldn't initialize disk filesystem: ");
2031 : 0 : return PDF_FALSE;
2032 : : }
2033 : :
2034 : 745 : return pdf_fsys_add (PDF_FSYS_DISK_ID,
2035 : : (struct pdf_fsys_s *)&pdf_fsys_disk_implementation,
2036 : : pdf_fsys_disk_deinit_cb,
2037 : : error);
2038 : : }
2039 : :
2040 : : void
2041 : 745 : pdf_fsys_disk_deinit (void)
2042 : : {
2043 : 745 : pdf_fsys_remove (PDF_FSYS_DISK_ID);
2044 : 745 : }
2045 : :
2046 : : /* End of pdf-fsys-disk.c */
|