]> git.ipfire.org Git - thirdparty/cups.git/blame - pstoraster/gp_unifs.c
Copyright update...
[thirdparty/cups.git] / pstoraster / gp_unifs.c
CommitLineData
efb2f309 1/*Copyright 1993-2002 by Easy Software Products.
caddbb58 2 Copyright 1993, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
3
8e4ff0ae 4 This file is part of GNU Ghostscript.
caddbb58 5
8e4ff0ae 6 GNU Ghostscript is distributed in the hope that it will be useful, but
caddbb58 7 WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8 to anyone for the consequences of using it or for whether it serves any
9 particular purpose or works at all, unless he says so in writing. Refer
10 to the GNU General Public License for full details.
11
8e4ff0ae 12 Everyone is granted permission to copy, modify and redistribute GNU
13 Ghostscript, but only under the conditions described in the GNU General
caddbb58 14 Public License. A copy of this license is supposed to have been given
15 to you along with GNU Ghostscript so you can know your rights and
8e4ff0ae 16 responsibilities. It should be in a file named COPYING. Among other
17 things, the copyright notice and this notice must be preserved on all
18 copies.
caddbb58 19
20 Aladdin Enterprises supports the work of the GNU Project, but is not
21 affiliated with the Free Software Foundation or the GNU Project. GNU
22 Ghostscript, as distributed by Aladdin Enterprises, does not require any
23 GNU software to build or run it.
8e4ff0ae 24*/
25
efb2f309 26/*$Id: gp_unifs.c,v 1.7 2002/01/02 17:59:10 mike Exp $ */
8e4ff0ae 27/* "Unix-like" file system platform routines for Ghostscript */
28#include "memory_.h"
29#include "string_.h"
30#include "gx.h"
31#include "gp.h"
32#include "gsstruct.h"
caddbb58 33#include "gsutil.h" /* for string_match */
8e4ff0ae 34#include "stat_.h"
35#include "dirent_.h"
caddbb58 36#include <sys/param.h> /* for MAXPATHLEN */
50b9556e 37#include <cups/cups.h>
8e4ff0ae 38
39/* Some systems (Interactive for example) don't define MAXPATHLEN,
40 * so we define it here. (This probably should be done via a Config-Script.)
41 */
42
43#ifndef MAXPATHLEN
44# define MAXPATHLEN 1024
45#endif
46
8e4ff0ae 47/* ------ File naming and accessing ------ */
48
49/* Define the default scratch file name prefix. */
50const char gp_scratch_file_name_prefix[] = "gs_";
51
52/* Define the name of the null output file. */
53const char gp_null_file_name[] = "/dev/null";
54
55/* Define the name that designates the current directory. */
56const char gp_current_directory_name[] = ".";
57
58/* Create and open a scratch file with a given name prefix. */
59/* Write the actual file name at fname. */
60FILE *
caddbb58 61gp_open_scratch_file(const char *prefix, char fname[gp_file_name_sizeof],
62 const char *mode)
50b9556e 63{
64 int fd; /* File descriptor for temp file */
caddbb58 65
caddbb58 66
50b9556e 67 if ((fd = cupsTempFd(fname, gp_file_name_sizeof)) < 0)
0819478b 68 return (NULL);
69 else
70 return (fdopen(fd, mode));
8e4ff0ae 71}
72
73/* Open a file with the given name, as a stream of uninterpreted bytes. */
74FILE *
75gp_fopen(const char *fname, const char *mode)
caddbb58 76{
77 return fopen(fname, mode);
78}
79
80/* Set a file into binary or text mode. */
81int
82gp_setmode_binary(FILE * pfile, bool mode)
83{
84 return 0; /* Noop under Unix */
8e4ff0ae 85}
86
87/* ------ File enumeration ------ */
88
89/* Thanks to Fritz Elfert (Fritz_Elfert@wue.maus.de) for */
90/* the original version of the following code, and Richard Mlynarik */
91/* (mly@adoc.xerox.com) for an improved version. */
92
93typedef struct dirstack_s dirstack;
94struct dirstack_s {
caddbb58 95 dirstack *next;
96 DIR *entry;
8e4ff0ae 97};
caddbb58 98
8e4ff0ae 99gs_private_st_ptrs1(st_dirstack, dirstack, "dirstack",
caddbb58 100 dirstack_enum_ptrs, dirstack_reloc_ptrs, next);
8e4ff0ae 101
102struct file_enum_s {
caddbb58 103 DIR *dirp; /* pointer to current open directory */
104 char *pattern; /* original pattern */
105 char *work; /* current path */
106 int worklen; /* strlen (work) */
107 dirstack *dstack; /* directory stack */
108 int patlen;
109 int pathead; /* how much of pattern to consider
8e4ff0ae 110 * when listing files in current directory */
caddbb58 111 bool first_time;
112 gs_memory_t *memory;
8e4ff0ae 113};
114gs_private_st_ptrs3(st_file_enum, struct file_enum_s, "file_enum",
caddbb58 115 file_enum_enum_ptrs, file_enum_reloc_ptrs, pattern, work, dstack);
8e4ff0ae 116
117/* Private procedures */
118
119/* Do a wild-card match. */
120#ifdef DEBUG
121private bool
caddbb58 122wmatch(const byte * str, uint len, const byte * pstr, uint plen,
123 const string_match_params * psmp)
124{
125 bool match = string_match(str, len, pstr, plen, psmp);
126
127 if (gs_debug_c('e')) {
128 dlputs("[e]string_match(\"");
129 fwrite(str, 1, len, dstderr);
130 dputs("\", \"");
131 fwrite(pstr, 1, plen, dstderr);
132 dprintf1("\") = %s\n", (match ? "TRUE" : "false"));
133 }
134 return match;
8e4ff0ae 135}
136#define string_match wmatch
137#endif
138
139/* Search a string backward for a character. */
140/* (This substitutes for strrchr, which some systems don't provide.) */
141private char *
142rchr(char *str, char ch, int len)
caddbb58 143{
144 register char *p = str + len;
145
146 while (p > str)
147 if (*--p == ch)
148 return p;
149 return 0;
8e4ff0ae 150}
151
152/* Pop a directory from the enumeration stack. */
153private bool
caddbb58 154popdir(file_enum * pfen)
155{
156 dirstack *d = pfen->dstack;
157
158 if (d == 0)
159 return false;
160 pfen->dirp = d->entry;
161 pfen->dstack = d->next;
162 gs_free_object(pfen->memory, d, "gp_enumerate_files(popdir)");
163 return true;
8e4ff0ae 164}
165
166/* Initialize an enumeration. */
167file_enum *
caddbb58 168gp_enumerate_files_init(const char *pat, uint patlen, gs_memory_t * mem)
169{
170 file_enum *pfen;
171 char *p;
172 char *work;
173
174 /* Reject attempts to enumerate paths longer than the */
175 /* system-dependent limit. */
176 if (patlen > MAXPATHLEN)
177 return 0;
8e4ff0ae 178
caddbb58 179 /* Reject attempts to enumerate with a pattern containing zeroes. */
180 {
181 const char *p1;
8e4ff0ae 182
caddbb58 183 for (p1 = pat; p1 < pat + patlen; p1++)
184 if (*p1 == 0)
8e4ff0ae 185 return 0;
caddbb58 186 }
187 /* >>> Should crunch strings of repeated "/"'s in pat to a single "/"
188 * >>> to match stupid unix filesystem "conventions" */
8e4ff0ae 189
caddbb58 190 pfen = gs_alloc_struct(mem, file_enum, &st_file_enum,
191 "gp_enumerate_files");
192 if (pfen == 0)
193 return 0;
8e4ff0ae 194
caddbb58 195 /* pattern and work could be allocated as strings, */
196 /* but it's simpler for GC and freeing to allocate them as bytes. */
8e4ff0ae 197
caddbb58 198 pfen->pattern =
199 (char *)gs_alloc_bytes(mem, patlen + 1,
200 "gp_enumerate_files(pattern)");
201 if (pfen->pattern == 0)
202 return 0;
203 memcpy(pfen->pattern, pat, patlen);
204 pfen->pattern[patlen] = 0;
8e4ff0ae 205
caddbb58 206 work = (char *)gs_alloc_bytes(mem, MAXPATHLEN + 1,
207 "gp_enumerate_files(work)");
208 if (work == 0)
209 return 0;
210 pfen->work = work;
211
212 p = work;
213 memcpy(p, pat, patlen);
214 p += patlen;
215 *p = 0;
216
217 /* Remove directory specifications beyond the first wild card. */
218 /* Some systems don't have strpbrk, so we code it open. */
219 p = pfen->work;
220 while (!(*p == '*' || *p == '?' || *p == 0))
221 p++;
222 while (!(*p == '/' || *p == 0))
223 p++;
224 if (*p == '/')
8e4ff0ae 225 *p = 0;
caddbb58 226 /* Substring for first wildcard match */
227 pfen->pathead = p - work;
228
229 /* Select the next higher directory-level. */
230 p = rchr(work, '/', p - work);
231 if (!p) { /* No directory specification */
232 work[0] = 0;
233 pfen->worklen = 0;
234 } else {
235 if (p == work) { /* Root directory -- don't turn "/" into "" */
236 p++;
237 }
238 *p = 0;
239 pfen->worklen = p - work;
240 }
241
242 pfen->memory = mem;
243 pfen->dstack = 0;
244 pfen->first_time = true;
245 pfen->patlen = patlen;
246 return pfen;
8e4ff0ae 247}
248
249/* Enumerate the next file. */
250uint
caddbb58 251gp_enumerate_files_next(file_enum * pfen, char *ptr, uint maxlen)
252{
253 const dir_entry *de;
254 char *work = pfen->work;
255 int worklen = pfen->worklen;
256 char *pattern = pfen->pattern;
257 int pathead = pfen->pathead;
258 int len;
259 struct stat stbuf;
260
261 if (pfen->first_time) {
262 pfen->dirp = ((worklen == 0) ? opendir(".") : opendir(work));
263 if_debug1('e', "[e]file_enum:First-Open '%s'\n", work);
264 pfen->first_time = false;
265 if (pfen->dirp == 0) { /* first opendir failed */
266 gp_enumerate_files_close(pfen);
267 return ~(uint) 0;
268 }
269 }
270 top:de = readdir(pfen->dirp);
271 if (de == 0) { /* No more entries in this directory */
272 char *p;
273
274 if_debug0('e', "[e]file_enum:Closedir\n");
275 closedir(pfen->dirp);
276 /* Back working directory and matching pattern up one level */
277 p = rchr(work, '/', worklen);
278 if (p != 0) {
279 if (p == work)
280 p++;
281 *p = 0;
282 worklen = p - work;
283 } else
284 worklen = 0;
285 p = rchr(pattern, '/', pathead);
286 if (p != 0)
287 pathead = p - pattern;
8e4ff0ae 288 else
caddbb58 289 pathead = 0;
290
291 if (popdir(pfen)) { /* Back up the directory tree. */
292 if_debug1('e', "[e]file_enum:Dir popped '%s'\n", work);
293 goto top;
294 } else {
295 if_debug0('e', "[e]file_enum:Dirstack empty\n");
296 gp_enumerate_files_close(pfen);
297 return ~(uint) 0;
298 }
299 }
300 /* Skip . and .. */
301 len = strlen(de->d_name);
302 if (len <= 2 && (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")))
303 goto top;
304 if (len + worklen + 1 > MAXPATHLEN)
305 /* Should be an error, I suppose */
306 goto top;
307 if (worklen == 0) { /* "Current" directory (evil un*x kludge) */
308 memcpy(work, de->d_name, len + 1);
309 } else if (worklen == 1 && work[0] == '/') { /* Root directory */
310 memcpy(work + 1, de->d_name, len + 1);
311 len = len + 1;
312 } else {
313 work[worklen] = '/';
314 memcpy(work + worklen + 1, de->d_name, len + 1);
315 len = worklen + 1 + len;
316 }
317
318 /* Test for a match at this directory level */
319 if (!string_match((byte *) work, len, (byte *) pattern, pathead, NULL))
320 goto top;
321
322 /* Perhaps descend into subdirectories */
323 if (pathead < pfen->patlen) {
324 DIR *dp;
325
326 if (((stat(work, &stbuf) >= 0)
327 ? !stat_is_dir(stbuf)
328 /* Couldn't stat it.
329 * Well, perhaps it's a directory and
330 * we'll be able to list it anyway.
331 * If it isn't or we can't, no harm done. */
332 : 0))
333 goto top;
334
335 if (pfen->patlen == pathead + 1) { /* Listing "foo/?/" -- return this entry */
336 /* if it's a directory. */
337 if (!stat_is_dir(stbuf)) { /* Do directoryp test the hard way */
8e4ff0ae 338 dp = opendir(work);
caddbb58 339 if (!dp)
340 goto top;
341 closedir(dp);
342 }
343 work[len++] = '/';
344 goto winner;
345 }
346 /* >>> Should optimise the case in which the next level */
347 /* >>> of directory has no wildcards. */
348 dp = opendir(work);
8e4ff0ae 349#ifdef DEBUG
caddbb58 350 {
351 char save_end = pattern[pathead];
352
353 pattern[pathead] = 0;
354 if_debug2('e', "[e]file_enum:fname='%s', p='%s'\n",
355 work, pattern);
356 pattern[pathead] = save_end;
357 }
8e4ff0ae 358#endif /* DEBUG */
caddbb58 359 if (!dp)
360 /* Can't list this one */
361 goto top;
362 else { /* Advance to the next directory-delimiter */
363 /* in pattern */
364 char *p;
365 dirstack *d;
366
367 for (p = pattern + pathead + 1;; p++) {
368 if (*p == 0) { /* No more subdirectories to match */
369 pathead = pfen->patlen;
370 break;
371 } else if (*p == '/') {
372 pathead = p - pattern;
373 break;
374 }
375 }
376
377 /* Push a directory onto the enumeration stack. */
378 d = gs_alloc_struct(pfen->memory, dirstack,
379 &st_dirstack,
380 "gp_enumerate_files(pushdir)");
381 if (d != 0) {
382 d->next = pfen->dstack;
383 d->entry = pfen->dirp;
384 pfen->dstack = d;
385 } else
386 DO_NOTHING; /* >>> e_VMerror!!! */
387
388 if_debug1('e', "[e]file_enum:Dir pushed '%s'\n",
389 work);
390 worklen = len;
391 pfen->dirp = dp;
392 goto top;
393 }
394 }
395 winner:
396 /* We have a winner! */
397 pfen->worklen = worklen;
398 pfen->pathead = pathead;
399 memcpy(ptr, work, len);
400 return len;
8e4ff0ae 401}
402
403/* Clean up the file enumeration. */
404void
caddbb58 405gp_enumerate_files_close(file_enum * pfen)
406{
407 gs_memory_t *mem = pfen->memory;
408
409 if_debug0('e', "[e]file_enum:Cleanup\n");
410 while (popdir(pfen)) /* clear directory stack */
411 DO_NOTHING;
412 gs_free_object(mem, (byte *) pfen->work,
413 "gp_enumerate_close(work)");
414 gs_free_object(mem, (byte *) pfen->pattern,
415 "gp_enumerate_files_close(pattern)");
416 gs_free_object(mem, pfen, "gp_enumerate_files_close");
8e4ff0ae 417}
418
419/* Test-cases:
420 (../?*r*?/?*.ps) {==} 100 string filenameforall
421 (../?*r*?/?*.ps*) {==} 100 string filenameforall
422 (../?*r*?/) {==} 100 string filenameforall
423 (/t*?/?*.ps) {==} 100 string filenameforall
caddbb58 424 */