2 |
- |
1 |
/* Copyright (C) 1989, 2000-2004, 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: zfile.c,v 1.42 2005/07/14 15:14:39 alexcher Exp $ */
|
|
|
18 |
/* Non-I/O file operators */
|
|
|
19 |
#include "memory_.h"
|
|
|
20 |
#include "string_.h"
|
|
|
21 |
#include "unistd_.h"
|
|
|
22 |
#include "ghost.h"
|
|
|
23 |
#include "gscdefs.h" /* for gx_io_device_table */
|
|
|
24 |
#include "gsutil.h" /* for bytes_compare */
|
|
|
25 |
#include "gp.h"
|
|
|
26 |
#include "gpmisc.h"
|
|
|
27 |
#include "gsfname.h"
|
|
|
28 |
#include "gsstruct.h" /* for registering root */
|
|
|
29 |
#include "gxalloc.h" /* for streams */
|
|
|
30 |
#include "oper.h"
|
|
|
31 |
#include "dstack.h" /* for systemdict */
|
|
|
32 |
#include "estack.h" /* for filenameforall, .execfile */
|
|
|
33 |
#include "ialloc.h"
|
|
|
34 |
#include "ilevel.h" /* %names only work in Level 2 */
|
|
|
35 |
#include "interp.h" /* gs_errorinfo_put_string prototype */
|
|
|
36 |
#include "iname.h"
|
|
|
37 |
#include "isave.h" /* for restore */
|
|
|
38 |
#include "idict.h"
|
|
|
39 |
#include "iutil.h"
|
|
|
40 |
#include "stream.h"
|
|
|
41 |
#include "strimpl.h"
|
|
|
42 |
#include "sfilter.h"
|
|
|
43 |
#include "gxiodev.h" /* must come after stream.h */
|
|
|
44 |
/* and before files.h */
|
|
|
45 |
#include "files.h"
|
|
|
46 |
#include "main.h" /* for gs_lib_paths */
|
|
|
47 |
#include "store.h"
|
|
|
48 |
|
|
|
49 |
/* Import the IODevice table. */
|
|
|
50 |
extern_gx_io_device_table();
|
|
|
51 |
|
|
|
52 |
/* Import the dtype of the stdio IODevices. */
|
|
|
53 |
extern const char iodev_dtype_stdio[];
|
|
|
54 |
|
|
|
55 |
/* Forward references: file name parsing. */
|
|
|
56 |
private int parse_file_name(const ref * op, gs_parsed_file_name_t * pfn, bool safemode);
|
|
|
57 |
private int parse_real_file_name(const ref * op,
|
|
|
58 |
gs_parsed_file_name_t * pfn,
|
|
|
59 |
gs_memory_t *mem, client_name_t cname);
|
|
|
60 |
private int parse_file_access_string(const ref *op, char file_access[4]);
|
|
|
61 |
|
|
|
62 |
/* Forward references: other. */
|
|
|
63 |
private int execfile_finish(i_ctx_t *);
|
|
|
64 |
private int execfile_cleanup(i_ctx_t *);
|
|
|
65 |
private int zopen_file(i_ctx_t *, const gs_parsed_file_name_t *pfn,
|
|
|
66 |
const char *file_access, stream **ps,
|
|
|
67 |
gs_memory_t *mem);
|
|
|
68 |
private iodev_proc_open_file(iodev_os_open_file);
|
|
|
69 |
private void file_init_stream(stream *s, FILE *file, const char *fmode,
|
|
|
70 |
byte *buffer, uint buffer_size);
|
|
|
71 |
stream_proc_report_error(filter_report_error);
|
|
|
72 |
|
|
|
73 |
/*
|
|
|
74 |
* Since there can be many file objects referring to the same file/stream,
|
|
|
75 |
* we can't simply free a stream when we close it. On the other hand,
|
|
|
76 |
* we don't want freed streams to clutter up memory needlessly.
|
|
|
77 |
* Our solution is to retain the freed streams, and reuse them.
|
|
|
78 |
* To prevent an old file object from being able to access a reused stream,
|
|
|
79 |
* we keep a serial number in each stream, and check it against a serial
|
|
|
80 |
* number stored in the file object (as the "size"); when we close a file,
|
|
|
81 |
* we increment its serial number. If the serial number ever overflows,
|
|
|
82 |
* we leave it at zero, and do not reuse the stream.
|
|
|
83 |
* (This will never happen.)
|
|
|
84 |
*
|
|
|
85 |
* Storage management for this scheme is a little tricky. We maintain an
|
|
|
86 |
* invariant that says that a stream opened at a given save level always
|
|
|
87 |
* uses a stream structure allocated at that level. By doing this, we don't
|
|
|
88 |
* need to keep track separately of streams open at a level vs. streams
|
|
|
89 |
* allocated at a level. To make this interact properly with save and
|
|
|
90 |
* restore, we maintain a list of all streams allocated at this level, both
|
|
|
91 |
* open and closed. We store this list in the allocator: this is a hack,
|
|
|
92 |
* but it simplifies bookkeeping (in particular, it guarantees the list is
|
|
|
93 |
* restored properly by a restore).
|
|
|
94 |
*
|
|
|
95 |
* We want to close streams freed by restore and by garbage collection. We
|
|
|
96 |
* use the finalization procedure for this. For restore, we don't have to
|
|
|
97 |
* do anything special to make this happen. For garbage collection, we do
|
|
|
98 |
* something more drastic: we simply clear the list of known streams (at all
|
|
|
99 |
* save levels). Any streams open at the time of garbage collection will no
|
|
|
100 |
* longer participate in the list of known streams, but this does no harm;
|
|
|
101 |
* it simply means that they won't get reused, and can only be reclaimed by
|
|
|
102 |
* a future garbage collection or restore.
|
|
|
103 |
*/
|
|
|
104 |
|
|
|
105 |
/*
|
|
|
106 |
* Define the default stream buffer sizes. For file streams,
|
|
|
107 |
* this is arbitrary, since the C library or operating system
|
|
|
108 |
* does its own buffering in addition.
|
|
|
109 |
* However, a buffer size of at least 2K bytes is necessary to prevent
|
|
|
110 |
* JPEG decompression from running very slow. When less than 2K, an
|
|
|
111 |
* intermediate filter is installed that transfers 1 byte at a time
|
|
|
112 |
* causing many aborted roundtrips through the JPEG filter code.
|
|
|
113 |
*/
|
|
|
114 |
#define DEFAULT_BUFFER_SIZE 2048
|
|
|
115 |
const uint file_default_buffer_size = DEFAULT_BUFFER_SIZE;
|
|
|
116 |
|
|
|
117 |
/* An invalid file object */
|
|
|
118 |
private stream invalid_file_stream;
|
|
|
119 |
stream *const invalid_file_entry = &invalid_file_stream;
|
|
|
120 |
|
|
|
121 |
/* Initialize the file table */
|
|
|
122 |
private int
|
|
|
123 |
zfile_init(i_ctx_t *i_ctx_p)
|
|
|
124 |
{
|
|
|
125 |
/* Create and initialize an invalid (closed) stream. */
|
|
|
126 |
/* Initialize the stream for the sake of the GC, */
|
|
|
127 |
/* and so it can act as an empty input stream. */
|
|
|
128 |
|
|
|
129 |
stream *const s = &invalid_file_stream;
|
|
|
130 |
|
|
|
131 |
s_init(s, NULL);
|
|
|
132 |
sread_string(s, NULL, 0);
|
|
|
133 |
s->next = s->prev = 0;
|
|
|
134 |
s_init_no_id(s);
|
|
|
135 |
return 0;
|
|
|
136 |
}
|
|
|
137 |
|
|
|
138 |
/* Make an invalid file object. */
|
|
|
139 |
void
|
|
|
140 |
make_invalid_file(ref * fp)
|
|
|
141 |
{
|
|
|
142 |
make_file(fp, avm_invalid_file_entry, ~0, invalid_file_entry);
|
|
|
143 |
}
|
|
|
144 |
|
|
|
145 |
/* Check a file name for permission by stringmatch on one of the */
|
|
|
146 |
/* strings of the permitgroup array. */
|
|
|
147 |
private int
|
|
|
148 |
check_file_permissions_reduced(i_ctx_t *i_ctx_p, const char *fname, int len,
|
|
|
149 |
const char *permitgroup)
|
|
|
150 |
{
|
|
|
151 |
long i;
|
|
|
152 |
ref *permitlist = NULL;
|
|
|
153 |
/* an empty string (first character == 0) if '\' character is */
|
|
|
154 |
/* recognized as a file name separator as on DOS & Windows */
|
|
|
155 |
const char *win_sep2 = "\\";
|
|
|
156 |
bool use_windows_pathsep = (gs_file_name_check_separator(win_sep2, 1, win_sep2) == 1);
|
|
|
157 |
uint plen = gp_file_name_parents(fname, len);
|
|
|
158 |
|
|
|
159 |
/* Assuming a reduced file name. */
|
|
|
160 |
|
|
|
161 |
if (dict_find_string(&(i_ctx_p->userparams), permitgroup, &permitlist) <= 0)
|
|
|
162 |
return 0; /* if Permissions not found, just allow access */
|
|
|
163 |
|
|
|
164 |
for (i=0; i<r_size(permitlist); i++) {
|
|
|
165 |
ref permitstring;
|
|
|
166 |
const string_match_params win_filename_params = {
|
|
|
167 |
'*', '?', '\\', true, true /* ignore case & '/' == '\\' */
|
|
|
168 |
};
|
|
|
169 |
const byte *permstr;
|
|
|
170 |
uint permlen;
|
|
|
171 |
int cwd_len = 0;
|
|
|
172 |
|
|
|
173 |
if (array_get(imemory, permitlist, i, &permitstring) < 0 ||
|
|
|
174 |
r_type(&permitstring) != t_string
|
|
|
175 |
)
|
|
|
176 |
break; /* any problem, just fail */
|
|
|
177 |
permstr = permitstring.value.bytes;
|
|
|
178 |
permlen = r_size(&permitstring);
|
|
|
179 |
/*
|
|
|
180 |
* Check if any file name is permitted with "*".
|
|
|
181 |
*/
|
|
|
182 |
if (permlen == 1 && permstr[0] == '*')
|
|
|
183 |
return 0; /* success */
|
|
|
184 |
/*
|
|
|
185 |
* If the filename starts with parent references,
|
|
|
186 |
* the permission element must start with same number of parent references.
|
|
|
187 |
*/
|
|
|
188 |
if (plen != 0 && plen != gp_file_name_parents((const char *)permstr, permlen))
|
|
|
189 |
continue;
|
|
|
190 |
cwd_len = gp_file_name_cwds((const char *)permstr, permlen);
|
|
|
191 |
/*
|
|
|
192 |
* If the permission starts with "./", absolute paths
|
|
|
193 |
* are not permitted.
|
|
|
194 |
*/
|
|
|
195 |
if (cwd_len > 0 && gp_file_name_is_absolute(fname, len))
|
|
|
196 |
continue;
|
|
|
197 |
/*
|
|
|
198 |
* If the permission starts with "./", relative paths
|
|
|
199 |
* with no "./" are allowed as well as with "./".
|
|
|
200 |
* 'fname' has no "./" because it is reduced.
|
|
|
201 |
*/
|
|
|
202 |
if (string_match( (const unsigned char*) fname, len,
|
|
|
203 |
permstr + cwd_len, permlen - cwd_len,
|
|
|
204 |
use_windows_pathsep ? &win_filename_params : NULL))
|
|
|
205 |
return 0; /* success */
|
|
|
206 |
}
|
|
|
207 |
/* not found */
|
|
|
208 |
return e_invalidfileaccess;
|
|
|
209 |
}
|
|
|
210 |
|
|
|
211 |
/* Check a file name for permission by stringmatch on one of the */
|
|
|
212 |
/* strings of the permitgroup array */
|
|
|
213 |
private int
|
|
|
214 |
check_file_permissions(i_ctx_t *i_ctx_p, const char *fname, int len,
|
|
|
215 |
const char *permitgroup)
|
|
|
216 |
{
|
|
|
217 |
char fname_reduced[gp_file_name_sizeof];
|
|
|
218 |
uint rlen = sizeof(fname_reduced);
|
|
|
219 |
|
|
|
220 |
if (gp_file_name_reduce(fname, len, fname_reduced, &rlen) != gp_combine_success)
|
|
|
221 |
return e_invalidaccess; /* fail if we couldn't reduce */
|
|
|
222 |
return check_file_permissions_reduced(i_ctx_p, fname_reduced, rlen, permitgroup);
|
|
|
223 |
}
|
|
|
224 |
|
|
|
225 |
/* <name_string> <access_string> file <file> */
|
|
|
226 |
private int
|
|
|
227 |
zfile(i_ctx_t *i_ctx_p)
|
|
|
228 |
{
|
|
|
229 |
os_ptr op = osp;
|
|
|
230 |
char file_access[4];
|
|
|
231 |
gs_parsed_file_name_t pname;
|
|
|
232 |
int code = parse_file_access_string(op, file_access);
|
|
|
233 |
stream *s;
|
|
|
234 |
|
|
|
235 |
if (code < 0)
|
|
|
236 |
return code;
|
|
|
237 |
code = parse_file_name(op - 1, &pname, i_ctx_p->LockFilePermissions);
|
|
|
238 |
if (code < 0)
|
|
|
239 |
return code;
|
|
|
240 |
/*
|
|
|
241 |
* HACK: temporarily patch the current context pointer into the
|
|
|
242 |
* state pointer for stdio-related devices. See ziodev.c for
|
|
|
243 |
* more information.
|
|
|
244 |
*/
|
|
|
245 |
if (pname.iodev && pname.iodev->dtype == iodev_dtype_stdio) {
|
|
|
246 |
bool statement = (strcmp(pname.iodev->dname, "%statementedit%") == 0);
|
|
|
247 |
bool lineedit = (strcmp(pname.iodev->dname, "%lineedit%") == 0);
|
|
|
248 |
if (pname.fname)
|
|
|
249 |
return_error(e_invalidfileaccess);
|
|
|
250 |
if (statement || lineedit) {
|
|
|
251 |
/* These need special code to support callouts */
|
|
|
252 |
gx_io_device *indev = gs_findiodevice((const byte *)"%stdin", 6);
|
|
|
253 |
stream *ins;
|
|
|
254 |
if (strcmp(file_access, "r"))
|
|
|
255 |
return_error(e_invalidfileaccess);
|
|
|
256 |
indev->state = i_ctx_p;
|
|
|
257 |
code = (indev->procs.open_device)(indev, file_access, &ins, imemory);
|
|
|
258 |
indev->state = 0;
|
|
|
259 |
if (code < 0)
|
|
|
260 |
return code;
|
|
|
261 |
check_ostack(2);
|
|
|
262 |
push(2);
|
|
|
263 |
make_stream_file(op - 3, ins, file_access);
|
|
|
264 |
make_bool(op-2, statement);
|
|
|
265 |
make_int(op-1, 0);
|
|
|
266 |
make_string(op, icurrent_space, 0, NULL);
|
|
|
267 |
return zfilelineedit(i_ctx_p);
|
|
|
268 |
}
|
|
|
269 |
pname.iodev->state = i_ctx_p;
|
|
|
270 |
code = (*pname.iodev->procs.open_device)(pname.iodev,
|
|
|
271 |
file_access, &s, imemory);
|
|
|
272 |
pname.iodev->state = NULL;
|
|
|
273 |
} else {
|
|
|
274 |
if (pname.iodev == NULL)
|
|
|
275 |
pname.iodev = iodev_default;
|
|
|
276 |
code = zopen_file(i_ctx_p, &pname, file_access, &s, imemory);
|
|
|
277 |
}
|
|
|
278 |
if (code < 0)
|
|
|
279 |
return code;
|
|
|
280 |
code = ssetfilename(s, op[-1].value.const_bytes, r_size(op - 1));
|
|
|
281 |
if (code < 0) {
|
|
|
282 |
sclose(s);
|
|
|
283 |
return_error(e_VMerror);
|
|
|
284 |
}
|
|
|
285 |
make_stream_file(op - 1, s, file_access);
|
|
|
286 |
pop(1);
|
|
|
287 |
return code;
|
|
|
288 |
}
|
|
|
289 |
|
|
|
290 |
/*
|
|
|
291 |
* Files created with .tempfile permit some operations even if the
|
|
|
292 |
* temp directory is not explicitly named on the PermitFile... path
|
|
|
293 |
* The names 'SAFETY' and 'tempfiles' are defined by gs_init.ps
|
|
|
294 |
*/
|
|
|
295 |
private bool
|
|
|
296 |
file_is_tempfile(i_ctx_t *i_ctx_p, const ref *op)
|
|
|
297 |
{
|
|
|
298 |
ref *SAFETY;
|
|
|
299 |
ref *tempfiles;
|
|
|
300 |
ref kname;
|
|
|
301 |
|
|
|
302 |
if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 ||
|
|
|
303 |
dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0)
|
|
|
304 |
return false;
|
|
|
305 |
if (name_ref(imemory, op->value.bytes, r_size(op), &kname, -1) < 0 ||
|
|
|
306 |
dict_find(tempfiles, &kname, &SAFETY) <= 0)
|
|
|
307 |
return false;
|
|
|
308 |
return true;
|
|
|
309 |
}
|
|
|
310 |
|
|
|
311 |
/* ------ Level 2 extensions ------ */
|
|
|
312 |
|
|
|
313 |
/* <string> deletefile - */
|
|
|
314 |
private int
|
|
|
315 |
zdeletefile(i_ctx_t *i_ctx_p)
|
|
|
316 |
{
|
|
|
317 |
os_ptr op = osp;
|
|
|
318 |
gs_parsed_file_name_t pname;
|
|
|
319 |
int code = parse_real_file_name(op, &pname, imemory, "deletefile");
|
|
|
320 |
|
|
|
321 |
if (code < 0)
|
|
|
322 |
return code;
|
|
|
323 |
if (pname.iodev == iodev_default) {
|
|
|
324 |
if ((code = check_file_permissions(i_ctx_p, pname.fname, pname.len,
|
|
|
325 |
"PermitFileControl")) < 0 &&
|
|
|
326 |
!file_is_tempfile(i_ctx_p, op)) {
|
|
|
327 |
return code;
|
|
|
328 |
}
|
|
|
329 |
}
|
|
|
330 |
code = (*pname.iodev->procs.delete_file)(pname.iodev, pname.fname);
|
|
|
331 |
gs_free_file_name(&pname, "deletefile");
|
|
|
332 |
if (code < 0)
|
|
|
333 |
return code;
|
|
|
334 |
pop(1);
|
|
|
335 |
return 0;
|
|
|
336 |
}
|
|
|
337 |
|
|
|
338 |
/* <template> <proc> <scratch> filenameforall - */
|
|
|
339 |
private int file_continue(i_ctx_t *);
|
|
|
340 |
private int file_cleanup(i_ctx_t *);
|
|
|
341 |
private int
|
|
|
342 |
zfilenameforall(i_ctx_t *i_ctx_p)
|
|
|
343 |
{
|
|
|
344 |
os_ptr op = osp;
|
|
|
345 |
file_enum *pfen;
|
|
|
346 |
gx_io_device *iodev = NULL;
|
|
|
347 |
gs_parsed_file_name_t pname;
|
|
|
348 |
int code = 0;
|
|
|
349 |
|
|
|
350 |
check_write_type(*op, t_string);
|
|
|
351 |
check_proc(op[-1]);
|
|
|
352 |
check_read_type(op[-2], t_string);
|
|
|
353 |
/* Push a mark, the iodev, devicenamelen, the scratch string, the enumerator, */
|
|
|
354 |
/* and the procedure, and invoke the continuation. */
|
|
|
355 |
check_estack(7);
|
|
|
356 |
/* Get the iodevice */
|
|
|
357 |
code = parse_file_name(op - 2, &pname, i_ctx_p->LockFilePermissions);
|
|
|
358 |
if (code < 0)
|
|
|
359 |
return code;
|
|
|
360 |
iodev = (pname.iodev == NULL) ? iodev_default : pname.iodev;
|
|
|
361 |
|
|
|
362 |
/* Check for several conditions that just cause us to return success */
|
|
|
363 |
if (pname.len == 0 || iodev->procs.enumerate_files == iodev_no_enumerate_files) {
|
|
|
364 |
pop(3);
|
|
|
365 |
return 0; /* no pattern, or device not found -- just return */
|
|
|
366 |
}
|
|
|
367 |
pfen = iodev->procs.enumerate_files(iodev, (const char *)pname.fname,
|
|
|
368 |
pname.len, imemory);
|
|
|
369 |
if (pfen == 0)
|
|
|
370 |
return_error(e_VMerror);
|
|
|
371 |
push_mark_estack(es_for, file_cleanup);
|
|
|
372 |
++esp;
|
|
|
373 |
make_istruct(esp, 0, iodev);
|
|
|
374 |
++esp;
|
|
|
375 |
make_int(esp, r_size(op-2) - pname.len);
|
|
|
376 |
*++esp = *op;
|
|
|
377 |
++esp;
|
|
|
378 |
make_istruct(esp, 0, pfen);
|
|
|
379 |
*++esp = op[-1];
|
|
|
380 |
pop(3);
|
|
|
381 |
code = file_continue(i_ctx_p);
|
|
|
382 |
return (code == o_pop_estack ? o_push_estack : code);
|
|
|
383 |
}
|
|
|
384 |
/* Continuation operator for enumerating files */
|
|
|
385 |
private int
|
|
|
386 |
file_continue(i_ctx_t *i_ctx_p)
|
|
|
387 |
{
|
|
|
388 |
os_ptr op = osp;
|
|
|
389 |
es_ptr pscratch = esp - 2;
|
|
|
390 |
file_enum *pfen = r_ptr(esp - 1, file_enum);
|
|
|
391 |
long devlen = esp[-3].value.intval;
|
|
|
392 |
gx_io_device *iodev = r_ptr(esp - 4, gx_io_device);
|
|
|
393 |
uint len = r_size(pscratch);
|
|
|
394 |
uint code;
|
|
|
395 |
|
|
|
396 |
if (len < devlen)
|
|
|
397 |
return_error(e_rangecheck); /* not even room for device len */
|
|
|
398 |
memcpy((char *)pscratch->value.bytes, iodev->dname, devlen);
|
|
|
399 |
code = iodev->procs.enumerate_next(pfen, (char *)pscratch->value.bytes + devlen,
|
|
|
400 |
len - devlen);
|
|
|
401 |
if (code == ~(uint) 0) { /* all done */
|
|
|
402 |
esp -= 5; /* pop proc, pfen, devlen, iodev , mark */
|
|
|
403 |
return o_pop_estack;
|
|
|
404 |
} else if (code > len) /* overran string */
|
|
|
405 |
return_error(e_rangecheck);
|
|
|
406 |
else {
|
|
|
407 |
push(1);
|
|
|
408 |
ref_assign(op, pscratch);
|
|
|
409 |
r_set_size(op, code + devlen);
|
|
|
410 |
push_op_estack(file_continue); /* come again */
|
|
|
411 |
*++esp = pscratch[2]; /* proc */
|
|
|
412 |
return o_push_estack;
|
|
|
413 |
}
|
|
|
414 |
}
|
|
|
415 |
/* Cleanup procedure for enumerating files */
|
|
|
416 |
private int
|
|
|
417 |
file_cleanup(i_ctx_t *i_ctx_p)
|
|
|
418 |
{
|
|
|
419 |
gx_io_device *iodev = r_ptr(esp + 2, gx_io_device);
|
|
|
420 |
|
|
|
421 |
iodev->procs.enumerate_close(r_ptr(esp + 5, file_enum));
|
|
|
422 |
return 0;
|
|
|
423 |
}
|
|
|
424 |
|
|
|
425 |
/* <string1> <string2> renamefile - */
|
|
|
426 |
private int
|
|
|
427 |
zrenamefile(i_ctx_t *i_ctx_p)
|
|
|
428 |
{
|
|
|
429 |
os_ptr op = osp;
|
|
|
430 |
gs_parsed_file_name_t pname1, pname2;
|
|
|
431 |
int code = parse_real_file_name(op - 1, &pname1, imemory,
|
|
|
432 |
"renamefile(from)");
|
|
|
433 |
|
|
|
434 |
if (code < 0)
|
|
|
435 |
return code;
|
|
|
436 |
pname2.fname = 0;
|
|
|
437 |
code = parse_real_file_name(op, &pname2, imemory, "renamefile(to)");
|
|
|
438 |
if (code >= 0) {
|
|
|
439 |
if (pname1.iodev != pname2.iodev ) {
|
|
|
440 |
if (pname1.iodev == iodev_default)
|
|
|
441 |
pname1.iodev = pname2.iodev;
|
|
|
442 |
if (pname2.iodev == iodev_default)
|
|
|
443 |
pname2.iodev = pname1.iodev;
|
|
|
444 |
}
|
|
|
445 |
if (pname1.iodev != pname2.iodev ||
|
|
|
446 |
(pname1.iodev == iodev_default &&
|
|
|
447 |
/*
|
|
|
448 |
* We require FileControl permissions on the source path
|
|
|
449 |
* unless it is a temporary file. Also, we require FileControl
|
|
|
450 |
* and FileWriting permissions to the destination file/path.
|
|
|
451 |
*/
|
|
|
452 |
((check_file_permissions(i_ctx_p, pname1.fname, pname1.len,
|
|
|
453 |
"PermitFileControl") < 0 &&
|
|
|
454 |
!file_is_tempfile(i_ctx_p, op - 1)) ||
|
|
|
455 |
(check_file_permissions(i_ctx_p, pname2.fname, pname2.len,
|
|
|
456 |
"PermitFileControl") < 0 ||
|
|
|
457 |
check_file_permissions(i_ctx_p, pname2.fname, pname2.len,
|
|
|
458 |
"PermitFileWriting") < 0 )))) {
|
|
|
459 |
code = gs_note_error(e_invalidfileaccess);
|
|
|
460 |
} else {
|
|
|
461 |
code = (*pname1.iodev->procs.rename_file)(pname1.iodev,
|
|
|
462 |
pname1.fname, pname2.fname);
|
|
|
463 |
}
|
|
|
464 |
}
|
|
|
465 |
gs_free_file_name(&pname2, "renamefile(to)");
|
|
|
466 |
gs_free_file_name(&pname1, "renamefile(from)");
|
|
|
467 |
if (code < 0)
|
|
|
468 |
return code;
|
|
|
469 |
pop(2);
|
|
|
470 |
return 0;
|
|
|
471 |
}
|
|
|
472 |
|
|
|
473 |
/* <file> status <open_bool> */
|
|
|
474 |
/* <string> status <pages> <bytes> <ref_time> <creation_time> true */
|
|
|
475 |
/* <string> status false */
|
|
|
476 |
private int
|
|
|
477 |
zstatus(i_ctx_t *i_ctx_p)
|
|
|
478 |
{
|
|
|
479 |
os_ptr op = osp;
|
|
|
480 |
|
|
|
481 |
switch (r_type(op)) {
|
|
|
482 |
case t_file:
|
|
|
483 |
{
|
|
|
484 |
stream *s;
|
|
|
485 |
|
|
|
486 |
make_bool(op, (file_is_valid(s, op) ? 1 : 0));
|
|
|
487 |
}
|
|
|
488 |
return 0;
|
|
|
489 |
case t_string:
|
|
|
490 |
{
|
|
|
491 |
gs_parsed_file_name_t pname;
|
|
|
492 |
struct stat fstat;
|
|
|
493 |
int code = parse_file_name(op, &pname, i_ctx_p->LockFilePermissions);
|
|
|
494 |
|
|
|
495 |
if (code < 0)
|
|
|
496 |
return code;
|
|
|
497 |
code = gs_terminate_file_name(&pname, imemory, "status");
|
|
|
498 |
if (code < 0)
|
|
|
499 |
return code;
|
|
|
500 |
code = (*pname.iodev->procs.file_status)(pname.iodev,
|
|
|
501 |
pname.fname, &fstat);
|
|
|
502 |
switch (code) {
|
|
|
503 |
case 0:
|
|
|
504 |
check_ostack(4);
|
|
|
505 |
/*
|
|
|
506 |
* Check to make sure that the file size fits into
|
|
|
507 |
* a PostScript integer. (On some systems, long is
|
|
|
508 |
* 32 bits, but file sizes are 64 bits.)
|
|
|
509 |
*/
|
|
|
510 |
push(4);
|
|
|
511 |
make_int(op - 4, stat_blocks(&fstat));
|
|
|
512 |
make_int(op - 3, fstat.st_size);
|
|
|
513 |
/*
|
|
|
514 |
* We can't check the value simply by using ==,
|
|
|
515 |
* because signed/unsigned == does the wrong thing.
|
|
|
516 |
* Instead, since integer assignment only keeps the
|
|
|
517 |
* bottom bits, we convert the values to double
|
|
|
518 |
* and then test for equality. This handles all
|
|
|
519 |
* cases of signed/unsigned or width mismatch.
|
|
|
520 |
*/
|
|
|
521 |
if ((double)op[-4].value.intval !=
|
|
|
522 |
(double)stat_blocks(&fstat) ||
|
|
|
523 |
(double)op[-3].value.intval !=
|
|
|
524 |
(double)fstat.st_size
|
|
|
525 |
)
|
|
|
526 |
return_error(e_limitcheck);
|
|
|
527 |
make_int(op - 2, fstat.st_mtime);
|
|
|
528 |
make_int(op - 1, fstat.st_ctime);
|
|
|
529 |
make_bool(op, 1);
|
|
|
530 |
break;
|
|
|
531 |
case e_undefinedfilename:
|
|
|
532 |
make_bool(op, 0);
|
|
|
533 |
code = 0;
|
|
|
534 |
}
|
|
|
535 |
gs_free_file_name(&pname, "status");
|
|
|
536 |
return code;
|
|
|
537 |
}
|
|
|
538 |
default:
|
|
|
539 |
return_op_typecheck(op);
|
|
|
540 |
}
|
|
|
541 |
}
|
|
|
542 |
|
|
|
543 |
/* ------ Non-standard extensions ------ */
|
|
|
544 |
|
|
|
545 |
/* <executable_file> .execfile - */
|
|
|
546 |
private int
|
|
|
547 |
zexecfile(i_ctx_t *i_ctx_p)
|
|
|
548 |
{
|
|
|
549 |
os_ptr op = osp;
|
|
|
550 |
|
|
|
551 |
check_type_access(*op, t_file, a_executable | a_read | a_execute);
|
|
|
552 |
check_estack(4); /* cleanup, file, finish, file */
|
|
|
553 |
push_mark_estack(es_other, execfile_cleanup);
|
|
|
554 |
*++esp = *op;
|
|
|
555 |
push_op_estack(execfile_finish);
|
|
|
556 |
return zexec(i_ctx_p);
|
|
|
557 |
}
|
|
|
558 |
/* Finish normally. */
|
|
|
559 |
private int
|
|
|
560 |
execfile_finish(i_ctx_t *i_ctx_p)
|
|
|
561 |
{
|
|
|
562 |
check_ostack(1);
|
|
|
563 |
esp -= 2;
|
|
|
564 |
execfile_cleanup(i_ctx_p);
|
|
|
565 |
return o_pop_estack;
|
|
|
566 |
}
|
|
|
567 |
/* Clean up by closing the file. */
|
|
|
568 |
private int
|
|
|
569 |
execfile_cleanup(i_ctx_t *i_ctx_p)
|
|
|
570 |
{
|
|
|
571 |
check_ostack(1);
|
|
|
572 |
*++osp = esp[2];
|
|
|
573 |
return zclosefile(i_ctx_p);
|
|
|
574 |
}
|
|
|
575 |
|
|
|
576 |
/* - .filenamelistseparator <string> */
|
|
|
577 |
private int
|
|
|
578 |
zfilenamelistseparator(i_ctx_t *i_ctx_p)
|
|
|
579 |
{
|
|
|
580 |
os_ptr op = osp;
|
|
|
581 |
|
|
|
582 |
push(1);
|
|
|
583 |
make_const_string(op, avm_foreign | a_readonly, 1,
|
|
|
584 |
(const byte *)&gp_file_name_list_separator);
|
|
|
585 |
return 0;
|
|
|
586 |
}
|
|
|
587 |
|
|
|
588 |
/* <name> .filenamesplit <dir> <base> <extension> */
|
|
|
589 |
private int
|
|
|
590 |
zfilenamesplit(i_ctx_t *i_ctx_p)
|
|
|
591 |
{
|
|
|
592 |
os_ptr op = osp;
|
|
|
593 |
|
|
|
594 |
check_read_type(*op, t_string);
|
|
|
595 |
/****** NOT IMPLEMENTED YET ******/
|
|
|
596 |
return_error(e_undefined);
|
|
|
597 |
}
|
|
|
598 |
|
|
|
599 |
/* <string> .libfile <file> true */
|
|
|
600 |
/* <string> .libfile <string> false */
|
|
|
601 |
private int
|
|
|
602 |
zlibfile(i_ctx_t *i_ctx_p)
|
|
|
603 |
{
|
|
|
604 |
os_ptr op = osp;
|
|
|
605 |
int code;
|
|
|
606 |
byte cname[gp_file_name_sizeof];
|
|
|
607 |
uint clen;
|
|
|
608 |
gs_parsed_file_name_t pname;
|
|
|
609 |
stream *s;
|
|
|
610 |
|
|
|
611 |
check_ostack(2);
|
|
|
612 |
code = parse_file_name(op, &pname, i_ctx_p->LockFilePermissions);
|
|
|
613 |
if (code < 0)
|
|
|
614 |
return code;
|
|
|
615 |
if (pname.iodev == NULL)
|
|
|
616 |
pname.iodev = iodev_default;
|
|
|
617 |
if (pname.iodev != iodev_default) { /* Non-OS devices don't have search paths (yet). */
|
|
|
618 |
code = zopen_file(i_ctx_p, &pname, "r", &s, imemory);
|
|
|
619 |
if (code >= 0) {
|
|
|
620 |
code = ssetfilename(s, op->value.const_bytes, r_size(op));
|
|
|
621 |
if (code < 0) {
|
|
|
622 |
sclose(s);
|
|
|
623 |
return_error(e_VMerror);
|
|
|
624 |
}
|
|
|
625 |
}
|
|
|
626 |
if (code < 0) {
|
|
|
627 |
push(1);
|
|
|
628 |
make_false(op);
|
|
|
629 |
return 0;
|
|
|
630 |
}
|
|
|
631 |
make_stream_file(op, s, "r");
|
|
|
632 |
} else {
|
|
|
633 |
ref fref;
|
|
|
634 |
|
|
|
635 |
code = lib_file_open(i_ctx_p->lib_path, i_ctx_p, pname.fname, pname.len, cname, sizeof(cname),
|
|
|
636 |
&clen, &fref, imemory);
|
|
|
637 |
if (code >= 0) {
|
|
|
638 |
s = fptr(&fref);
|
|
|
639 |
code = ssetfilename(s, cname, clen);
|
|
|
640 |
if (code < 0) {
|
|
|
641 |
sclose(s);
|
|
|
642 |
return_error(e_VMerror);
|
|
|
643 |
}
|
|
|
644 |
}
|
|
|
645 |
if (code < 0) {
|
|
|
646 |
if (code == e_VMerror || code == e_invalidfileaccess)
|
|
|
647 |
return code;
|
|
|
648 |
push(1);
|
|
|
649 |
make_false(op);
|
|
|
650 |
return 0;
|
|
|
651 |
}
|
|
|
652 |
ref_assign(op, &fref);
|
|
|
653 |
}
|
|
|
654 |
push(1);
|
|
|
655 |
make_true(op);
|
|
|
656 |
return 0;
|
|
|
657 |
}
|
|
|
658 |
|
|
|
659 |
/* A "simple" prefix is defined as a (possibly empty) string of
|
|
|
660 |
alphanumeric, underscore, and hyphen characters. */
|
|
|
661 |
private bool
|
|
|
662 |
prefix_is_simple(const char *pstr)
|
|
|
663 |
{
|
|
|
664 |
int i;
|
|
|
665 |
char c;
|
|
|
666 |
|
|
|
667 |
for (i = 0; (c = pstr[i]) != 0; i++) {
|
|
|
668 |
if (!(c == '-' || c == '_' || (c >= '0' && c <= '9') ||
|
|
|
669 |
(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')))
|
|
|
670 |
return false;
|
|
|
671 |
}
|
|
|
672 |
return true;
|
|
|
673 |
}
|
|
|
674 |
|
|
|
675 |
/* <prefix|null> <access_string> .tempfile <name_string> <file> */
|
|
|
676 |
private int
|
|
|
677 |
ztempfile(i_ctx_t *i_ctx_p)
|
|
|
678 |
{
|
|
|
679 |
os_ptr op = osp;
|
|
|
680 |
const char *pstr;
|
|
|
681 |
char fmode[4];
|
|
|
682 |
int code = parse_file_access_string(op, fmode);
|
|
|
683 |
char prefix[gp_file_name_sizeof];
|
|
|
684 |
char fname[gp_file_name_sizeof];
|
|
|
685 |
uint fnlen;
|
|
|
686 |
FILE *sfile;
|
|
|
687 |
stream *s;
|
|
|
688 |
byte *buf;
|
|
|
689 |
|
|
|
690 |
if (code < 0)
|
|
|
691 |
return code;
|
|
|
692 |
strcat(fmode, gp_fmode_binary_suffix);
|
|
|
693 |
if (r_has_type(op - 1, t_null))
|
|
|
694 |
pstr = gp_scratch_file_name_prefix;
|
|
|
695 |
else {
|
|
|
696 |
uint psize;
|
|
|
697 |
|
|
|
698 |
check_read_type(op[-1], t_string);
|
|
|
699 |
psize = r_size(op - 1);
|
|
|
700 |
if (psize >= gp_file_name_sizeof)
|
|
|
701 |
return_error(e_rangecheck);
|
|
|
702 |
memcpy(prefix, op[-1].value.const_bytes, psize);
|
|
|
703 |
prefix[psize] = 0;
|
|
|
704 |
pstr = prefix;
|
|
|
705 |
}
|
|
|
706 |
|
|
|
707 |
if (gp_file_name_is_absolute(pstr, strlen(pstr))) {
|
|
|
708 |
if (check_file_permissions(i_ctx_p, pstr, strlen(pstr),
|
|
|
709 |
"PermitFileWriting") < 0) {
|
|
|
710 |
return_error(e_invalidfileaccess);
|
|
|
711 |
}
|
|
|
712 |
} else if (!prefix_is_simple(pstr)) {
|
|
|
713 |
return_error(e_invalidfileaccess);
|
|
|
714 |
}
|
|
|
715 |
|
|
|
716 |
s = file_alloc_stream(imemory, "ztempfile(stream)");
|
|
|
717 |
if (s == 0)
|
|
|
718 |
return_error(e_VMerror);
|
|
|
719 |
buf = gs_alloc_bytes(imemory, file_default_buffer_size,
|
|
|
720 |
"ztempfile(buffer)");
|
|
|
721 |
if (buf == 0)
|
|
|
722 |
return_error(e_VMerror);
|
|
|
723 |
sfile = gp_open_scratch_file(pstr, fname, fmode);
|
|
|
724 |
if (sfile == 0) {
|
|
|
725 |
gs_free_object(imemory, buf, "ztempfile(buffer)");
|
|
|
726 |
return_error(e_invalidfileaccess);
|
|
|
727 |
}
|
|
|
728 |
fnlen = strlen(fname);
|
|
|
729 |
file_init_stream(s, sfile, fmode, buf, file_default_buffer_size);
|
|
|
730 |
code = ssetfilename(s, (const unsigned char*) fname, fnlen);
|
|
|
731 |
if (code < 0) {
|
|
|
732 |
sclose(s);
|
|
|
733 |
iodev_default->procs.delete_file(iodev_default, fname);
|
|
|
734 |
return_error(e_VMerror);
|
|
|
735 |
}
|
|
|
736 |
make_const_string(op - 1, a_readonly | icurrent_space, fnlen,
|
|
|
737 |
s->file_name.data);
|
|
|
738 |
make_stream_file(op, s, fmode);
|
|
|
739 |
return code;
|
|
|
740 |
}
|
|
|
741 |
|
|
|
742 |
/* ------ Initialization procedure ------ */
|
|
|
743 |
|
|
|
744 |
const op_def zfile_op_defs[] =
|
|
|
745 |
{
|
|
|
746 |
{"1deletefile", zdeletefile},
|
|
|
747 |
{"1.execfile", zexecfile},
|
|
|
748 |
{"2file", zfile},
|
|
|
749 |
{"3filenameforall", zfilenameforall},
|
|
|
750 |
{"0.filenamelistseparator", zfilenamelistseparator},
|
|
|
751 |
{"1.filenamesplit", zfilenamesplit},
|
|
|
752 |
{"1.libfile", zlibfile},
|
|
|
753 |
{"2renamefile", zrenamefile},
|
|
|
754 |
{"1status", zstatus},
|
|
|
755 |
{"2.tempfile", ztempfile},
|
|
|
756 |
/* Internal operators */
|
|
|
757 |
{"0%file_continue", file_continue},
|
|
|
758 |
{"0%execfile_finish", execfile_finish},
|
|
|
759 |
op_def_end(zfile_init)
|
|
|
760 |
};
|
|
|
761 |
|
|
|
762 |
/* ------ File name parsing ------ */
|
|
|
763 |
|
|
|
764 |
/* Parse a file name into device and individual name. */
|
|
|
765 |
/* See gsfname.c for details. */
|
|
|
766 |
private int
|
|
|
767 |
parse_file_name(const ref * op, gs_parsed_file_name_t * pfn, bool safemode)
|
|
|
768 |
{
|
|
|
769 |
int code;
|
|
|
770 |
|
|
|
771 |
check_read_type(*op, t_string);
|
|
|
772 |
code = gs_parse_file_name(pfn, (const char *)op->value.const_bytes,
|
|
|
773 |
r_size(op));
|
|
|
774 |
if (code < 0)
|
|
|
775 |
return code;
|
|
|
776 |
/*
|
|
|
777 |
* Check here for the %pipe device which is illegal when
|
|
|
778 |
* LockFilePermissions is true. In the future we might want to allow
|
|
|
779 |
* the %pipe device to be included on the PermitFile... paths, but
|
|
|
780 |
* for now it is simply disallowed.
|
|
|
781 |
*/
|
|
|
782 |
if (pfn->iodev && safemode && strcmp(pfn->iodev->dname, "%pipe%") == 0)
|
|
|
783 |
return e_invalidfileaccess;
|
|
|
784 |
return code;
|
|
|
785 |
}
|
|
|
786 |
|
|
|
787 |
/* Parse a real (non-device) file name and convert to a C string. */
|
|
|
788 |
/* See gsfname.c for details. */
|
|
|
789 |
private int
|
|
|
790 |
parse_real_file_name(const ref *op, gs_parsed_file_name_t *pfn,
|
|
|
791 |
gs_memory_t *mem, client_name_t cname)
|
|
|
792 |
{
|
|
|
793 |
check_read_type(*op, t_string);
|
|
|
794 |
return gs_parse_real_file_name(pfn, (const char *)op->value.const_bytes,
|
|
|
795 |
r_size(op), mem, cname);
|
|
|
796 |
}
|
|
|
797 |
|
|
|
798 |
/* Parse the access string for opening a file. */
|
|
|
799 |
/* [4] is for r/w, +, b, \0. */
|
|
|
800 |
private int
|
|
|
801 |
parse_file_access_string(const ref *op, char file_access[4])
|
|
|
802 |
{
|
|
|
803 |
const byte *astr;
|
|
|
804 |
|
|
|
805 |
check_read_type(*op, t_string);
|
|
|
806 |
astr = op->value.const_bytes;
|
|
|
807 |
switch (r_size(op)) {
|
|
|
808 |
case 2:
|
|
|
809 |
if (astr[1] != '+')
|
|
|
810 |
return_error(e_invalidfileaccess);
|
|
|
811 |
file_access[1] = '+';
|
|
|
812 |
file_access[2] = 0;
|
|
|
813 |
break;
|
|
|
814 |
case 1:
|
|
|
815 |
file_access[1] = 0;
|
|
|
816 |
break;
|
|
|
817 |
default:
|
|
|
818 |
return_error(e_invalidfileaccess);
|
|
|
819 |
}
|
|
|
820 |
switch (astr[0]) {
|
|
|
821 |
case 'r':
|
|
|
822 |
case 'w':
|
|
|
823 |
case 'a':
|
|
|
824 |
break;
|
|
|
825 |
default:
|
|
|
826 |
return_error(e_invalidfileaccess);
|
|
|
827 |
}
|
|
|
828 |
file_access[0] = astr[0];
|
|
|
829 |
return 0;
|
|
|
830 |
}
|
|
|
831 |
|
|
|
832 |
/* ------ Stream opening ------ */
|
|
|
833 |
|
|
|
834 |
/*
|
|
|
835 |
* Open a file specified by a parsed file name (which may be only a
|
|
|
836 |
* device).
|
|
|
837 |
*/
|
|
|
838 |
private int
|
|
|
839 |
zopen_file(i_ctx_t *i_ctx_p, const gs_parsed_file_name_t *pfn,
|
|
|
840 |
const char *file_access, stream **ps, gs_memory_t *mem)
|
|
|
841 |
{
|
|
|
842 |
gx_io_device *const iodev = pfn->iodev;
|
|
|
843 |
|
|
|
844 |
if (pfn->fname == NULL) /* just a device */
|
|
|
845 |
return iodev->procs.open_device(iodev, file_access, ps, mem);
|
|
|
846 |
else { /* file */
|
|
|
847 |
iodev_proc_open_file((*open_file)) = iodev->procs.open_file;
|
|
|
848 |
|
|
|
849 |
if (open_file == 0)
|
|
|
850 |
open_file = iodev_os_open_file;
|
|
|
851 |
/* Check OS files to make sure we allow the type of access */
|
|
|
852 |
if (open_file == iodev_os_open_file) {
|
|
|
853 |
int code = check_file_permissions(i_ctx_p, pfn->fname, pfn->len,
|
|
|
854 |
file_access[0] == 'r' ? "PermitFileReading" : "PermitFileWriting");
|
|
|
855 |
|
|
|
856 |
if (code < 0)
|
|
|
857 |
return code;
|
|
|
858 |
}
|
|
|
859 |
return open_file(iodev, pfn->fname, pfn->len, file_access, ps, mem);
|
|
|
860 |
}
|
|
|
861 |
}
|
|
|
862 |
|
|
|
863 |
/*
|
|
|
864 |
* Define the file_open procedure for the %os% IODevice (also used, as the
|
|
|
865 |
* default, for %pipe% and possibly others).
|
|
|
866 |
*/
|
|
|
867 |
private int
|
|
|
868 |
iodev_os_open_file(gx_io_device * iodev, const char *fname, uint len,
|
|
|
869 |
const char *file_access, stream ** ps, gs_memory_t * mem)
|
|
|
870 |
{
|
|
|
871 |
return file_open_stream(fname, len, file_access,
|
|
|
872 |
file_default_buffer_size, ps,
|
|
|
873 |
iodev, iodev->procs.fopen, mem);
|
|
|
874 |
}
|
|
|
875 |
|
|
|
876 |
/* Make a t_file reference to a stream. */
|
|
|
877 |
void
|
|
|
878 |
make_stream_file(ref * pfile, stream * s, const char *access)
|
|
|
879 |
{
|
|
|
880 |
uint attrs =
|
|
|
881 |
(access[1] == '+' ? a_write + a_read + a_execute : 0) |
|
|
|
882 |
imemory_space((gs_ref_memory_t *) s->memory);
|
|
|
883 |
|
|
|
884 |
if (access[0] == 'r') {
|
|
|
885 |
make_file(pfile, attrs | (a_read | a_execute), s->read_id, s);
|
|
|
886 |
s->write_id = 0;
|
|
|
887 |
} else {
|
|
|
888 |
make_file(pfile, attrs | a_write, s->write_id, s);
|
|
|
889 |
s->read_id = 0;
|
|
|
890 |
}
|
|
|
891 |
}
|
|
|
892 |
|
|
|
893 |
private gp_file_name_combine_result
|
|
|
894 |
gp_file_name_combine_patch(const char *prefix, uint plen, const char *fname, uint flen,
|
|
|
895 |
bool no_sibling, char *buffer, uint *blen)
|
|
|
896 |
{
|
|
|
897 |
return gp_file_name_combine(prefix, plen, fname, flen, no_sibling, buffer, blen);
|
|
|
898 |
}
|
|
|
899 |
|
|
|
900 |
/* Prepare a stream with a file name. */
|
|
|
901 |
/* Return 0 if successful, error code if not. */
|
|
|
902 |
/* On a successful return, the C file name is in the stream buffer. */
|
|
|
903 |
/* If fname==0, set up stream, and buffer. */
|
|
|
904 |
private int
|
|
|
905 |
file_prepare_stream(const char *fname, uint len, const char *file_access,
|
|
|
906 |
uint buffer_size, stream ** ps, char fmode[4],
|
|
|
907 |
gx_io_device *iodev, gs_memory_t *mem)
|
|
|
908 |
{
|
|
|
909 |
byte *buffer;
|
|
|
910 |
register stream *s;
|
|
|
911 |
|
|
|
912 |
/* Open the file, always in binary mode. */
|
|
|
913 |
strcpy(fmode, file_access);
|
|
|
914 |
strcat(fmode, gp_fmode_binary_suffix);
|
|
|
915 |
if (buffer_size == 0)
|
|
|
916 |
buffer_size = file_default_buffer_size;
|
|
|
917 |
if (len >= buffer_size) /* we copy the file name into the buffer */
|
|
|
918 |
return_error(e_limitcheck);
|
|
|
919 |
/* Allocate the stream first, since it persists */
|
|
|
920 |
/* even after the file has been closed. */
|
|
|
921 |
s = file_alloc_stream(mem, "file_prepare_stream");
|
|
|
922 |
if (s == 0)
|
|
|
923 |
return_error(e_VMerror);
|
|
|
924 |
/* Allocate the buffer. */
|
|
|
925 |
buffer = gs_alloc_bytes(mem, buffer_size, "file_prepare_stream(buffer)");
|
|
|
926 |
if (buffer == 0)
|
|
|
927 |
return_error(e_VMerror);
|
|
|
928 |
if (fname != 0) {
|
|
|
929 |
memcpy(buffer, fname, len);
|
|
|
930 |
buffer[len] = 0; /* terminate string */
|
|
|
931 |
} else
|
|
|
932 |
buffer[0] = 0; /* safety */
|
|
|
933 |
s->cbuf = buffer;
|
|
|
934 |
s->bsize = s->cbsize = buffer_size;
|
|
|
935 |
*ps = s;
|
|
|
936 |
return 0;
|
|
|
937 |
}
|
|
|
938 |
|
|
|
939 |
private int
|
|
|
940 |
check_file_permissions_aux(i_ctx_t *i_ctx_p, char *fname, uint flen)
|
|
|
941 |
{ /* i_ctx_p is NULL running init files. */
|
|
|
942 |
/* fname must be reduced. */
|
|
|
943 |
if (i_ctx_p == NULL)
|
|
|
944 |
return 0;
|
|
|
945 |
if (check_file_permissions_reduced(i_ctx_p, fname, flen, "PermitFileReading") < 0)
|
|
|
946 |
return_error(e_invalidfileaccess);
|
|
|
947 |
return 0;
|
|
|
948 |
}
|
|
|
949 |
|
|
|
950 |
|
|
|
951 |
/* The startup code calls this to open @-files. */
|
|
|
952 |
private int
|
|
|
953 |
lib_fopen_with_libpath(gs_file_path_ptr lib_path,
|
|
|
954 |
const gs_memory_t *mem,
|
|
|
955 |
i_ctx_t *i_ctx_p,
|
|
|
956 |
gx_io_device *iodev,
|
|
|
957 |
const char *fname, uint flen, char fmode[4], char *buffer, int blen,
|
|
|
958 |
FILE **file)
|
|
|
959 |
{ /* i_ctx_p is NULL running init files.
|
|
|
960 |
* lib_path and mem are never NULL
|
|
|
961 |
*/
|
|
|
962 |
bool starting_arg_file = false;
|
|
|
963 |
bool search_with_no_combine = false;
|
|
|
964 |
bool search_with_combine = false;
|
|
|
965 |
|
|
|
966 |
if (i_ctx_p != NULL) {
|
|
|
967 |
starting_arg_file = i_ctx_p->starting_arg_file;
|
|
|
968 |
i_ctx_p->starting_arg_file = false;
|
|
|
969 |
} else
|
|
|
970 |
starting_arg_file = true;
|
|
|
971 |
if (gp_file_name_is_absolute(fname, flen)) {
|
|
|
972 |
search_with_no_combine = true;
|
|
|
973 |
search_with_combine = false;
|
|
|
974 |
} else {
|
|
|
975 |
search_with_no_combine = starting_arg_file;
|
|
|
976 |
search_with_combine = true;
|
|
|
977 |
}
|
|
|
978 |
if (search_with_no_combine) {
|
|
|
979 |
uint blen1 = blen;
|
|
|
980 |
|
|
|
981 |
if (gp_file_name_reduce(fname, flen, buffer, &blen1) != gp_combine_success)
|
|
|
982 |
goto skip;
|
|
|
983 |
if (iodev->procs.fopen(iodev, buffer, fmode, file,
|
|
|
984 |
buffer, blen) == 0) {
|
|
|
985 |
if (starting_arg_file ||
|
|
|
986 |
check_file_permissions_aux(i_ctx_p, buffer, blen1) >= 0)
|
|
|
987 |
return 0;
|
|
|
988 |
iodev->procs.fclose(iodev, *file);
|
|
|
989 |
*file = NULL;
|
|
|
990 |
return_error(e_invalidfileaccess);
|
|
|
991 |
} else
|
|
|
992 |
*file = NULL;
|
|
|
993 |
skip:;
|
|
|
994 |
}
|
|
|
995 |
if (search_with_combine) {
|
|
|
996 |
const gs_file_path *pfpath = lib_path;
|
|
|
997 |
uint pi;
|
|
|
998 |
|
|
|
999 |
for (pi = 0; pi < r_size(&pfpath->list); ++pi) {
|
|
|
1000 |
const ref *prdir = pfpath->list.value.refs + pi;
|
|
|
1001 |
const char *pstr = (const char *)prdir->value.const_bytes;
|
|
|
1002 |
uint plen = r_size(prdir), blen1 = blen;
|
|
|
1003 |
|
|
|
1004 |
gp_file_name_combine_result r = gp_file_name_combine_patch(pstr, plen,
|
|
|
1005 |
fname, flen, false, buffer, &blen1);
|
|
|
1006 |
if (r != gp_combine_success)
|
|
|
1007 |
continue;
|
|
|
1008 |
if (iodev->procs.fopen(iodev, buffer, fmode, file,
|
|
|
1009 |
buffer, blen) == 0) {
|
|
|
1010 |
if (starting_arg_file ||
|
|
|
1011 |
check_file_permissions_aux(i_ctx_p, buffer, blen1) >= 0)
|
|
|
1012 |
return 0;
|
|
|
1013 |
iodev->procs.fclose(iodev, *file);
|
|
|
1014 |
*file = NULL;
|
|
|
1015 |
return_error(e_invalidfileaccess);
|
|
|
1016 |
}
|
|
|
1017 |
*file = NULL; /* safety */
|
|
|
1018 |
}
|
|
|
1019 |
}
|
|
|
1020 |
return_error(e_undefinedfilename);
|
|
|
1021 |
}
|
|
|
1022 |
|
|
|
1023 |
/* The startup code calls this to open @-files. */
|
|
|
1024 |
FILE *
|
|
|
1025 |
lib_fopen(const gs_file_path_ptr pfpath, const gs_memory_t *mem, const char *fname)
|
|
|
1026 |
{
|
|
|
1027 |
/* We need a buffer to hold the expanded file name. */
|
|
|
1028 |
char buffer[gp_file_name_sizeof];
|
|
|
1029 |
/* We can't count on the IODevice table to have been initialized yet. */
|
|
|
1030 |
/* Allocate a copy of the default IODevice. */
|
|
|
1031 |
gx_io_device iodev_default_copy = *gx_io_device_table[0];
|
|
|
1032 |
char fmode[3] = "r";
|
|
|
1033 |
FILE *file = NULL;
|
|
|
1034 |
|
|
|
1035 |
strcat(fmode, gp_fmode_binary_suffix);
|
|
|
1036 |
lib_fopen_with_libpath(pfpath, mem, NULL, &iodev_default_copy, fname, strlen(fname),
|
|
|
1037 |
fmode, buffer, sizeof(buffer), &file);
|
|
|
1038 |
return file;
|
|
|
1039 |
}
|
|
|
1040 |
|
|
|
1041 |
/* Open a file stream on an OS file and create a file object, */
|
|
|
1042 |
/* using the search paths. */
|
|
|
1043 |
/* The startup code calls this to open the initialization file gs_init.ps. */
|
|
|
1044 |
int
|
|
|
1045 |
lib_file_open(const gs_file_path_ptr pfpath,
|
|
|
1046 |
i_ctx_t *i_ctx_p, const char *fname, uint len, byte * cname, uint max_clen,
|
|
|
1047 |
uint * pclen, ref * pfile, gs_memory_t *mem)
|
|
|
1048 |
{ /* i_ctx_p is NULL running init files. */
|
|
|
1049 |
stream *s;
|
|
|
1050 |
int code;
|
|
|
1051 |
char fmode[4]; /* r/w/a, [+], [b], null */
|
|
|
1052 |
char *buffer;
|
|
|
1053 |
uint blen;
|
|
|
1054 |
gx_io_device *iodev = iodev_default;
|
|
|
1055 |
FILE *file;
|
|
|
1056 |
|
|
|
1057 |
code = file_prepare_stream(fname, len, "r", file_default_buffer_size,
|
|
|
1058 |
&s, fmode, iodev, mem);
|
|
|
1059 |
if (code < 0)
|
|
|
1060 |
return code;
|
|
|
1061 |
if (fname == 0)
|
|
|
1062 |
return 0;
|
|
|
1063 |
buffer = (char *)s->cbuf;
|
|
|
1064 |
code = lib_fopen_with_libpath(pfpath, mem, i_ctx_p,
|
|
|
1065 |
iodev, fname, len, fmode, buffer, s->bsize, &file);
|
|
|
1066 |
if (code < 0) {
|
|
|
1067 |
s->cbuf = NULL;
|
|
|
1068 |
s->bsize = s->cbsize = 0;
|
|
|
1069 |
gs_free_object(mem, buffer, "lib_file_open");
|
|
|
1070 |
return code;
|
|
|
1071 |
}
|
|
|
1072 |
file_init_stream(s, file, fmode, (byte *)buffer, s->bsize);
|
|
|
1073 |
/* Get the name from the stream buffer. */
|
|
|
1074 |
blen = strlen(buffer);
|
|
|
1075 |
if (blen > max_clen) {
|
|
|
1076 |
sclose(s);
|
|
|
1077 |
return_error(e_limitcheck);
|
|
|
1078 |
}
|
|
|
1079 |
memcpy(cname, buffer, blen);
|
|
|
1080 |
*pclen = blen;
|
|
|
1081 |
make_stream_file(pfile, s, "r");
|
|
|
1082 |
return 0;
|
|
|
1083 |
}
|
|
|
1084 |
|
|
|
1085 |
/* Open a file stream that reads a string. */
|
|
|
1086 |
/* (This is currently used only by the ccinit feature.) */
|
|
|
1087 |
/* The string must be allocated in non-garbage-collectable (foreign) space. */
|
|
|
1088 |
int
|
|
|
1089 |
file_read_string(const byte *str, uint len, ref *pfile, gs_ref_memory_t *imem)
|
|
|
1090 |
{
|
|
|
1091 |
stream *s = file_alloc_stream((gs_memory_t *)imem, "file_read_string");
|
|
|
1092 |
|
|
|
1093 |
if (s == 0)
|
|
|
1094 |
return_error(e_VMerror);
|
|
|
1095 |
sread_string(s, str, len);
|
|
|
1096 |
s->foreign = 1;
|
|
|
1097 |
s->write_id = 0;
|
|
|
1098 |
make_file(pfile, a_readonly | imemory_space(imem), s->read_id, s);
|
|
|
1099 |
s->save_close = s->procs.close;
|
|
|
1100 |
s->procs.close = file_close_disable;
|
|
|
1101 |
return 0;
|
|
|
1102 |
}
|
|
|
1103 |
|
|
|
1104 |
/*
|
|
|
1105 |
* Set up a file stream on an OS file. The caller has allocated the
|
|
|
1106 |
* stream and buffer.
|
|
|
1107 |
*/
|
|
|
1108 |
private void
|
|
|
1109 |
file_init_stream(stream *s, FILE *file, const char *fmode, byte *buffer,
|
|
|
1110 |
uint buffer_size)
|
|
|
1111 |
{
|
|
|
1112 |
switch (fmode[0]) {
|
|
|
1113 |
case 'a':
|
|
|
1114 |
sappend_file(s, file, buffer, buffer_size);
|
|
|
1115 |
break;
|
|
|
1116 |
case 'r':
|
|
|
1117 |
/* Defeat buffering for terminals. */
|
|
|
1118 |
{
|
|
|
1119 |
struct stat rstat;
|
|
|
1120 |
|
|
|
1121 |
fstat(fileno(file), &rstat);
|
|
|
1122 |
sread_file(s, file, buffer,
|
|
|
1123 |
(S_ISCHR(rstat.st_mode) ? 1 : buffer_size));
|
|
|
1124 |
}
|
|
|
1125 |
break;
|
|
|
1126 |
case 'w':
|
|
|
1127 |
swrite_file(s, file, buffer, buffer_size);
|
|
|
1128 |
}
|
|
|
1129 |
if (fmode[1] == '+')
|
|
|
1130 |
s->file_modes |= s_mode_read | s_mode_write;
|
|
|
1131 |
s->save_close = s->procs.close;
|
|
|
1132 |
s->procs.close = file_close_file;
|
|
|
1133 |
}
|
|
|
1134 |
|
|
|
1135 |
/* Open a file stream, optionally on an OS file. */
|
|
|
1136 |
/* Return 0 if successful, error code if not. */
|
|
|
1137 |
/* On a successful return, the C file name is in the stream buffer. */
|
|
|
1138 |
/* If fname==0, set up the file entry, stream, and buffer, */
|
|
|
1139 |
/* but don't open an OS file or initialize the stream. */
|
|
|
1140 |
int
|
|
|
1141 |
file_open_stream(const char *fname, uint len, const char *file_access,
|
|
|
1142 |
uint buffer_size, stream ** ps, gx_io_device *iodev,
|
|
|
1143 |
iodev_proc_fopen_t fopen_proc, gs_memory_t *mem)
|
|
|
1144 |
{
|
|
|
1145 |
int code;
|
|
|
1146 |
FILE *file;
|
|
|
1147 |
char fmode[4]; /* r/w/a, [+], [b], null */
|
|
|
1148 |
|
|
|
1149 |
if (!iodev)
|
|
|
1150 |
iodev = iodev_default;
|
|
|
1151 |
code = file_prepare_stream(fname, len, file_access, buffer_size, ps, fmode,
|
|
|
1152 |
(!iodev ? iodev_default : iodev), mem);
|
|
|
1153 |
if (code < 0)
|
|
|
1154 |
return code;
|
|
|
1155 |
if (fname == 0)
|
|
|
1156 |
return 0;
|
|
|
1157 |
code = (*fopen_proc)(iodev, (char *)(*ps)->cbuf, fmode, &file,
|
|
|
1158 |
(char *)(*ps)->cbuf, (*ps)->bsize);
|
|
|
1159 |
if (code < 0)
|
|
|
1160 |
return code;
|
|
|
1161 |
file_init_stream(*ps, file, fmode, (*ps)->cbuf, (*ps)->bsize);
|
|
|
1162 |
return 0;
|
|
|
1163 |
}
|
|
|
1164 |
|
|
|
1165 |
/* Report an error by storing it in the stream's error_string. */
|
|
|
1166 |
int
|
|
|
1167 |
filter_report_error(stream_state * st, const char *str)
|
|
|
1168 |
{
|
|
|
1169 |
if_debug1('s', "[s]stream error: %s\n", str);
|
|
|
1170 |
strncpy(st->error_string, str, STREAM_MAX_ERROR_STRING);
|
|
|
1171 |
/* Ensure null termination. */
|
|
|
1172 |
st->error_string[STREAM_MAX_ERROR_STRING] = 0;
|
|
|
1173 |
return 0;
|
|
|
1174 |
}
|
|
|
1175 |
|
|
|
1176 |
/* Open a file stream for a filter. */
|
|
|
1177 |
int
|
|
|
1178 |
filter_open(const char *file_access, uint buffer_size, ref * pfile,
|
|
|
1179 |
const stream_procs * procs, const stream_template * template,
|
|
|
1180 |
const stream_state * st, gs_memory_t *mem)
|
|
|
1181 |
{
|
|
|
1182 |
stream *s;
|
|
|
1183 |
uint ssize = gs_struct_type_size(template->stype);
|
|
|
1184 |
stream_state *sst = 0;
|
|
|
1185 |
int code;
|
|
|
1186 |
|
|
|
1187 |
if (template->stype != &st_stream_state) {
|
|
|
1188 |
sst = s_alloc_state(mem, template->stype, "filter_open(stream_state)");
|
|
|
1189 |
if (sst == 0)
|
|
|
1190 |
return_error(e_VMerror);
|
|
|
1191 |
}
|
|
|
1192 |
code = file_open_stream((char *)0, 0, file_access, buffer_size, &s,
|
|
|
1193 |
(gx_io_device *)0, (iodev_proc_fopen_t)0, mem);
|
|
|
1194 |
if (code < 0) {
|
|
|
1195 |
gs_free_object(mem, sst, "filter_open(stream_state)");
|
|
|
1196 |
return code;
|
|
|
1197 |
}
|
|
|
1198 |
s_std_init(s, s->cbuf, s->bsize, procs,
|
|
|
1199 |
(*file_access == 'r' ? s_mode_read : s_mode_write));
|
|
|
1200 |
s->procs.process = template->process;
|
|
|
1201 |
s->save_close = s->procs.close;
|
|
|
1202 |
s->procs.close = file_close_file;
|
|
|
1203 |
if (sst == 0) {
|
|
|
1204 |
/* This stream doesn't have any state of its own. */
|
|
|
1205 |
/* Hack: use the stream itself as the state. */
|
|
|
1206 |
sst = (stream_state *) s;
|
|
|
1207 |
} else if (st != 0) /* might not have client parameters */
|
|
|
1208 |
memcpy(sst, st, ssize);
|
|
|
1209 |
s->state = sst;
|
|
|
1210 |
s_init_state(sst, template, mem);
|
|
|
1211 |
sst->report_error = filter_report_error;
|
|
|
1212 |
|
|
|
1213 |
if (template->init != 0) {
|
|
|
1214 |
code = (*template->init)(sst);
|
|
|
1215 |
if (code < 0) {
|
|
|
1216 |
gs_free_object(mem, sst, "filter_open(stream_state)");
|
|
|
1217 |
gs_free_object(mem, s->cbuf, "filter_open(buffer)");
|
|
|
1218 |
return code;
|
|
|
1219 |
}
|
|
|
1220 |
}
|
|
|
1221 |
make_stream_file(pfile, s, file_access);
|
|
|
1222 |
return 0;
|
|
|
1223 |
}
|
|
|
1224 |
|
|
|
1225 |
/* Allocate and return a file stream. */
|
|
|
1226 |
/* Return 0 if the allocation failed. */
|
|
|
1227 |
/* The stream is initialized to an invalid state, so the caller need not */
|
|
|
1228 |
/* worry about cleaning up if a later step in opening the stream fails. */
|
|
|
1229 |
stream *
|
|
|
1230 |
file_alloc_stream(gs_memory_t * mem, client_name_t cname)
|
|
|
1231 |
{
|
|
|
1232 |
stream *s;
|
|
|
1233 |
gs_ref_memory_t *imem = 0;
|
|
|
1234 |
|
|
|
1235 |
/*
|
|
|
1236 |
* HACK: Figure out whether this is a gs_ref_memory_t.
|
|
|
1237 |
* Avoiding this hack would require rippling a change
|
|
|
1238 |
* from gs_memory_t to gs_ref_memory_t into the open_file and
|
|
|
1239 |
* open_device procedures of gx_io_device, which in turn would
|
|
|
1240 |
* impact other things we don't want to change.
|
|
|
1241 |
*/
|
|
|
1242 |
if (mem->procs.free_object == gs_ref_memory_procs.free_object)
|
|
|
1243 |
imem = (gs_ref_memory_t *) mem;
|
|
|
1244 |
|
|
|
1245 |
if (imem) {
|
|
|
1246 |
/* Look first for a free stream allocated at this level. */
|
|
|
1247 |
s = imem->streams;
|
|
|
1248 |
while (s != 0) {
|
|
|
1249 |
if (!s_is_valid(s) && s->read_id != 0 /* i.e. !overflowed */ ) {
|
|
|
1250 |
s->is_temp = 0; /* not a temp stream */
|
|
|
1251 |
return s;
|
|
|
1252 |
}
|
|
|
1253 |
s = s->next;
|
|
|
1254 |
}
|
|
|
1255 |
}
|
|
|
1256 |
s = s_alloc(mem, cname);
|
|
|
1257 |
if (s == 0)
|
|
|
1258 |
return 0;
|
|
|
1259 |
s_init_ids(s);
|
|
|
1260 |
s->is_temp = 0; /* not a temp stream */
|
|
|
1261 |
/*
|
|
|
1262 |
* Disable the stream now (in case we can't open the file,
|
|
|
1263 |
* or a filter init procedure fails) so that `restore' won't
|
|
|
1264 |
* crash when it tries to close open files.
|
|
|
1265 |
*/
|
|
|
1266 |
s_disable(s);
|
|
|
1267 |
if (imem) {
|
|
|
1268 |
/* Add s to the list of files. */
|
|
|
1269 |
if (imem->streams != 0)
|
|
|
1270 |
imem->streams->prev = s;
|
|
|
1271 |
s->next = imem->streams;
|
|
|
1272 |
imem->streams = s;
|
|
|
1273 |
} else {
|
|
|
1274 |
s->next = 0;
|
|
|
1275 |
}
|
|
|
1276 |
s->prev = 0;
|
|
|
1277 |
return s;
|
|
|
1278 |
}
|
|
|
1279 |
|
|
|
1280 |
/* ------ Stream closing ------ */
|
|
|
1281 |
|
|
|
1282 |
/*
|
|
|
1283 |
* Finish closing a file stream. This used to check whether it was
|
|
|
1284 |
* currentfile, but we don't have to do this any longer. This replaces the
|
|
|
1285 |
* close procedure for the std* streams, which cannot actually be closed.
|
|
|
1286 |
*
|
|
|
1287 |
* This is exported for ziodev.c. */
|
|
|
1288 |
int
|
|
|
1289 |
file_close_finish(stream * s)
|
|
|
1290 |
{
|
|
|
1291 |
return 0;
|
|
|
1292 |
}
|
|
|
1293 |
|
|
|
1294 |
/*
|
|
|
1295 |
* Close a file stream, but don't deallocate the buffer. This replaces the
|
|
|
1296 |
* close procedure for %lineedit and %statementedit. (This is WRONG: these
|
|
|
1297 |
* streams should allocate a new buffer each time they are opened, but that
|
|
|
1298 |
* would overstress the allocator right now.) This is exported for ziodev.c.
|
|
|
1299 |
* This also replaces the close procedure for the string-reading stream
|
|
|
1300 |
* created for gs_run_string.
|
|
|
1301 |
*/
|
|
|
1302 |
int
|
|
|
1303 |
file_close_disable(stream * s)
|
|
|
1304 |
{
|
|
|
1305 |
int code = (*s->save_close)(s);
|
|
|
1306 |
|
|
|
1307 |
if (code)
|
|
|
1308 |
return code;
|
|
|
1309 |
/* Increment the IDs to prevent further access. */
|
|
|
1310 |
s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
|
|
|
1311 |
return file_close_finish(s);
|
|
|
1312 |
}
|
|
|
1313 |
|
|
|
1314 |
/* Close a file stream. This replaces the close procedure in the stream */
|
|
|
1315 |
/* for normal (OS) files and for filters. */
|
|
|
1316 |
int
|
|
|
1317 |
file_close_file(stream * s)
|
|
|
1318 |
{
|
|
|
1319 |
stream *stemp = s->strm;
|
|
|
1320 |
gs_memory_t *mem;
|
|
|
1321 |
int code = file_close_disable(s);
|
|
|
1322 |
|
|
|
1323 |
if (code)
|
|
|
1324 |
return code;
|
|
|
1325 |
/*
|
|
|
1326 |
* Check for temporary streams created for filters.
|
|
|
1327 |
* There may be more than one in the case of a procedure-based filter,
|
|
|
1328 |
* or if we created an intermediate stream to ensure
|
|
|
1329 |
* a large enough buffer. Note that these streams may have been
|
|
|
1330 |
* allocated by file_alloc_stream, so we mustn't free them.
|
|
|
1331 |
*/
|
|
|
1332 |
while (stemp != 0 && stemp->is_temp != 0) {
|
|
|
1333 |
stream *snext = stemp->strm;
|
|
|
1334 |
|
|
|
1335 |
mem = stemp->memory;
|
|
|
1336 |
if (stemp->is_temp > 1)
|
|
|
1337 |
gs_free_object(mem, stemp->cbuf,
|
|
|
1338 |
"file_close(temp stream buffer)");
|
|
|
1339 |
s_disable(stemp);
|
|
|
1340 |
stemp = snext;
|
|
|
1341 |
}
|
|
|
1342 |
mem = s->memory;
|
|
|
1343 |
gs_free_object(mem, s->cbuf, "file_close(buffer)");
|
|
|
1344 |
if (s->close_strm && stemp != 0)
|
|
|
1345 |
return sclose(stemp);
|
|
|
1346 |
return 0;
|
|
|
1347 |
}
|
|
|
1348 |
|
|
|
1349 |
/* Close a file object. */
|
|
|
1350 |
/* This is exported only for gsmain.c. */
|
|
|
1351 |
int
|
|
|
1352 |
file_close(ref * pfile)
|
|
|
1353 |
{
|
|
|
1354 |
stream *s;
|
|
|
1355 |
|
|
|
1356 |
if (file_is_valid(s, pfile)) { /* closing a closed file is a no-op */
|
|
|
1357 |
if (sclose(s))
|
|
|
1358 |
return_error(e_ioerror);
|
|
|
1359 |
}
|
|
|
1360 |
return 0;
|
|
|
1361 |
}
|