1 /* Copyright (C) 2000-2003 artofcode LLC. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gpmisc.c,v 1.22 2003/12/04 12:35:35 igor Exp $ */
18 /* Miscellaneous support for platform facilities */
19
20 #include "unistd_.h"
21 #include "fcntl_.h"
22 #include "stdio_.h"
23 #include "stat_.h"
24 #include "memory_.h"
25 #include "string_.h"
26 #include "gp.h"
27 #include "gpgetenv.h"
28 #include "gpmisc.h"
29
30 /*
31 * Get the name of the directory for temporary files, if any. Currently
32 * this checks the TMPDIR and TEMP environment variables, in that order.
33 * The return value and the setting of *ptr and *plen are as for gp_getenv.
34 */
35 int
gp_gettmpdir(char * ptr,int * plen)36 gp_gettmpdir(char *ptr, int *plen)
37 {
38 int max_len = *plen;
39 int code = gp_getenv("TMPDIR", ptr, plen);
40
41 if (code != 1)
42 return code;
43 *plen = max_len;
44 return gp_getenv("TEMP", ptr, plen);
45 }
46
47 /*
48 * Open a temporary file, using O_EXCL and S_I*USR to prevent race
49 * conditions and symlink attacks.
50 */
51 FILE *
gp_fopentemp(const char * fname,const char * mode)52 gp_fopentemp(const char *fname, const char *mode)
53 {
54 int flags = O_EXCL;
55 /* Scan the mode to construct the flags. */
56 const char *p = mode;
57 int fildes;
58 FILE *file;
59
60 while (*p)
61 switch (*p++) {
62 case 'a':
63 flags |= O_CREAT | O_APPEND;
64 break;
65 case 'r':
66 flags |= O_RDONLY;
67 break;
68 case 'w':
69 flags |= O_CREAT | O_WRONLY | O_TRUNC;
70 break;
71 #ifdef O_BINARY
72 /* Watcom C insists on this non-ANSI flag being set. */
73 case 'b':
74 flags |= O_BINARY;
75 break;
76 #endif
77 case '+':
78 flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
79 break;
80 default: /* e.g., 'b' */
81 break;
82 }
83 fildes = open(fname, flags, S_IRUSR | S_IWUSR);
84 if (fildes < 0)
85 return 0;
86 /*
87 * The DEC VMS C compiler incorrectly defines the second argument of
88 * fdopen as (char *), rather than following the POSIX.1 standard,
89 * which defines it as (const char *). Patch this here.
90 */
91 file = fdopen(fildes, (char *)mode); /* still really const */
92 if (file == 0)
93 close(fildes);
94 return file;
95 }
96
97 /* Append a string to buffer. */
98 private inline bool
append(char ** bp,const char * bpe,const char ** ip,uint len)99 append(char **bp, const char *bpe, const char **ip, uint len)
100 {
101 if (bpe - *bp < len)
102 return false;
103 memcpy(*bp, *ip, len);
104 *bp += len;
105 *ip += len;
106 return true;
107 }
108
109 /* Search a separator forward. */
110 private inline uint
search_separator(const char ** ip,const char * ipe,const char * item,int direction)111 search_separator(const char **ip, const char *ipe, const char *item, int direction)
112 { uint slen = 0;
113 for (slen = 0; (*ip - ipe) * direction < 0; (*ip) += direction)
114 if((slen = gs_file_name_check_separator(*ip, ipe - *ip, item)) != 0)
115 break;
116 return slen;
117 }
118
119
120 /*
121 * Combine a file name with a prefix.
122 * Concatenates two paths and reduce parent references and current
123 * directory references from the concatenation when possible.
124 * The trailing zero byte is being added.
125 *
126 * Examples :
127 * "/gs/lib" + "../Resource/CMap/H" --> "/gs/Resource/CMap/H"
128 * "C:/gs/lib" + "../Resource/CMap/H" --> "C:/gs/Resource/CMap/H"
129 * "hard disk:gs:lib" + "::Resource:CMap:H" -->
130 * "hard disk:gs:Resource:CMap:H"
131 * "DUA1:[GHOSTSCRIPT.LIB" + "-.RESOURCE.CMAP]H" -->
132 * "DUA1:[GHOSTSCRIPT.RESOURCE.CMAP]H"
133 * "\\server\share/a/b///c/../d.e/./" + "../x.e/././/../../y.z/v.v" -->
134 * "\\server\share/a/y.z/v.v"
135 */
136 gp_file_name_combine_result
gp_file_name_combine_generic(const char * prefix,uint plen,const char * fname,uint flen,bool no_sibling,char * buffer,uint * blen)137 gp_file_name_combine_generic(const char *prefix, uint plen, const char *fname, uint flen,
138 bool no_sibling, char *buffer, uint *blen)
139 {
140 /*
141 * THIS CODE IS SHARED FOR MULTIPLE PLATFORMS.
142 * PLEASE DON'T CHANGE IT FOR A SPECIFIC PLATFORM.
143 * Change gp_file_name_combine instead.
144 */
145 char *bp = buffer, *bpe = buffer + *blen;
146 const char *ip, *ipe;
147 uint slen;
148 uint infix_type = 0; /* 0=none, 1=current, 2=parent. */
149 uint infix_len = 0;
150 uint rlen = gp_file_name_root(fname, flen);
151 /* We need a special handling of infixes only immediately after a drive. */
152
153 if (rlen != 0) {
154 /* 'fname' is absolute, ignore the prefix. */
155 ip = fname;
156 ipe = fname + flen;
157 } else {
158 /* Concatenate with prefix. */
159 ip = prefix;
160 ipe = prefix + plen;
161 rlen = gp_file_name_root(prefix, plen);
162 }
163 if (!append(&bp, bpe, &ip, rlen))
164 return gp_combine_small_buffer;
165 slen = gs_file_name_check_separator(bp, buffer - bp, bp); /* Backward search. */
166 if (rlen != 0 && slen == 0) {
167 /* Patch it against names like "c:dir" on Windows. */
168 const char *sep = gp_file_name_directory_separator();
169
170 slen = strlen(sep);
171 if (!append(&bp, bpe, &sep, slen))
172 return gp_combine_small_buffer;
173 rlen += slen;
174 }
175 for (;;) {
176 const char *item = ip;
177 uint ilen;
178
179 slen = search_separator(&ip, ipe, item, 1);
180 ilen = ip - item;
181 if (ilen == 0 && !gp_file_name_is_empty_item_meanful()) {
182 ip += slen;
183 slen = 0;
184 } else if (gp_file_name_is_current(item, ilen)) {
185 /* Skip the reference to 'current', except the starting one.
186 * We keep the starting 'current' for platforms, which
187 * require relative paths to start with it.
188 */
189 if (bp == buffer) {
190 if (!append(&bp, bpe, &item, ilen))
191 return gp_combine_small_buffer;
192 infix_type = 1;
193 infix_len = ilen;
194 } else {
195 ip += slen;
196 slen = 0;
197 }
198 } else if (!gp_file_name_is_parent(item, ilen)) {
199 if (!append(&bp, bpe, &item, ilen))
200 return gp_combine_small_buffer;
201 /* The 'item' pointer is now broken; it may be restored using 'ilen'. */
202 } else if (bp == buffer + rlen + infix_len) {
203 /* Input is a parent and the output only contains a root and an infix. */
204 if (rlen != 0)
205 return gp_combine_cant_handle;
206 switch (infix_type) {
207 case 1:
208 /* Replace the infix with parent. */
209 bp = buffer + rlen; /* Drop the old infix, if any. */
210 infix_len = 0;
211 /* Falls through. */
212 case 0:
213 /* We have no infix, start with parent. */
214 if ((no_sibling && ipe == fname + flen && flen != 0) ||
215 !gp_file_name_is_partent_allowed())
216 return gp_combine_cant_handle;
217 /* Falls through. */
218 case 2:
219 /* Append one more parent - falls through. */
220 DO_NOTHING;
221 }
222 if (!append(&bp, bpe, &item, ilen))
223 return gp_combine_small_buffer;
224 infix_type = 2;
225 infix_len += ilen;
226 /* Recompute the separator length. We cannot use the old slen on Mac OS. */
227 slen = gs_file_name_check_separator(ip, ipe - ip, ip);
228 } else {
229 /* Input is a parent and the output continues after infix. */
230 /* Unappend the last separator and the last item. */
231 uint slen1 = gs_file_name_check_separator(bp, buffer + rlen - bp, bp); /* Backward search. */
232 char *bie = bp - slen1;
233
234 bp = bie;
235 DISCARD(search_separator((const char **)&bp, buffer + rlen, bp, -1));
236 /* The cast above quiets a gcc warning. We believe it's a bug in the compiler. */
237 /* Skip the input with separator. We cannot use slen on Mac OS. */
238 ip += gs_file_name_check_separator(ip, ipe - ip, ip);
239 if (no_sibling) {
240 const char *p = ip;
241
242 DISCARD(search_separator(&p, ipe, ip, 1));
243 if (p - ip != bie - bp || memcmp(ip, bp, p - ip))
244 return gp_combine_cant_handle;
245 }
246 slen = 0;
247 }
248 if (slen) {
249 if (bp == buffer + rlen + infix_len)
250 infix_len += slen;
251 if (!append(&bp, bpe, &ip, slen))
252 return gp_combine_small_buffer;
253 }
254 if (ip == ipe) {
255 if (ipe == fname + flen) {
256 /* All done.
257 * Note that the case (prefix + plen == fname && flen == 0)
258 * falls here without appending a separator.
259 */
260 const char *zero="";
261
262 if (bp == buffer) {
263 /* Must not return empty path. */
264 const char *current = gp_file_name_current();
265 int clen = strlen(current);
266
267 if (!append(&bp, bpe, ¤t, clen))
268 return gp_combine_small_buffer;
269 }
270 *blen = bp - buffer;
271 if (!append(&bp, bpe, &zero, 1))
272 return gp_combine_small_buffer;
273 return gp_combine_success;
274 } else {
275 /* ipe == prefix + plen */
276 /* Switch to fname. */
277 ip = fname;
278 ipe = fname + flen;
279 if (slen == 0) {
280 /* Insert a separator. */
281 const char *sep;
282
283 slen = search_separator(&ip, ipe, fname, 1);
284 sep = (slen != 0 ? gp_file_name_directory_separator()
285 : gp_file_name_separator());
286 slen = strlen(sep);
287 if (bp == buffer + rlen + infix_len)
288 infix_len += slen;
289 if (!append(&bp, bpe, &sep, slen))
290 return gp_combine_small_buffer;
291 ip = fname; /* Switch to fname. */
292 }
293 }
294 }
295 }
296 }
297
298 /*
299 * Reduces parent references and current directory references when possible.
300 * The trailing zero byte is being added.
301 *
302 * Examples :
303 * "/gs/lib/../Resource/CMap/H" --> "/gs/Resource/CMap/H"
304 * "C:/gs/lib/../Resource/CMap/H" --> "C:/gs/Resource/CMap/H"
305 * "hard disk:gs:lib::Resource:CMap:H" -->
306 * "hard disk:gs:Resource:CMap:H"
307 * "DUA1:[GHOSTSCRIPT.LIB.-.RESOURCE.CMAP]H" -->
308 * "DUA1:[GHOSTSCRIPT.RESOURCE.CMAP]H"
309 * "\\server\share/a/b///c/../d.e/./../x.e/././/../../y.z/v.v" -->
310 * "\\server\share/a/y.z/v.v"
311 *
312 */
313 gp_file_name_combine_result
gp_file_name_reduce(const char * fname,uint flen,char * buffer,uint * blen)314 gp_file_name_reduce(const char *fname, uint flen, char *buffer, uint *blen)
315 {
316 return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen);
317 }
318
319 /*
320 * Answers whether a file name is absolute (starts from a root).
321 */
322 bool
gp_file_name_is_absolute(const char * fname,uint flen)323 gp_file_name_is_absolute(const char *fname, uint flen)
324 {
325 return (gp_file_name_root(fname, flen) > 0);
326 }
327
328 /*
329 * Returns length of all starting parent references.
330 */
331 private uint
gp_file_name_prefix(const char * fname,uint flen,bool (* test)(const char * fname,uint flen))332 gp_file_name_prefix(const char *fname, uint flen,
333 bool (*test)(const char *fname, uint flen))
334 {
335 uint plen = gp_file_name_root(fname, flen), slen;
336 const char *ip, *ipe;
337 const char *item = fname; /* plen == flen could cause an indeterminizm. */
338
339 if (plen > 0)
340 return 0;
341 ip = fname + plen;
342 ipe = fname + flen;
343 for (; ip < ipe; ) {
344 item = ip;
345 slen = search_separator(&ip, ipe, item, 1);
346 if (!(*test)(item, ip - item))
347 break;
348 ip += slen;
349 }
350 return item - fname;
351 }
352
353 /*
354 * Returns length of all starting parent references.
355 */
356 uint
gp_file_name_parents(const char * fname,uint flen)357 gp_file_name_parents(const char *fname, uint flen)
358 {
359 return gp_file_name_prefix(fname, flen, gp_file_name_is_parent);
360 }
361
362 /*
363 * Returns length of all starting cwd references.
364 */
365 uint
gp_file_name_cwds(const char * fname,uint flen)366 gp_file_name_cwds(const char *fname, uint flen)
367 {
368 return gp_file_name_prefix(fname, flen, gp_file_name_is_current);
369 }
370