/src/ghostpdl/base/gp_unifs.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* "Unix-like" file system platform routines for Ghostscript */ |
18 | | |
19 | | /* prevent gp.h from defining fopen */ |
20 | | #define fopen fopen |
21 | | |
22 | | #include "stdio_.h" /* for FILENAME_MAX */ |
23 | | #include "memory_.h" |
24 | | #include "string_.h" |
25 | | #include "gx.h" |
26 | | |
27 | | #include "gp.h" |
28 | | #include "gpmisc.h" |
29 | | #include "gsstruct.h" |
30 | | #include "gsutil.h" /* for string_match */ |
31 | | #include "stat_.h" |
32 | | #include "dirent_.h" |
33 | | #include "unistd_.h" |
34 | | #include <stdlib.h> /* for mkstemp/mktemp */ |
35 | | |
36 | | #if !defined(HAVE_FSEEKO) |
37 | | #define ftello ftell |
38 | | #define fseeko fseek |
39 | | #define ftello64 ftell |
40 | | #define fseeko64 fseek |
41 | | #endif |
42 | | |
43 | | /* Provide a definition of the maximum path length in case the system |
44 | | * headers don't define it. This should be gp_file_name_sizeof from |
45 | | * gp.h once that value is properly sent in a system-dependent way. |
46 | | * HP-UX 11i 11.11 incorrectly defines FILENAME_MAX as 14. |
47 | | */ |
48 | | #ifdef FILENAME_MAX |
49 | | # if FILENAME_MAX < 80 /* arbitrary */ |
50 | | # undef FILENAME_MAX |
51 | | # endif |
52 | | #endif |
53 | | #ifndef FILENAME_MAX |
54 | | # define FILENAME_MAX 1024 |
55 | | #endif |
56 | | |
57 | | /* Library routines not declared in a standard header */ |
58 | | extern char *mktemp(char *); |
59 | | |
60 | | /* ------ File naming and accessing ------ */ |
61 | | |
62 | | /* Define the default scratch file name prefix. */ |
63 | | const char gp_scratch_file_name_prefix[] = "gs_"; |
64 | | |
65 | | /* Define the name of the null output file. */ |
66 | | const char gp_null_file_name[] = "/dev/null"; |
67 | | |
68 | | /* Define the name that designates the current directory. */ |
69 | | const char gp_current_directory_name[] = "."; |
70 | | |
71 | | /* Create and open a scratch file with a given name prefix. */ |
72 | | /* Write the actual file name at fname. */ |
73 | | FILE * |
74 | | gp_open_scratch_file_impl(const gs_memory_t *mem, |
75 | | const char *prefix, |
76 | | char fname[gp_file_name_sizeof], |
77 | | const char *mode, |
78 | | int remove) |
79 | 287k | { /* The -8 is for XXXXXX plus a possible final / and -. */ |
80 | | #ifdef GS_NO_FILESYSTEM |
81 | | return NULL; |
82 | | #else |
83 | 287k | int prefix_length = strlen(prefix); |
84 | 287k | int len = gp_file_name_sizeof - prefix_length - 8; |
85 | 287k | FILE *fp; |
86 | | |
87 | 287k | if (gp_file_name_is_absolute(prefix, prefix_length)) |
88 | 0 | *fname = 0; |
89 | 287k | else if (gp_gettmpdir(fname, &len) != 0) |
90 | 287k | strcpy(fname, "/tmp/"); |
91 | 0 | else { |
92 | 0 | if (strlen(fname) != 0 && fname[strlen(fname) - 1] != '/') |
93 | 0 | strcat(fname, "/"); |
94 | 0 | } |
95 | 287k | if (strlen(fname) + prefix_length + 8 >= gp_file_name_sizeof) |
96 | 0 | return 0; /* file name too long */ |
97 | 287k | strcat(fname, prefix); |
98 | | /* Prevent trailing X's in path from being converted by mktemp. */ |
99 | 287k | if (*fname != 0 && fname[strlen(fname) - 1] == 'X') |
100 | 0 | strcat(fname, "-"); |
101 | 287k | strcat(fname, "XXXXXX"); |
102 | | |
103 | 287k | #ifdef HAVE_MKSTEMP |
104 | 287k | { |
105 | 287k | int file; |
106 | 287k | char ofname[gp_file_name_sizeof]; |
107 | | |
108 | | /* save the old filename template in case mkstemp fails */ |
109 | 287k | memcpy(ofname, fname, gp_file_name_sizeof); |
110 | 287k | # ifdef HAVE_MKSTEMP64 |
111 | 287k | file = mkstemp64(fname); |
112 | | # else |
113 | | file = mkstemp(fname); |
114 | | # endif |
115 | 287k | if (file < 0) { |
116 | 0 | emprintf1(mem, "**** Could not open temporary file %s\n", ofname); |
117 | 0 | return NULL; |
118 | 0 | } |
119 | | # if defined(O_LARGEFILE) && defined(__hpux) |
120 | | fcntl(file, F_SETFD, fcntl(file, F_GETFD) | O_LARGEFILE); |
121 | | # else |
122 | | /* Fixme : what to do with b64 and 32-bit mkstemp? Unimplemented. */ |
123 | 287k | # endif |
124 | | |
125 | 287k | fp = fdopen(file, mode); |
126 | 287k | if (fp == NULL) { |
127 | 0 | close(file); |
128 | 0 | } |
129 | 287k | } |
130 | | #else |
131 | | /* Coverity thinks that any use of mktemp() is insecure. But if we |
132 | | reach here then there is no mkstemp() alternative available, so there's |
133 | | not much we can do. Haven't been able to disable this - e.g. '// |
134 | | coverity[SECURE_TEMP]' doesn't have any affect. */ |
135 | | mktemp(fname); |
136 | | fp = gp_fopentemp(fname, mode); |
137 | | #endif |
138 | 287k | if (fp == NULL) |
139 | 0 | emprintf1(mem, "**** Could not open temporary file %s\n", fname); |
140 | | |
141 | 287k | if (remove) |
142 | 72.8k | unlink(fname); /* unlink, not gp_unlink here. */ |
143 | | |
144 | 287k | return fp; |
145 | 287k | #endif |
146 | 287k | } |
147 | | |
148 | | /* Open a file with the given name, as a stream of uninterpreted bytes. */ |
149 | | FILE * |
150 | | gp_fopen_impl(gs_memory_t *mem, const char *fname, const char *mode) |
151 | 74.4M | { |
152 | 74.4M | #if defined(HAVE_FILE64) |
153 | 74.4M | return fopen64(fname, mode); |
154 | | #else |
155 | | return fopen(fname, mode); |
156 | | #endif |
157 | 74.4M | } |
158 | | |
159 | | int |
160 | | gp_unlink_impl(gs_memory_t *mem, const char *fname) |
161 | 438k | { |
162 | 438k | return unlink(fname); |
163 | 438k | } |
164 | | |
165 | | int |
166 | | gp_rename_impl(gs_memory_t *mem, const char *from, const char *to) |
167 | 0 | { |
168 | 0 | return rename(from, to); |
169 | 0 | } |
170 | | |
171 | | int gp_stat_impl(const gs_memory_t *mem, const char *path, struct stat *buf) |
172 | 0 | { |
173 | 0 | return stat(path, buf); |
174 | 0 | } |
175 | | |
176 | | int gp_can_share_fdesc(void) |
177 | 1.80G | { |
178 | 1.80G | #if defined(HAVE_PREAD_PWRITE) && HAVE_PREAD_PWRITE == 1 |
179 | 1.80G | return 1; |
180 | | #else |
181 | | return 0; /* can't share FILE * descriptors w/o pread due to seek..read..seek */ |
182 | | #endif |
183 | 1.80G | } |
184 | | |
185 | | FILE *gp_fdup_impl(FILE *f, const char *mode) |
186 | 0 | { |
187 | | #ifdef GS_NO_FILESYSTEM |
188 | | return NULL; |
189 | | #else |
190 | 0 | int fd = fileno(f); |
191 | 0 | if (fd < 0) |
192 | 0 | return NULL; |
193 | 0 | fd = dup(fd); |
194 | 0 | if (fd < 0) |
195 | 0 | return NULL; |
196 | 0 | return fdopen(fd, mode); |
197 | 0 | #endif |
198 | 0 | } |
199 | | |
200 | | int gp_pread_impl(char *buf, size_t count, gs_offset_t offset, FILE *f) |
201 | 7.19M | { |
202 | | #ifdef GS_NO_FILESYSTEM |
203 | | return 0; |
204 | | #elif defined(HAVE_PREAD_PWRITE) && HAVE_PREAD_PWRITE == 1 |
205 | | return pread(fileno(f), buf, count, offset); |
206 | | #else |
207 | | int c; |
208 | | int64_t os, curroff = gp_ftell_impl(f); |
209 | | if (curroff < 0) return curroff; |
210 | | |
211 | | os = gp_fseek_impl(f, offset, 0); |
212 | | if (os < 0) return os; |
213 | | |
214 | | c = fread(buf, 1, count, f); |
215 | | if (c < 0) return c; |
216 | | |
217 | | os = gp_fseek_impl(f, curroff, 0); |
218 | | if (os < 0) return os; |
219 | | |
220 | | return c; |
221 | | #endif |
222 | 7.19M | } |
223 | | |
224 | | int gp_pwrite_impl(const char *buf, size_t count, gs_offset_t offset, FILE *f) |
225 | 42.3M | { |
226 | | #ifdef GS_NO_FILESYSTEM |
227 | | return 0; |
228 | | #elif defined(HAVE_PREAD_PWRITE) && HAVE_PREAD_PWRITE == 1 |
229 | | return pwrite(fileno(f), buf, count, offset); |
230 | | #else |
231 | | int c; |
232 | | int64_t os, curroff = gp_ftell_impl(f); |
233 | | if (curroff < 0) return curroff; |
234 | | |
235 | | os = gp_fseek_impl(f, offset, 0); |
236 | | if (os < 0) return os; |
237 | | |
238 | | c = fwrite(buf, 1, count, f); |
239 | | if (c < 0) return c; |
240 | | |
241 | | os = gp_fseek_impl(f, curroff, 0); |
242 | | if (os < 0) return os; |
243 | | |
244 | | return c; |
245 | | #endif |
246 | 42.3M | } |
247 | | |
248 | | /* Set a file into binary or text mode. */ |
249 | | int |
250 | | gp_setmode_binary_impl(FILE * pfile, bool mode) /* lgtm [cpp/useless-expression] */ |
251 | 72.2k | { |
252 | 72.2k | return 0; /* Noop under Unix */ |
253 | 72.2k | } |
254 | | |
255 | | /* ------ File enumeration ------ */ |
256 | | |
257 | | /* Thanks to Fritz Elfert (Fritz_Elfert@wue.maus.de) for */ |
258 | | /* the original version of the following code, and Richard Mlynarik */ |
259 | | /* (mly@adoc.xerox.com) for an improved version. */ |
260 | | |
261 | | #ifdef GS_NO_FILESYSTEM |
262 | | struct file_enum_s { |
263 | | int dummy; |
264 | | }; |
265 | | |
266 | | static file_enum dummy_enum; |
267 | | #else |
268 | | typedef struct dirstack_s dirstack; |
269 | | struct dirstack_s { |
270 | | dirstack *next; |
271 | | DIR *entry; |
272 | | }; |
273 | | |
274 | | gs_private_st_ptrs1(st_dirstack, dirstack, "dirstack", |
275 | | dirstack_enum_ptrs, dirstack_reloc_ptrs, next); |
276 | | |
277 | | struct file_enum_s { |
278 | | DIR *dirp; /* pointer to current open directory */ |
279 | | char *pattern; /* original pattern */ |
280 | | char *work; /* current path */ |
281 | | int worklen; /* strlen (work) */ |
282 | | dirstack *dstack; /* directory stack */ |
283 | | int patlen; |
284 | | int pathead; /* how much of pattern to consider |
285 | | * when listing files in current directory */ |
286 | | bool first_time; |
287 | | gs_memory_t *memory; |
288 | | }; |
289 | | gs_private_st_ptrs3(st_file_enum, struct file_enum_s, "file_enum", |
290 | | file_enum_enum_ptrs, file_enum_reloc_ptrs, pattern, work, dstack); |
291 | | |
292 | | /* Private procedures */ |
293 | | |
294 | | /* Do a wild-card match. */ |
295 | | #ifdef DEBUG |
296 | | static bool |
297 | | wmatch(const byte * str, uint len, const byte * pstr, uint plen, |
298 | | const gs_memory_t *mem) |
299 | | { |
300 | | bool match = string_match(str, len, pstr, plen, NULL); |
301 | | |
302 | | if (gs_debug_c('e')) { |
303 | | int i; |
304 | | dmlputs(mem, "[e]string_match(\""); |
305 | | for (i=0; i<len; i++) |
306 | | errprintf(mem, "%c", str[i]); |
307 | | dmputs(mem, "\", \""); |
308 | | for (i=0; i<plen; i++) |
309 | | errprintf(mem, "%c", pstr[i]); |
310 | | dmprintf1(mem, "\") = %s\n", (match ? "TRUE" : "false")); |
311 | | } |
312 | | return match; |
313 | | } |
314 | | #else |
315 | 0 | #define wmatch(S,L,PS,PL,M) string_match(S,L,PS,PL,NULL) |
316 | | #endif |
317 | | |
318 | | /* Search a string backward for a character. */ |
319 | | /* (This substitutes for strrchr, which some systems don't provide.) */ |
320 | | static char * |
321 | | rchr(char *str, char ch, int len) |
322 | 1.78M | { |
323 | 1.78M | register char *p = str + len; |
324 | | |
325 | 3.57M | while (p > str) |
326 | 3.57M | if (*--p == ch) |
327 | 1.78M | return p; |
328 | 0 | return 0; |
329 | 1.78M | } |
330 | | |
331 | | /* Pop a directory from the enumeration stack. */ |
332 | | static bool |
333 | | popdir(file_enum * pfen) |
334 | 1.78M | { |
335 | 1.78M | dirstack *d = pfen->dstack; |
336 | | |
337 | 1.78M | if (d == 0) |
338 | 1.78M | return false; |
339 | 0 | pfen->dirp = d->entry; |
340 | 0 | pfen->dstack = d->next; |
341 | 0 | gs_free_object(pfen->memory, d, "gp_enumerate_files(popdir)"); |
342 | 0 | return true; |
343 | 1.78M | } |
344 | | #endif |
345 | | |
346 | | /* Initialize an enumeration. */ |
347 | | file_enum * |
348 | | gp_enumerate_files_init_impl(gs_memory_t * mem, const char *pat, uint patlen) |
349 | 1.78M | { |
350 | | #ifdef GS_NO_FILESYSTEM |
351 | | return &dummy_enum; |
352 | | #else |
353 | 1.78M | file_enum *pfen; |
354 | 1.78M | char *p; |
355 | 1.78M | char *work; |
356 | | |
357 | | /* Reject attempts to enumerate paths longer than the */ |
358 | | /* system-dependent limit. */ |
359 | 1.78M | if (patlen > FILENAME_MAX) |
360 | 0 | return 0; |
361 | | |
362 | | /* Reject attempts to enumerate with a pattern containing zeroes. */ |
363 | 1.78M | { |
364 | 1.78M | const char *p1; |
365 | | |
366 | 87.2M | for (p1 = pat; p1 < pat + patlen; p1++) |
367 | 85.4M | if (*p1 == 0) |
368 | 0 | return 0; |
369 | 1.78M | } |
370 | | /* >>> Should crunch strings of repeated "/"'s in pat to a single "/" |
371 | | * >>> to match stupid unix filesystem "conventions" */ |
372 | | |
373 | 1.78M | pfen = gs_alloc_struct(mem, file_enum, &st_file_enum, |
374 | 1.78M | "gp_enumerate_files"); |
375 | 1.78M | if (pfen == 0) |
376 | 0 | return 0; |
377 | | |
378 | | /* pattern and work could be allocated as strings, */ |
379 | | /* but it's simpler for GC and freeing to allocate them as bytes. */ |
380 | | |
381 | 1.78M | pfen->memory = mem; |
382 | 1.78M | pfen->dstack = 0; |
383 | 1.78M | pfen->first_time = true; |
384 | 1.78M | pfen->patlen = patlen; |
385 | 1.78M | pfen->work = 0; |
386 | 1.78M | pfen->pattern = |
387 | 1.78M | (char *)gs_alloc_bytes(mem, patlen + 1, |
388 | 1.78M | "gp_enumerate_files(pattern)"); |
389 | 1.78M | if (pfen->pattern == 0) |
390 | 0 | goto fail1; |
391 | 1.78M | memcpy(pfen->pattern, pat, patlen); |
392 | 1.78M | pfen->pattern[patlen] = 0; |
393 | | |
394 | 1.78M | work = (char *)gs_alloc_bytes(mem, FILENAME_MAX + 1, |
395 | 1.78M | "gp_enumerate_files(work)"); |
396 | 1.78M | if (work == 0) |
397 | 0 | goto fail2; |
398 | 1.78M | pfen->work = work; |
399 | | |
400 | 1.78M | p = work; |
401 | 1.78M | memcpy(p, pat, patlen); |
402 | 1.78M | p += patlen; |
403 | 1.78M | *p = 0; |
404 | | |
405 | | /* Remove directory specifications beyond the first wild card. */ |
406 | | /* Some systems don't have strpbrk, so we code it open. */ |
407 | 1.78M | p = pfen->work; |
408 | 85.4M | while (*p != '*' && *p != '?' && *p != 0) |
409 | 83.6M | p++; |
410 | 3.57M | while (*p != '/' && *p != 0) |
411 | 1.78M | p++; |
412 | 1.78M | if (*p == '/') |
413 | 0 | *p = 0; |
414 | | /* Substring for first wildcard match */ |
415 | 1.78M | pfen->pathead = p - work; |
416 | | |
417 | | /* Select the next higher directory-level. */ |
418 | 1.78M | p = rchr(work, '/', p - work); |
419 | 1.78M | if (!p) { /* No directory specification */ |
420 | 0 | work[0] = 0; |
421 | 0 | pfen->worklen = 0; |
422 | 1.78M | } else { |
423 | 1.78M | if (p == work) { /* Root directory -- don't turn "/" into "" */ |
424 | 0 | p++; |
425 | 0 | } |
426 | 1.78M | *p = 0; |
427 | 1.78M | pfen->worklen = p - work; |
428 | 1.78M | } |
429 | | |
430 | 1.78M | return pfen; |
431 | | |
432 | 0 | fail2: |
433 | 0 | gs_free_object(mem, pfen->pattern, "gp_enumerate_files(pattern)"); |
434 | 0 | fail1: |
435 | 0 | gs_free_object(mem, pfen, "gp_enumerate_files"); |
436 | 0 | return NULL; |
437 | 0 | #endif |
438 | 0 | } |
439 | | |
440 | | /* Enumerate the next file. */ |
441 | | uint |
442 | | gp_enumerate_files_next_impl(gs_memory_t * mem, file_enum * pfen, char *ptr, uint maxlen) |
443 | 1.78M | { |
444 | | #ifdef GS_NO_FILESYSTEM |
445 | | return ~(uint)0; |
446 | | #else |
447 | 1.78M | const dir_entry *de; |
448 | 1.78M | char *work = pfen->work; |
449 | 1.78M | int worklen = pfen->worklen; |
450 | 1.78M | char *pattern = pfen->pattern; |
451 | 1.78M | int pathead = pfen->pathead; |
452 | 1.78M | int len; |
453 | | |
454 | 1.78M | if (pfen->first_time) { |
455 | 1.78M | pfen->dirp = ((worklen == 0) ? opendir(".") : opendir(work)); |
456 | 1.78M | if_debug1m('e', pfen->memory, "[e]file_enum:First-Open '%s'\n", work); |
457 | 1.78M | pfen->first_time = false; |
458 | 1.78M | if (pfen->dirp == 0) { /* first opendir failed */ |
459 | 1.78M | gp_enumerate_files_close(mem, pfen); |
460 | 1.78M | return ~(uint) 0; |
461 | 1.78M | } |
462 | 1.78M | } |
463 | 0 | top:de = readdir(pfen->dirp); |
464 | 0 | if (de == 0) { /* No more entries in this directory */ |
465 | 0 | char *p; |
466 | |
|
467 | 0 | if_debug0m('e', pfen->memory, "[e]file_enum:Closedir\n"); |
468 | 0 | closedir(pfen->dirp); |
469 | | /* Back working directory and matching pattern up one level */ |
470 | 0 | p = rchr(work, '/', worklen); |
471 | 0 | if (p != 0) { |
472 | 0 | if (p == work) |
473 | 0 | p++; |
474 | 0 | *p = 0; |
475 | 0 | worklen = p - work; |
476 | 0 | } else |
477 | 0 | worklen = 0; |
478 | 0 | if (pathead != pfen->patlen) { |
479 | 0 | p = rchr(pattern, '/', pathead); |
480 | 0 | if (p != 0) |
481 | 0 | pathead = p - pattern; |
482 | 0 | else |
483 | 0 | pathead = 0; |
484 | 0 | } |
485 | |
|
486 | 0 | if (popdir(pfen)) { /* Back up the directory tree. */ |
487 | 0 | if_debug1m('e', pfen->memory, "[e]file_enum:Dir popped '%s'\n", work); |
488 | 0 | goto top; |
489 | 0 | } else { |
490 | 0 | if_debug0m('e', pfen->memory, "[e]file_enum:Dirstack empty\n"); |
491 | 0 | gp_enumerate_files_close(mem, pfen); |
492 | 0 | return ~(uint) 0; |
493 | 0 | } |
494 | 0 | } |
495 | | /* Skip . and .. */ |
496 | 0 | len = strlen(de->d_name); |
497 | 0 | if (len <= 2 && (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))) |
498 | 0 | goto top; |
499 | 0 | if (len + worklen + 1 > FILENAME_MAX) |
500 | | /* Should be an error, I suppose */ |
501 | 0 | goto top; |
502 | 0 | if (worklen == 0) { /* "Current" directory (evil un*x kludge) */ |
503 | 0 | memcpy(work, de->d_name, len + 1); |
504 | 0 | } else if (worklen == 1 && work[0] == '/') { /* Root directory */ |
505 | 0 | memcpy(work + 1, de->d_name, len + 1); |
506 | 0 | len = len + 1; |
507 | 0 | } else { |
508 | 0 | work[worklen] = '/'; |
509 | 0 | memcpy(work + worklen + 1, de->d_name, len + 1); |
510 | 0 | len = worklen + 1 + len; |
511 | 0 | } |
512 | | |
513 | | /* Test for a match at this directory level */ |
514 | 0 | if (!wmatch((byte *) work, len, (byte *) pattern, pathead, pfen->memory)) |
515 | 0 | goto top; |
516 | | |
517 | | /* Perhaps descend into subdirectories */ |
518 | 0 | if (pathead < maxlen) { |
519 | | /* Using stat() to decide whether this item is a directory then opening |
520 | | using opendir(), results in Coverity complaining about races. Se |
521 | | instead we simple call opendir() immediately and look at whether it |
522 | | succeeded. */ |
523 | 0 | DIR *dp = opendir(work); |
524 | 0 | if (!dp) { |
525 | | /* Not a directory. */ |
526 | 0 | goto winner; |
527 | 0 | } |
528 | | |
529 | 0 | if (pfen->patlen == pathead + 1) { /* Listing "foo/?/" -- return this entry */ |
530 | 0 | closedir(dp); |
531 | 0 | work[len++] = '/'; |
532 | 0 | goto winner; |
533 | 0 | } |
534 | | |
535 | | /* >>> Should optimise the case in which the next level */ |
536 | | /* >>> of directory has no wildcards. */ |
537 | | #ifdef DEBUG |
538 | | { |
539 | | char save_end = pattern[pathead]; |
540 | | |
541 | | pattern[pathead] = 0; |
542 | | if_debug2m('e', pfen->memory, "[e]file_enum:fname='%s', p='%s'\n", |
543 | | work, pattern); |
544 | | pattern[pathead] = save_end; |
545 | | } |
546 | | #endif /* DEBUG */ |
547 | 0 | { /* Advance to the next directory-delimiter */ |
548 | | /* in pattern */ |
549 | 0 | char *p; |
550 | 0 | dirstack *d; |
551 | |
|
552 | 0 | if (pattern[pathead] == 0) { |
553 | 0 | pathead = pfen->patlen; |
554 | 0 | } else { |
555 | 0 | for (p = pattern + pathead + 1;; p++) { |
556 | 0 | if (*p == 0) { /* No more subdirectories to match */ |
557 | 0 | pathead = pfen->patlen; |
558 | 0 | break; |
559 | 0 | } else if (*p == '/') { |
560 | 0 | pathead = p - pattern; |
561 | 0 | break; |
562 | 0 | } |
563 | 0 | } |
564 | 0 | } |
565 | | |
566 | | /* Push a directory onto the enumeration stack. */ |
567 | 0 | d = gs_alloc_struct(pfen->memory, dirstack, |
568 | 0 | &st_dirstack, |
569 | 0 | "gp_enumerate_files(pushdir)"); |
570 | 0 | if (d != 0) { |
571 | 0 | d->next = pfen->dstack; |
572 | 0 | d->entry = pfen->dirp; |
573 | 0 | pfen->dstack = d; |
574 | 0 | } else |
575 | 0 | DO_NOTHING; /* >>> gs_error_VMerror!!! */ |
576 | |
|
577 | 0 | if_debug1m('e', pfen->memory, "[e]file_enum:Dir pushed '%s'\n", |
578 | 0 | work); |
579 | 0 | worklen = len; |
580 | 0 | pfen->dirp = dp; |
581 | 0 | goto top; |
582 | 0 | } |
583 | 0 | } |
584 | 0 | winner: |
585 | | /* We have a winner! */ |
586 | 0 | pfen->worklen = worklen; |
587 | 0 | pfen->pathead = pathead; |
588 | 0 | memcpy(ptr, work, len > maxlen ? maxlen : len); |
589 | |
|
590 | 0 | return len; |
591 | 0 | #endif |
592 | 0 | } |
593 | | |
594 | | /* Clean up the file enumeration. */ |
595 | | void |
596 | | gp_enumerate_files_close_impl(gs_memory_t * mem, file_enum * pfen) |
597 | 1.78M | { |
598 | | #ifdef GS_NO_FILESYSTEM |
599 | | /* No cleanup necessary */ |
600 | | #else |
601 | 1.78M | gs_memory_t *mem2 = pfen->memory; |
602 | 1.78M | (void)mem; |
603 | | |
604 | 1.78M | if_debug0m('e', mem2, "[e]file_enum:Cleanup\n"); |
605 | 1.78M | while (popdir(pfen)) /* clear directory stack */ |
606 | 1.78M | DO_NOTHING; |
607 | 1.78M | gs_free_object(mem2, (byte *) pfen->work, |
608 | 1.78M | "gp_enumerate_close(work)"); |
609 | 1.78M | gs_free_object(mem2, (byte *) pfen->pattern, |
610 | 1.78M | "gp_enumerate_files_close(pattern)"); |
611 | 1.78M | gs_free_object(mem2, pfen, "gp_enumerate_files_close"); |
612 | 1.78M | #endif |
613 | 1.78M | } |
614 | | |
615 | | /* Test-cases: |
616 | | (../?*r*?/?*.ps) {==} 100 string filenameforall |
617 | | (../?*r*?/?*.ps*) {==} 100 string filenameforall |
618 | | (../?*r*?/) {==} 100 string filenameforall |
619 | | (/t*?/?*.ps) {==} 100 string filenameforall |
620 | | */ |
621 | | |
622 | | /* --------- 64 bit file access ----------- */ |
623 | | |
624 | | gs_offset_t gp_ftell_impl(FILE *strm) |
625 | 4.98M | { |
626 | 4.98M | #if defined(HAVE_FILE64) |
627 | 4.98M | return ftello64(strm); |
628 | | #else |
629 | | return ftello(strm); |
630 | | #endif |
631 | 4.98M | } |
632 | | |
633 | | int gp_fseek_impl(FILE *strm, gs_offset_t offset, int origin) |
634 | 18.0M | { |
635 | 18.0M | #if defined(HAVE_FILE64) |
636 | 18.0M | return fseeko64(strm, offset, origin); |
637 | | #else |
638 | | off_t offset1 = (off_t)offset; |
639 | | |
640 | | if (offset != offset1) |
641 | | return -1; |
642 | | return fseeko(strm, offset1, origin); |
643 | | #endif |
644 | 18.0M | } |
645 | | |
646 | | bool gp_fseekable_impl(FILE *f) |
647 | 4.18k | { |
648 | 4.18k | struct stat s; |
649 | 4.18k | int fno; |
650 | | |
651 | 4.18k | fno = fileno(f); |
652 | 4.18k | if (fno < 0) |
653 | 0 | return(false); |
654 | | |
655 | 4.18k | if (fstat(fno, &s) < 0) |
656 | 0 | return(false); |
657 | | |
658 | 4.18k | return((bool)S_ISREG(s.st_mode)); |
659 | 4.18k | } |