xref: /plan9/sys/src/cmd/gs/src/gpmisc.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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, &current, 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