1 /* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
13 CA 94903, U.S.A., +1(415)492-9861, for further information.
14 */
15
16
17 /* "Unix-like" file system platform routines for Ghostscript */
18
19 #include "stdio_.h" /* for FILENAME_MAX */
20 #include "memory_.h"
21 #include "string_.h"
22 #include "gx.h"
23 #include "gp.h"
24 #include "gpmisc.h"
25 #include "gsstruct.h"
26 #include "gsutil.h" /* for string_match */
27 #include "stat_.h"
28 #include "dirent_.h"
29 #include "unistd_.h"
30 #include <stdlib.h> /* for mkstemp/mktemp */
31
32 #if defined(__MINGW32__) && __MINGW32__ == 1
33 #define ftello ftell
34 #define fseeko fseek
35 #endif
36
37 /* Provide a definition of the maximum path length in case the system
38 * headers don't define it. This should be gp_file_name_sizeof from
39 * gp.h once that value is properly sent in a system-dependent way.
40 * HP-UX 11i 11.11 incorrectly defines FILENAME_MAX as 14.
41 */
42 #ifdef FILENAME_MAX
43 # if FILENAME_MAX < 80 /* arbitrary */
44 # undef FILENAME_MAX
45 # endif
46 #endif
47 #ifndef FILENAME_MAX
48 # define FILENAME_MAX 1024
49 #endif
50
51 /* Library routines not declared in a standard header */
52 extern char *mktemp(char *);
53
54 /* ------ File naming and accessing ------ */
55
56 /* Define the default scratch file name prefix. */
57 const char gp_scratch_file_name_prefix[] = "gs_";
58
59 /* Define the name of the null output file. */
60 const char gp_null_file_name[] = "/dev/null";
61
62 /* Define the name that designates the current directory. */
63 const char gp_current_directory_name[] = ".";
64
65 /* Create and open a scratch file with a given name prefix. */
66 /* Write the actual file name at fname. */
67 static FILE *
gp_open_scratch_file_generic(const gs_memory_t * mem,const char * prefix,char fname[gp_file_name_sizeof],const char * mode,bool b64)68 gp_open_scratch_file_generic(const gs_memory_t *mem,
69 const char *prefix,
70 char fname[gp_file_name_sizeof],
71 const char *mode,
72 bool b64)
73 { /* The -8 is for XXXXXX plus a possible final / and -. */
74 int prefix_length = strlen(prefix);
75 int len = gp_file_name_sizeof - prefix_length - 8;
76 FILE *fp;
77
78 if (gp_file_name_is_absolute(prefix, prefix_length))
79 *fname = 0;
80 else if (gp_gettmpdir(fname, &len) != 0)
81 strcpy(fname, "/tmp/");
82 else {
83 if (strlen(fname) != 0 && fname[strlen(fname) - 1] != '/')
84 strcat(fname, "/");
85 }
86 if (strlen(fname) + prefix_length + 8 >= gp_file_name_sizeof)
87 return 0; /* file name too long */
88 strcat(fname, prefix);
89 /* Prevent trailing X's in path from being converted by mktemp. */
90 if (*fname != 0 && fname[strlen(fname) - 1] == 'X')
91 strcat(fname, "-");
92 strcat(fname, "XXXXXX");
93
94 #ifdef HAVE_MKSTEMP
95 {
96 int file;
97 char ofname[gp_file_name_sizeof];
98
99 /* save the old filename template in case mkstemp fails */
100 memcpy(ofname, fname, gp_file_name_sizeof);
101 #ifdef HAVE_MKSTEMP64
102 if (b64)
103 file = mkstemp64(fname);
104 else
105 file = mkstemp(fname);
106 #else
107 file = mkstemp(fname);
108 #endif
109 if (file < -1) {
110 emprintf1(mem, "**** Could not open temporary file %s\n", ofname);
111 return NULL;
112 }
113 #if defined(O_LARGEFILE) && defined(__hpux)
114 if (b64)
115 fcntl(file, F_SETFD, fcntl(file, F_GETFD) | O_LARGEFILE);
116 #else
117 /* Fixme : what to do with b64 and 32-bit mkstemp? Unimplemented. */
118 #endif
119
120 fp = fdopen(file, mode);
121 if (fp == NULL)
122 close(file);
123 }
124 #else
125 mktemp(fname);
126 fp = (b64 ? gp_fopentemp : gp_fopentemp_64)(fname, mode);
127 #endif
128 if (fp == NULL)
129 emprintf1(mem, "**** Could not open temporary file %s\n", fname);
130 return fp;
131 }
132 FILE *
gp_open_scratch_file(const gs_memory_t * mem,const char * prefix,char fname[gp_file_name_sizeof],const char * mode)133 gp_open_scratch_file(const gs_memory_t *mem,
134 const char *prefix,
135 char fname[gp_file_name_sizeof],
136 const char *mode)
137 {
138 return gp_open_scratch_file_generic(mem, prefix, fname, mode, false);
139 }
140
141 /* Open a file with the given name, as a stream of uninterpreted bytes. */
142 FILE *
gp_fopen(const char * fname,const char * mode)143 gp_fopen(const char *fname, const char *mode)
144 {
145 return fopen(fname, mode);
146 }
147
148 /* Set a file into binary or text mode. */
149 int
gp_setmode_binary(FILE * pfile,bool mode)150 gp_setmode_binary(FILE * pfile, bool mode)
151 {
152 return 0; /* Noop under Unix */
153 }
154
155 /* ------ File enumeration ------ */
156
157 /* Thanks to Fritz Elfert (Fritz_Elfert@wue.maus.de) for */
158 /* the original version of the following code, and Richard Mlynarik */
159 /* (mly@adoc.xerox.com) for an improved version. */
160
161 typedef struct dirstack_s dirstack;
162 struct dirstack_s {
163 dirstack *next;
164 DIR *entry;
165 };
166
167 gs_private_st_ptrs1(st_dirstack, dirstack, "dirstack",
168 dirstack_enum_ptrs, dirstack_reloc_ptrs, next);
169
170 struct file_enum_s {
171 DIR *dirp; /* pointer to current open directory */
172 char *pattern; /* original pattern */
173 char *work; /* current path */
174 int worklen; /* strlen (work) */
175 dirstack *dstack; /* directory stack */
176 int patlen;
177 int pathead; /* how much of pattern to consider
178 * when listing files in current directory */
179 bool first_time;
180 gs_memory_t *memory;
181 };
182 gs_private_st_ptrs3(st_file_enum, struct file_enum_s, "file_enum",
183 file_enum_enum_ptrs, file_enum_reloc_ptrs, pattern, work, dstack);
184
185 /* Private procedures */
186
187 /* Do a wild-card match. */
188 #ifdef DEBUG
189 static bool
wmatch(const byte * str,uint len,const byte * pstr,uint plen,const string_match_params * psmp)190 wmatch(const byte * str, uint len, const byte * pstr, uint plen,
191 const string_match_params * psmp)
192 {
193 bool match = string_match(str, len, pstr, plen, psmp);
194
195 if (gs_debug_c('e')) {
196 int i;
197 dlputs("[e]string_match(\"");
198 for (i=0; i<len; i++)
199 errprintf_nomem("%c", str[i]);
200 dputs("\", \"");
201 for (i=0; i<plen; i++)
202 errprintf_nomem("%c", pstr[i]);
203 dprintf1("\") = %s\n", (match ? "TRUE" : "false"));
204 }
205 return match;
206 }
207 #define string_match wmatch
208 #endif
209
210 /* Search a string backward for a character. */
211 /* (This substitutes for strrchr, which some systems don't provide.) */
212 static char *
rchr(char * str,char ch,int len)213 rchr(char *str, char ch, int len)
214 {
215 register char *p = str + len;
216
217 while (p > str)
218 if (*--p == ch)
219 return p;
220 return 0;
221 }
222
223 /* Pop a directory from the enumeration stack. */
224 static bool
popdir(file_enum * pfen)225 popdir(file_enum * pfen)
226 {
227 dirstack *d = pfen->dstack;
228
229 if (d == 0)
230 return false;
231 pfen->dirp = d->entry;
232 pfen->dstack = d->next;
233 gs_free_object(pfen->memory, d, "gp_enumerate_files(popdir)");
234 return true;
235 }
236
237 /* Initialize an enumeration. */
238 file_enum *
gp_enumerate_files_init(const char * pat,uint patlen,gs_memory_t * mem)239 gp_enumerate_files_init(const char *pat, uint patlen, gs_memory_t * mem)
240 {
241 file_enum *pfen;
242 char *p;
243 char *work;
244
245 /* Reject attempts to enumerate paths longer than the */
246 /* system-dependent limit. */
247 if (patlen > FILENAME_MAX)
248 return 0;
249
250 /* Reject attempts to enumerate with a pattern containing zeroes. */
251 {
252 const char *p1;
253
254 for (p1 = pat; p1 < pat + patlen; p1++)
255 if (*p1 == 0)
256 return 0;
257 }
258 /* >>> Should crunch strings of repeated "/"'s in pat to a single "/"
259 * >>> to match stupid unix filesystem "conventions" */
260
261 pfen = gs_alloc_struct(mem, file_enum, &st_file_enum,
262 "gp_enumerate_files");
263 if (pfen == 0)
264 return 0;
265
266 /* pattern and work could be allocated as strings, */
267 /* but it's simpler for GC and freeing to allocate them as bytes. */
268
269 pfen->pattern =
270 (char *)gs_alloc_bytes(mem, patlen + 1,
271 "gp_enumerate_files(pattern)");
272 if (pfen->pattern == 0)
273 return 0;
274 memcpy(pfen->pattern, pat, patlen);
275 pfen->pattern[patlen] = 0;
276
277 work = (char *)gs_alloc_bytes(mem, FILENAME_MAX + 1,
278 "gp_enumerate_files(work)");
279 if (work == 0)
280 return 0;
281 pfen->work = work;
282
283 p = work;
284 memcpy(p, pat, patlen);
285 p += patlen;
286 *p = 0;
287
288 /* Remove directory specifications beyond the first wild card. */
289 /* Some systems don't have strpbrk, so we code it open. */
290 p = pfen->work;
291 while (!(*p == '*' || *p == '?' || *p == 0))
292 p++;
293 while (!(*p == '/' || *p == 0))
294 p++;
295 if (*p == '/')
296 *p = 0;
297 /* Substring for first wildcard match */
298 pfen->pathead = p - work;
299
300 /* Select the next higher directory-level. */
301 p = rchr(work, '/', p - work);
302 if (!p) { /* No directory specification */
303 work[0] = 0;
304 pfen->worklen = 0;
305 } else {
306 if (p == work) { /* Root directory -- don't turn "/" into "" */
307 p++;
308 }
309 *p = 0;
310 pfen->worklen = p - work;
311 }
312
313 pfen->memory = mem;
314 pfen->dstack = 0;
315 pfen->first_time = true;
316 pfen->patlen = patlen;
317 return pfen;
318 }
319
320 /* Enumerate the next file. */
321 uint
gp_enumerate_files_next(file_enum * pfen,char * ptr,uint maxlen)322 gp_enumerate_files_next(file_enum * pfen, char *ptr, uint maxlen)
323 {
324 const dir_entry *de;
325 char *work = pfen->work;
326 int worklen = pfen->worklen;
327 char *pattern = pfen->pattern;
328 int pathead = pfen->pathead;
329 int len;
330 struct stat stbuf;
331
332 if (pfen->first_time) {
333 pfen->dirp = ((worklen == 0) ? opendir(".") : opendir(work));
334 if_debug1('e', "[e]file_enum:First-Open '%s'\n", work);
335 pfen->first_time = false;
336 if (pfen->dirp == 0) { /* first opendir failed */
337 gp_enumerate_files_close(pfen);
338 return ~(uint) 0;
339 }
340 }
341 top:de = readdir(pfen->dirp);
342 if (de == 0) { /* No more entries in this directory */
343 char *p;
344
345 if_debug0('e', "[e]file_enum:Closedir\n");
346 closedir(pfen->dirp);
347 /* Back working directory and matching pattern up one level */
348 p = rchr(work, '/', worklen);
349 if (p != 0) {
350 if (p == work)
351 p++;
352 *p = 0;
353 worklen = p - work;
354 } else
355 worklen = 0;
356 p = rchr(pattern, '/', pathead);
357 if (p != 0)
358 pathead = p - pattern;
359 else
360 pathead = 0;
361
362 if (popdir(pfen)) { /* Back up the directory tree. */
363 if_debug1('e', "[e]file_enum:Dir popped '%s'\n", work);
364 goto top;
365 } else {
366 if_debug0('e', "[e]file_enum:Dirstack empty\n");
367 gp_enumerate_files_close(pfen);
368 return ~(uint) 0;
369 }
370 }
371 /* Skip . and .. */
372 len = strlen(de->d_name);
373 if (len <= 2 && (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")))
374 goto top;
375 if (len + worklen + 1 > FILENAME_MAX)
376 /* Should be an error, I suppose */
377 goto top;
378 if (worklen == 0) { /* "Current" directory (evil un*x kludge) */
379 memcpy(work, de->d_name, len + 1);
380 } else if (worklen == 1 && work[0] == '/') { /* Root directory */
381 memcpy(work + 1, de->d_name, len + 1);
382 len = len + 1;
383 } else {
384 work[worklen] = '/';
385 memcpy(work + worklen + 1, de->d_name, len + 1);
386 len = worklen + 1 + len;
387 }
388
389 /* Test for a match at this directory level */
390 if (!string_match((byte *) work, len, (byte *) pattern, pathead, NULL))
391 goto top;
392
393 /* Perhaps descend into subdirectories */
394 if (pathead < pfen->patlen) {
395 DIR *dp;
396
397 if (((stat(work, &stbuf) >= 0)
398 ? !stat_is_dir(stbuf)
399 /* Couldn't stat it.
400 * Well, perhaps it's a directory and
401 * we'll be able to list it anyway.
402 * If it isn't or we can't, no harm done. */
403 : 0))
404 goto top;
405
406 if (pfen->patlen == pathead + 1) { /* Listing "foo/?/" -- return this entry */
407 /* if it's a directory. */
408 if (!stat_is_dir(stbuf)) { /* Do directoryp test the hard way */
409 dp = opendir(work);
410 if (!dp)
411 goto top;
412 closedir(dp);
413 }
414 work[len++] = '/';
415 goto winner;
416 }
417 /* >>> Should optimise the case in which the next level */
418 /* >>> of directory has no wildcards. */
419 dp = opendir(work);
420 #ifdef DEBUG
421 {
422 char save_end = pattern[pathead];
423
424 pattern[pathead] = 0;
425 if_debug2('e', "[e]file_enum:fname='%s', p='%s'\n",
426 work, pattern);
427 pattern[pathead] = save_end;
428 }
429 #endif /* DEBUG */
430 if (!dp)
431 /* Can't list this one */
432 goto top;
433 else { /* Advance to the next directory-delimiter */
434 /* in pattern */
435 char *p;
436 dirstack *d;
437
438 for (p = pattern + pathead + 1;; p++) {
439 if (*p == 0) { /* No more subdirectories to match */
440 pathead = pfen->patlen;
441 break;
442 } else if (*p == '/') {
443 pathead = p - pattern;
444 break;
445 }
446 }
447
448 /* Push a directory onto the enumeration stack. */
449 d = gs_alloc_struct(pfen->memory, dirstack,
450 &st_dirstack,
451 "gp_enumerate_files(pushdir)");
452 if (d != 0) {
453 d->next = pfen->dstack;
454 d->entry = pfen->dirp;
455 pfen->dstack = d;
456 } else
457 DO_NOTHING; /* >>> e_VMerror!!! */
458
459 if_debug1('e', "[e]file_enum:Dir pushed '%s'\n",
460 work);
461 worklen = len;
462 pfen->dirp = dp;
463 goto top;
464 }
465 }
466 winner:
467 /* We have a winner! */
468 pfen->worklen = worklen;
469 pfen->pathead = pathead;
470 memcpy(ptr, work, len);
471 return len;
472 }
473
474 /* Clean up the file enumeration. */
475 void
gp_enumerate_files_close(file_enum * pfen)476 gp_enumerate_files_close(file_enum * pfen)
477 {
478 gs_memory_t *mem = pfen->memory;
479
480 if_debug0('e', "[e]file_enum:Cleanup\n");
481 while (popdir(pfen)) /* clear directory stack */
482 DO_NOTHING;
483 gs_free_object(mem, (byte *) pfen->work,
484 "gp_enumerate_close(work)");
485 gs_free_object(mem, (byte *) pfen->pattern,
486 "gp_enumerate_files_close(pattern)");
487 gs_free_object(mem, pfen, "gp_enumerate_files_close");
488 }
489
490 /* Test-cases:
491 (../?*r*?/?*.ps) {==} 100 string filenameforall
492 (../?*r*?/?*.ps*) {==} 100 string filenameforall
493 (../?*r*?/) {==} 100 string filenameforall
494 (/t*?/?*.ps) {==} 100 string filenameforall
495 */
496
497 /* --------- 64 bit file access ----------- */
498
gp_fopen_64(const char * filename,const char * mode)499 FILE *gp_fopen_64(const char *filename, const char *mode)
500 {
501 #if defined(HAVE_FILE64)
502 return fopen64(filename, mode);
503 #else
504 return fopen(filename, mode);
505 #endif
506 }
507
gp_open_scratch_file_64(const gs_memory_t * mem,const char * prefix,char fname[gp_file_name_sizeof],const char * mode)508 FILE *gp_open_scratch_file_64(const gs_memory_t *mem,
509 const char *prefix,
510 char fname[gp_file_name_sizeof],
511 const char *mode)
512 {
513 return gp_open_scratch_file_generic(mem, prefix, fname, mode, true);
514 }
515
516 /* gp_open_printer_64 is defined in gp_unix.h */
517
gp_ftell_64(FILE * strm)518 int64_t gp_ftell_64(FILE *strm)
519 {
520 #if defined(HAVE_FILE64)
521 return ftello64(strm);
522 #else
523 return ftello(strm);
524 #endif
525 }
526
gp_fseek_64(FILE * strm,int64_t offset,int origin)527 int gp_fseek_64(FILE *strm, int64_t offset, int origin)
528 {
529 #if defined(HAVE_FILE64)
530 return fseeko64(strm, offset, origin);
531 #else
532 off_t offset1 = (off_t)offset;
533
534 if (offset != offset1)
535 return -1;
536 return fseeko(strm, offset1, origin);
537 #endif
538 }
539