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