/src/ghostpdl/base/gslibctx.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2022 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., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | /* |
17 | | Some notes on the structure here: |
18 | | |
19 | | At the top level, we have 'instances' of Ghostscript (or GPL). |
20 | | Here, 'instance' is short for 'instance of the Ghostscript API'. |
21 | | Each instance is returned by 'gsapi_new_instance'. Every new |
22 | | instance gets a unique gs_lib_ctx_core_t. Each instance can be |
23 | | called from any number of threads, but only from one thread at |
24 | | a time! |
25 | | |
26 | | Each instance of Ghostscript owns one or more interpreters. |
27 | | Each interpreter gets a unique gs_lib_ctx_t, that shares the |
28 | | instance's gs_lib_ctx_core_t. |
29 | | |
30 | | Each interpreter (by which we include the graphics library |
31 | | called by that interpreter) can make multiple gs_memory_t's. |
32 | | Certainly, every simultaneous (rendering) thread created by by |
33 | | the interpreter will get a unique gs_memory_t. These |
34 | | gs_memory_t's share the same gs_lib_ctx_t (and hence the same |
35 | | gs_lib_ctx_core_t). |
36 | | */ |
37 | | |
38 | | |
39 | | /* library context functionality for ghostscript |
40 | | * api callers get a gs_main_instance |
41 | | */ |
42 | | |
43 | | /* Capture stdin/out/err before gs.h redefines them. */ |
44 | | #include "stdio_.h" |
45 | | #include "string_.h" /* memset */ |
46 | | #include "gp.h" |
47 | | #include "gpmisc.h" |
48 | | #include "gsicc_manage.h" |
49 | | #include "gserrors.h" |
50 | | #include "gscdefs.h" /* for gs_lib_device_list */ |
51 | | #include "gsstruct.h" /* for gs_gc_root_t */ |
52 | | #ifdef WITH_CAL |
53 | | #include "cal.h" |
54 | | #endif |
55 | | #include "gsargs.h" |
56 | | #include "globals.h" |
57 | | |
58 | | /* Include the extern for the device list. */ |
59 | | extern_gs_lib_device_list(); |
60 | | |
61 | | static void |
62 | | gs_lib_ctx_get_real_stdio(FILE **in, FILE **out, FILE **err) |
63 | 683 | { |
64 | 683 | *in = stdin; |
65 | 683 | *out = stdout; |
66 | 683 | *err = stderr; |
67 | 683 | } |
68 | | |
69 | | #include "gslibctx.h" |
70 | | #include "gsmemory.h" |
71 | | |
72 | | /* This sets the directory to prepend to the ICC profile names specified for |
73 | | defaultgray, defaultrgb, defaultcmyk, proofing, linking, named color and device */ |
74 | | int |
75 | | gs_lib_ctx_set_icc_directory(const gs_memory_t *mem_gc, const char* pname, |
76 | | int dir_namelen) |
77 | 4.12k | { |
78 | 4.12k | char *result; |
79 | 4.12k | gs_lib_ctx_t *p_ctx = mem_gc->gs_lib_ctx; |
80 | 4.12k | gs_memory_t *p_ctx_mem = p_ctx->memory; |
81 | | |
82 | | /* If it is already set and the incoming is the default then don't set |
83 | | as we are coming from a VMreclaim which is trying to reset the user |
84 | | parameter */ |
85 | 4.12k | if (p_ctx->profiledir != NULL && strcmp(pname,DEFAULT_DIR_ICC) == 0) { |
86 | 1.36k | return 0; |
87 | 1.36k | } |
88 | 2.75k | if (p_ctx->profiledir != NULL && p_ctx->profiledir_len > 0) { |
89 | 2.07k | if (strncmp(pname, p_ctx->profiledir, p_ctx->profiledir_len) == 0) { |
90 | 1.38k | return 0; |
91 | 1.38k | } |
92 | 683 | gs_free_object(p_ctx_mem, p_ctx->profiledir, |
93 | 683 | "gs_lib_ctx_set_icc_directory"); |
94 | 683 | p_ctx->profiledir = NULL; |
95 | 683 | p_ctx->profiledir_len = 0; |
96 | 683 | } |
97 | | /* User param string. Must allocate in non-gc memory */ |
98 | 1.36k | result = (char*) gs_alloc_bytes(p_ctx_mem, dir_namelen+1, |
99 | 1.36k | "gs_lib_ctx_set_icc_directory"); |
100 | 1.36k | if (result == NULL) { |
101 | 0 | return gs_error_VMerror; |
102 | 0 | } |
103 | 1.36k | strcpy(result, pname); |
104 | 1.36k | p_ctx->profiledir = result; |
105 | 1.36k | p_ctx->profiledir_len = dir_namelen; |
106 | 1.36k | return 0; |
107 | 1.36k | } |
108 | | |
109 | | /* Sets/Gets the string containing the list of default devices we should try */ |
110 | | int |
111 | | gs_lib_ctx_set_default_device_list(const gs_memory_t *mem, const char* dev_list_str, |
112 | | int list_str_len) |
113 | 683 | { |
114 | 683 | char *result; |
115 | 683 | gs_lib_ctx_t *p_ctx = mem->gs_lib_ctx; |
116 | 683 | gs_memory_t *ctx_mem = p_ctx->memory; |
117 | 683 | int code = 0; |
118 | | |
119 | 683 | result = (char *)gs_alloc_bytes(ctx_mem, list_str_len + 1, |
120 | 683 | "gs_lib_ctx_set_default_device_list"); |
121 | | |
122 | 683 | if (result) { |
123 | 683 | gs_free_object(ctx_mem, p_ctx->default_device_list, |
124 | 683 | "gs_lib_ctx_set_default_device_list"); |
125 | | |
126 | 683 | memcpy(result, dev_list_str, list_str_len); |
127 | 683 | result[list_str_len] = '\0'; |
128 | 683 | p_ctx->default_device_list = result; |
129 | 683 | } |
130 | 0 | else { |
131 | 0 | code = gs_note_error(gs_error_VMerror); |
132 | 0 | } |
133 | 683 | return code; |
134 | 683 | } |
135 | | |
136 | | int |
137 | | gs_lib_ctx_get_default_device_list(const gs_memory_t *mem, char** dev_list_str, |
138 | | int *list_str_len) |
139 | 0 | { |
140 | | /* In the case the lib ctx hasn't been initialised */ |
141 | 0 | if (mem && mem->gs_lib_ctx && mem->gs_lib_ctx->default_device_list) { |
142 | 0 | *dev_list_str = mem->gs_lib_ctx->default_device_list; |
143 | 0 | } |
144 | 0 | else { |
145 | 0 | *dev_list_str = (char *)gs_dev_defaults; |
146 | 0 | } |
147 | |
|
148 | 0 | *list_str_len = strlen(*dev_list_str); |
149 | |
|
150 | 0 | return 0; |
151 | 0 | } |
152 | | |
153 | | static int |
154 | | gs_lib_ctx_alloc_root_structure(gs_memory_t *mem, gs_gc_root_ptr *rp) |
155 | 2.04k | { |
156 | 2.04k | int code = 0; |
157 | | |
158 | 2.04k | *rp = gs_raw_alloc_struct_immovable(mem, &st_gc_root_t, "gs_lib_ctx_alloc_root_structure"); |
159 | 2.04k | if (*rp == 0) |
160 | 0 | code = gs_note_error(gs_error_VMerror); |
161 | | |
162 | 2.04k | return code; |
163 | 2.04k | } |
164 | | |
165 | | static int |
166 | | fs_file_open_file(const gs_memory_t *mem, |
167 | | void *secret, |
168 | | const char *fname, |
169 | | const char *mode, |
170 | | gp_file **file) |
171 | 137k | { |
172 | 137k | FILE *f; |
173 | | |
174 | 137k | *file = gp_file_FILE_alloc(mem); |
175 | 137k | if (*file == NULL) |
176 | 0 | return 0; |
177 | 137k | f = gp_fopen_impl(mem->non_gc_memory, fname, mode); |
178 | 137k | if (gp_file_FILE_set(*file, f, fclose)) { |
179 | 136k | *file = NULL; |
180 | 136k | return gs_error_VMerror; |
181 | 136k | } |
182 | 683 | return 0; |
183 | 137k | } |
184 | | |
185 | | static int |
186 | | fs_file_open_scratch(const gs_memory_t *mem, void *secret, const char *prefix, char *rfname, const char *mode, int rm, gp_file **file) |
187 | 669 | { |
188 | 669 | *file = gp_file_FILE_alloc(mem); |
189 | 669 | if (*file == NULL) |
190 | 0 | return gs_error_VMerror; |
191 | 669 | if (gp_file_FILE_set(*file, |
192 | 669 | gp_open_scratch_file_impl(mem, |
193 | 669 | prefix, |
194 | 669 | rfname, |
195 | 669 | mode, |
196 | 669 | rm), |
197 | 669 | NULL)) { |
198 | 0 | *file = NULL; |
199 | 0 | return gs_error_invalidfileaccess; |
200 | 0 | } |
201 | | |
202 | 669 | return 0; |
203 | 669 | } |
204 | | |
205 | | static int |
206 | | fs_file_open_printer(const gs_memory_t *mem, void *secret, const char *fname, int binary_mode, gp_file **file) |
207 | 645 | { |
208 | 645 | FILE *f; |
209 | 645 | int (*close)(FILE *) = NULL; |
210 | | |
211 | 645 | *file = gp_file_FILE_alloc(mem); |
212 | 645 | if (*file == NULL) |
213 | 0 | return gs_error_VMerror; |
214 | 645 | f = gp_open_printer_impl(mem->non_gc_memory, fname, &binary_mode, &close); |
215 | 645 | if (gp_file_FILE_set(*file, f, close)) { |
216 | 0 | *file = NULL; |
217 | 0 | return gs_error_invalidfileaccess; |
218 | 0 | } |
219 | | /* The lgtm comment below is required because on some platforms that function |
220 | | * does nothing. */ |
221 | 645 | gp_setmode_binary_impl(f, binary_mode); /* lgtm [cpp/useless-expression] */ |
222 | | |
223 | 645 | return 0; |
224 | 645 | } |
225 | | |
226 | | #ifdef WITH_CAL |
227 | | static void * |
228 | | cal_do_malloc(void *opaque, size_t size) |
229 | | { |
230 | | gs_memory_t *mem = (gs_memory_t *)opaque; |
231 | | return gs_alloc_bytes(mem, size, "cal_do_malloc"); |
232 | | } |
233 | | |
234 | | static void * |
235 | | cal_do_realloc(void *opaque, void *ptr, size_t newsize) |
236 | | { |
237 | | gs_memory_t *mem = (gs_memory_t *)opaque; |
238 | | return gs_resize_object(mem, ptr, newsize, "cal_do_malloc"); |
239 | | } |
240 | | |
241 | | static void |
242 | | cal_do_free(void *opaque, void *ptr) |
243 | | { |
244 | | gs_memory_t *mem = (gs_memory_t *)opaque; |
245 | | gs_free_object(mem, ptr, "cal_do_malloc"); |
246 | | } |
247 | | |
248 | | static cal_allocators cal_allocs = |
249 | | { |
250 | | cal_do_malloc, |
251 | | cal_do_realloc, |
252 | | cal_do_free |
253 | | }; |
254 | | #endif |
255 | | |
256 | | int gs_lib_ctx_init(gs_lib_ctx_t *ctx, gs_memory_t *mem) |
257 | 683 | { |
258 | 683 | gs_lib_ctx_t *pio = NULL; |
259 | 683 | gs_globals *globals; |
260 | | |
261 | | /* Check the non gc allocator is being passed in */ |
262 | 683 | if (mem == 0 || mem != mem->non_gc_memory) |
263 | 0 | return_error(gs_error_Fatal); |
264 | | |
265 | | /* Get globals here, earlier than it seems we might need it |
266 | | * because a side effect of this is ensuring that the thread |
267 | | * local storage for the malloc pointer is set up. */ |
268 | 683 | globals = gp_get_globals(); |
269 | | |
270 | | /* Now it's safe to set this. */ |
271 | 683 | gp_set_debug_mem_ptr(mem); |
272 | | |
273 | 683 | if (mem->gs_lib_ctx) /* one time initialization */ |
274 | 0 | return 0; |
275 | | |
276 | 683 | pio = (gs_lib_ctx_t*)gs_alloc_bytes_immovable(mem, |
277 | 683 | sizeof(gs_lib_ctx_t), |
278 | 683 | "gs_lib_ctx_init"); |
279 | 683 | if(pio == NULL) |
280 | 0 | return -1; |
281 | | |
282 | | /* Wholesale blanking is cheaper than retail, and scales better when new |
283 | | * fields are added. */ |
284 | 683 | memset(pio, 0, sizeof(*pio)); |
285 | | |
286 | 683 | if (ctx != NULL) { |
287 | 0 | pio->core = ctx->core; |
288 | 0 | gx_monitor_enter((gx_monitor_t *)(pio->core->monitor)); |
289 | 0 | pio->core->refs++; |
290 | 0 | gx_monitor_leave((gx_monitor_t *)(pio->core->monitor)); |
291 | 683 | } else { |
292 | 683 | pio->core = (gs_lib_ctx_core_t *)gs_alloc_bytes_immovable(mem, |
293 | 683 | sizeof(gs_lib_ctx_core_t), |
294 | 683 | "gs_lib_ctx_init(core)"); |
295 | 683 | if (pio->core == NULL) { |
296 | 0 | gs_free_object(mem, pio, "gs_lib_ctx_init"); |
297 | 0 | return -1; |
298 | 0 | } |
299 | 683 | memset(pio->core, 0, sizeof(*pio->core)); |
300 | 683 | pio->core->globals = globals; |
301 | 683 | pio->core->fs = (gs_fs_list_t *)gs_alloc_bytes_immovable(mem, |
302 | 683 | sizeof(gs_fs_list_t), |
303 | 683 | "gs_lib_ctx_init(gs_fs_list_t)"); |
304 | 683 | if (pio->core->fs == NULL) { |
305 | 0 | gs_free_object(mem, pio->core, "gs_lib_ctx_init"); |
306 | 0 | gs_free_object(mem, pio, "gs_lib_ctx_init"); |
307 | 0 | return -1; |
308 | 0 | } |
309 | | #ifdef WITH_CAL |
310 | | pio->core->cal_ctx = cal_init(&cal_allocs, mem); |
311 | | if (pio->core->cal_ctx == NULL) { |
312 | | gs_free_object(mem, pio->core->fs, "gs_lib_ctx_init"); |
313 | | gs_free_object(mem, pio->core, "gs_lib_ctx_init"); |
314 | | gs_free_object(mem, pio, "gs_lib_ctx_init"); |
315 | | return -1; |
316 | | } |
317 | | #endif |
318 | 683 | pio->core->fs->fs.open_file = fs_file_open_file; |
319 | | /* The iodev will fill this in later, if pipes are enabled */ |
320 | 683 | pio->core->fs->fs.open_pipe = NULL; |
321 | 683 | pio->core->fs->fs.open_scratch = fs_file_open_scratch; |
322 | 683 | pio->core->fs->fs.open_printer = fs_file_open_printer; |
323 | 683 | pio->core->fs->secret = NULL; |
324 | 683 | pio->core->fs->memory = mem; |
325 | 683 | pio->core->fs->next = NULL; |
326 | | |
327 | 683 | pio->core->monitor = gx_monitor_alloc(mem); |
328 | 683 | if (pio->core->monitor == NULL) |
329 | 0 | goto core_create_failed; |
330 | 683 | pio->core->refs = 1; |
331 | 683 | pio->core->memory = mem; |
332 | | |
333 | | /* Set the non-zero "shared" things */ |
334 | 683 | gs_lib_ctx_get_real_stdio(&pio->core->fstdin, &pio->core->fstdout, &pio->core->fstderr ); |
335 | 683 | pio->core->stdin_is_interactive = true; |
336 | | /* id's 1 through 4 are reserved for Device color spaces; see gscspace.h */ |
337 | 683 | pio->core->gs_next_id = 5; /* Cloned contexts share the state */ |
338 | | /* Set scanconverter to 1 (default) */ |
339 | 683 | pio->core->scanconverter = GS_SCANCONVERTER_DEFAULT; |
340 | | /* Initialise the underlying CMS. */ |
341 | 683 | pio->core->cms_context = gscms_create(mem); |
342 | 683 | if (pio->core->cms_context == NULL) { |
343 | 0 | gx_monitor_free((gx_monitor_t *)(pio->core->monitor)); |
344 | 0 | core_create_failed: |
345 | | #ifdef WITH_CAL |
346 | | cal_fin(pio->core->cal_ctx, mem); |
347 | | #endif |
348 | 0 | gs_free_object(mem, pio->core->fs, "gs_lib_ctx_init"); |
349 | 0 | gs_free_object(mem, pio->core, "gs_lib_ctx_init"); |
350 | 0 | gs_free_object(mem, pio, "gs_lib_ctx_init"); |
351 | 0 | return -1; |
352 | 0 | } |
353 | 683 | } |
354 | | |
355 | | /* Now set the non zero/false/NULL things */ |
356 | 683 | pio->memory = mem; |
357 | | |
358 | | /* Need to set this before calling gs_lib_ctx_set_icc_directory. */ |
359 | 683 | mem->gs_lib_ctx = pio; |
360 | | /* Initialize our default ICCProfilesDir */ |
361 | 683 | pio->profiledir = NULL; |
362 | 683 | pio->profiledir_len = 0; |
363 | 683 | pio->icc_color_accuracy = MAX_COLOR_ACCURACY; |
364 | 683 | if (gs_lib_ctx_set_icc_directory(mem, DEFAULT_DIR_ICC, strlen(DEFAULT_DIR_ICC)) < 0) |
365 | 0 | goto Failure; |
366 | | |
367 | 683 | if (gs_lib_ctx_set_default_device_list(mem, gs_dev_defaults, |
368 | 683 | strlen(gs_dev_defaults)) < 0) |
369 | 0 | goto Failure; |
370 | | |
371 | | /* Initialise any lock required for the jpx codec */ |
372 | 683 | if (sjpxd_create(mem)) |
373 | 0 | goto Failure; |
374 | | |
375 | 683 | pio->client_check_file_permission = NULL; |
376 | 683 | gp_get_realtime(pio->real_time_0); |
377 | | |
378 | 683 | if (gs_lib_ctx_alloc_root_structure(mem, &pio->name_table_root)) |
379 | 0 | goto Failure; |
380 | | |
381 | 683 | if (gs_lib_ctx_alloc_root_structure(mem, &pio->io_device_table_root)) |
382 | 0 | goto Failure; |
383 | | |
384 | 683 | if (gs_lib_ctx_alloc_root_structure(mem, &pio->font_dir_root)) |
385 | 0 | goto Failure; |
386 | 683 | if (gs_add_control_path(mem, gs_permit_file_writing, gp_null_file_name) < 0) |
387 | 0 | goto Failure; |
388 | | |
389 | 683 | return 0; |
390 | | |
391 | 0 | Failure: |
392 | 0 | gs_lib_ctx_fin(mem); |
393 | 0 | return -1; |
394 | 683 | } |
395 | | |
396 | | static void remove_ctx_pointers(gs_memory_t *mem) |
397 | 683 | { |
398 | 683 | mem->gs_lib_ctx = NULL; |
399 | 683 | if (mem->stable_memory && mem->stable_memory != mem) |
400 | 0 | remove_ctx_pointers(mem->stable_memory); |
401 | 683 | if (mem->non_gc_memory && mem->non_gc_memory != mem) |
402 | 0 | remove_ctx_pointers(mem->non_gc_memory); |
403 | 683 | if (mem->thread_safe_memory && mem->thread_safe_memory != mem) |
404 | 0 | remove_ctx_pointers(mem->thread_safe_memory); |
405 | 683 | } |
406 | | |
407 | | void gs_lib_ctx_fin(gs_memory_t *mem) |
408 | 683 | { |
409 | 683 | gs_lib_ctx_t *ctx; |
410 | 683 | gs_memory_t *ctx_mem; |
411 | 683 | int refs, i; |
412 | 683 | gs_fs_list_t *fs; |
413 | 683 | gs_callout_list_t *entry; |
414 | | |
415 | 683 | if (!mem || !mem->gs_lib_ctx) |
416 | 0 | return; |
417 | | |
418 | 683 | ctx = mem->gs_lib_ctx; |
419 | 683 | ctx_mem = ctx->memory; |
420 | | |
421 | 683 | sjpxd_destroy(mem); |
422 | 683 | gs_free_object(ctx_mem, ctx->profiledir, |
423 | 683 | "gs_lib_ctx_fin"); |
424 | | |
425 | 683 | gs_free_object(ctx_mem, ctx->default_device_list, |
426 | 683 | "gs_lib_ctx_fin"); |
427 | | |
428 | 683 | gs_free_object(ctx_mem, ctx->name_table_root, "gs_lib_ctx_fin"); |
429 | 683 | gs_free_object(ctx_mem, ctx->io_device_table_root, "gs_lib_ctx_fin"); |
430 | 683 | gs_free_object(ctx_mem, ctx->font_dir_root, "gs_lib_ctx_fin"); |
431 | | |
432 | 683 | gx_monitor_enter((gx_monitor_t *)(ctx->core->monitor)); |
433 | 683 | refs = --ctx->core->refs; |
434 | 683 | gx_monitor_leave((gx_monitor_t *)(ctx->core->monitor)); |
435 | 683 | if (refs == 0) { |
436 | 683 | gscms_destroy(ctx->core->cms_context); |
437 | 683 | gx_monitor_free((gx_monitor_t *)(ctx->core->monitor)); |
438 | | #ifdef WITH_CAL |
439 | | cal_fin(ctx->core->cal_ctx, ctx->core->memory); |
440 | | #endif |
441 | 683 | gs_purge_scratch_files(ctx->core->memory); |
442 | 683 | gs_purge_control_paths(ctx->core->memory, gs_permit_file_reading); |
443 | 683 | gs_purge_control_paths(ctx->core->memory, gs_permit_file_writing); |
444 | 683 | gs_purge_control_paths(ctx->core->memory, gs_permit_file_control); |
445 | | |
446 | 683 | fs = ctx->core->fs; |
447 | 1.36k | while (fs) { |
448 | 683 | gs_fs_list_t *next = fs->next; |
449 | 683 | gs_free_object(fs->memory, fs, "gs_lib_ctx_fin"); |
450 | 683 | fs = next; |
451 | 683 | } |
452 | | |
453 | 683 | entry = ctx->core->callouts; |
454 | 683 | while (entry) { |
455 | 0 | gs_callout_list_t *next = entry->next; |
456 | 0 | gs_free_object(mem->non_gc_memory, entry, "gs_callout_list_t"); |
457 | 0 | entry = next; |
458 | 0 | } |
459 | | |
460 | 12.9k | for (i = 0; i < ctx->core->argc; i++) |
461 | 12.2k | gs_free_object(ctx->core->memory, ctx->core->argv[i], "gs_lib_ctx_arg"); |
462 | 683 | gs_free_object(ctx->core->memory, ctx->core->argv, "gs_lib_ctx_args"); |
463 | | |
464 | 683 | gs_free_object(ctx->core->memory, ctx->core, "gs_lib_ctx_fin"); |
465 | 683 | } |
466 | 683 | remove_ctx_pointers(ctx_mem); |
467 | | |
468 | 683 | gs_free_object(ctx_mem, ctx, "gs_lib_ctx_init"); |
469 | 683 | } |
470 | | |
471 | | gs_lib_ctx_t *gs_lib_ctx_get_interp_instance(const gs_memory_t *mem) |
472 | 4.76M | { |
473 | 4.76M | if (mem == NULL) |
474 | 0 | return NULL; |
475 | 4.76M | return mem->gs_lib_ctx; |
476 | 4.76M | } |
477 | | |
478 | | void *gs_lib_ctx_get_cms_context( const gs_memory_t *mem ) |
479 | 1.61M | { |
480 | 1.61M | if (mem == NULL) |
481 | 0 | return NULL; |
482 | 1.61M | return mem->gs_lib_ctx->core->cms_context; |
483 | 1.61M | } |
484 | | |
485 | | int gs_lib_ctx_get_act_on_uel( const gs_memory_t *mem ) |
486 | 683 | { |
487 | 683 | if (mem == NULL) |
488 | 0 | return 0; |
489 | 683 | return mem->gs_lib_ctx->core->act_on_uel; |
490 | 683 | } |
491 | | |
492 | | /* Provide a single point for all "C" stdout and stderr. |
493 | | */ |
494 | | |
495 | | int outwrite(const gs_memory_t *mem, const char *str, int len) |
496 | 12.3k | { |
497 | 12.3k | int code; |
498 | 12.3k | gs_lib_ctx_t *pio = mem->gs_lib_ctx; |
499 | 12.3k | gs_lib_ctx_core_t *core = pio->core; |
500 | | |
501 | 12.3k | if (len == 0) |
502 | 0 | return 0; |
503 | 12.3k | if (core->stdout_is_redirected) { |
504 | 12.3k | if (core->stdout_to_stderr) |
505 | 0 | return errwrite(mem, str, len); |
506 | 12.3k | code = gp_fwrite(str, 1, len, core->fstdout2); |
507 | 12.3k | gp_fflush(core->fstdout2); |
508 | 12.3k | } else if (core->stdout_fn) { |
509 | 0 | return (*core->stdout_fn)(core->std_caller_handle, str, len); |
510 | 0 | } else { |
511 | 0 | code = fwrite(str, 1, len, core->fstdout); |
512 | 0 | fflush(core->fstdout); |
513 | 0 | } |
514 | 12.3k | return code; |
515 | 12.3k | } |
516 | | |
517 | | int errwrite(const gs_memory_t *mem, const char *str, int len) |
518 | 5.54k | { |
519 | 5.54k | int code; |
520 | 5.54k | gs_lib_ctx_t *ctx; |
521 | 5.54k | gs_lib_ctx_core_t *core; |
522 | 5.54k | if (len == 0) |
523 | 0 | return 0; |
524 | 5.54k | if (mem == NULL) { |
525 | | #ifdef DEBUG |
526 | | mem = gp_get_debug_mem_ptr(); |
527 | | if (mem == NULL) |
528 | | #endif |
529 | 0 | return 0; |
530 | 0 | } |
531 | 5.54k | ctx = mem->gs_lib_ctx; |
532 | 5.54k | if (ctx == NULL) |
533 | 0 | return 0; |
534 | 5.54k | core = ctx->core; |
535 | 5.54k | if (core->stderr_fn) |
536 | 5.54k | return (*core->stderr_fn)(core->std_caller_handle, str, len); |
537 | | |
538 | 0 | code = fwrite(str, 1, len, core->fstderr); |
539 | 0 | fflush(core->fstderr); |
540 | 0 | return code; |
541 | 5.54k | } |
542 | | |
543 | | void outflush(const gs_memory_t *mem) |
544 | 0 | { |
545 | 0 | gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core; |
546 | 0 | if (core->stdout_is_redirected) { |
547 | 0 | if (core->stdout_to_stderr) { |
548 | 0 | if (!core->stderr_fn) |
549 | 0 | fflush(core->fstderr); |
550 | 0 | } |
551 | 0 | else |
552 | 0 | gp_fflush(core->fstdout2); |
553 | 0 | } |
554 | 0 | else if (!core->stdout_fn) |
555 | 0 | fflush(core->fstdout); |
556 | 0 | } |
557 | | |
558 | | void errflush(const gs_memory_t *mem) |
559 | 0 | { |
560 | 0 | if (!mem->gs_lib_ctx->core->stderr_fn) |
561 | 0 | fflush(mem->gs_lib_ctx->core->fstderr); |
562 | | /* else nothing to flush */ |
563 | 0 | } |
564 | | |
565 | | int |
566 | | gs_check_file_permission (gs_memory_t *mem, const char *fname, const int len, const char *permission) |
567 | 464k | { |
568 | 464k | int code = 0; |
569 | 464k | if (mem->gs_lib_ctx->client_check_file_permission != NULL) { |
570 | 464k | code = mem->gs_lib_ctx->client_check_file_permission(mem, fname, len, permission); |
571 | 464k | } |
572 | 464k | return code; |
573 | 464k | } |
574 | | |
575 | | static void |
576 | | rewrite_percent_specifiers(char *s) |
577 | 683 | { |
578 | 683 | char *match_start; |
579 | | |
580 | 683 | while (*s) |
581 | 683 | { |
582 | 683 | int flags; |
583 | | /* Find a % */ |
584 | 6.83k | while (*s && *s != '%') |
585 | 6.14k | s++; |
586 | 683 | if (*s == 0) |
587 | 683 | return; |
588 | 0 | match_start = s; |
589 | 0 | s++; |
590 | | /* Skip over flags (just one instance of any given flag, in any order) */ |
591 | 0 | flags = 0; |
592 | 0 | while (*s) { |
593 | 0 | if (*s == '-' && (flags & 1) == 0) |
594 | 0 | flags |= 1; |
595 | 0 | else if (*s == '+' && (flags & 2) == 0) |
596 | 0 | flags |= 2; |
597 | 0 | else if (*s == ' ' && (flags & 4) == 0) |
598 | 0 | flags |= 4; |
599 | 0 | else if (*s == '0' && (flags & 8) == 0) |
600 | 0 | flags |= 8; |
601 | 0 | else if (*s == '#' && (flags & 16) == 0) |
602 | 0 | flags |= 16; |
603 | 0 | else |
604 | 0 | break; |
605 | 0 | s++; |
606 | 0 | } |
607 | | /* Skip over width */ |
608 | 0 | while (*s >= '0' && *s <= '9') |
609 | 0 | s++; |
610 | | /* Skip over .precision */ |
611 | 0 | if (*s == '.' && s[1] >= '0' && s[1] <= '9') { |
612 | 0 | s++; |
613 | 0 | while (*s >= '0' && *s <= '9') |
614 | 0 | s++; |
615 | 0 | } |
616 | | /* Skip over 'l' */ |
617 | 0 | if (*s == 'l') |
618 | 0 | s++; |
619 | 0 | if (*s == 'd' || |
620 | 0 | *s == 'i' || |
621 | 0 | *s == 'u' || |
622 | 0 | *s == 'o' || |
623 | 0 | *s == 'x' || |
624 | 0 | *s == 'X') { |
625 | | /* Success! */ |
626 | 0 | memset(match_start, '*', s - match_start + 1); |
627 | 0 | } |
628 | | /* If we have escaped percents ("%%") so the percent |
629 | | will survive a call to sprintf and co, then we need |
630 | | to drop the extra one here, because the validation |
631 | | code will see the string *after* it's been sprintf'ed. |
632 | | */ |
633 | 0 | else if (*s == '%') { |
634 | 0 | char *s0 = s; |
635 | 0 | while (*s0) { |
636 | 0 | *s0 = *(s0 + 1); |
637 | 0 | s0++; |
638 | 0 | } |
639 | 0 | } |
640 | 0 | } |
641 | 683 | } |
642 | | |
643 | | /* For the OutputFile permission we have to deal with formattable strings |
644 | | i.e. ones that have "%d" or similar in them. For these we want to replace |
645 | | everything after the %d with a wildcard "*". |
646 | | Since we do this in two places (the -s and -o cases) split it into a |
647 | | function. |
648 | | */ |
649 | | #define IS_WHITESPACE(c) ((c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA)) |
650 | | int |
651 | | gs_add_outputfile_control_path(gs_memory_t *mem, const char *fname) |
652 | 683 | { |
653 | 683 | char f[gp_file_name_sizeof]; |
654 | 683 | int code; |
655 | | |
656 | | /* Be sure the string copy will fit */ |
657 | 683 | if (strlen(fname) >= gp_file_name_sizeof) |
658 | 0 | return gs_error_rangecheck; |
659 | 683 | strcpy(f, fname); |
660 | | /* Try to rewrite any %d (or similar) in the string */ |
661 | 683 | rewrite_percent_specifiers(f); |
662 | | |
663 | 683 | code = gs_add_control_path(mem, gs_permit_file_control, f); |
664 | 683 | if (code < 0) |
665 | 0 | return code; |
666 | 683 | return gs_add_control_path(mem, gs_permit_file_writing, f); |
667 | 683 | } |
668 | | |
669 | | int |
670 | | gs_remove_outputfile_control_path(gs_memory_t *mem, const char *fname) |
671 | 0 | { |
672 | 0 | char f[gp_file_name_sizeof]; |
673 | 0 | int code; |
674 | | |
675 | | /* Be sure the string copy will fit */ |
676 | 0 | if (strlen(fname) >= gp_file_name_sizeof) |
677 | 0 | return gs_error_rangecheck; |
678 | 0 | strcpy(f, fname); |
679 | | /* Try to rewrite any %d (or similar) in the string */ |
680 | 0 | rewrite_percent_specifiers(f); |
681 | |
|
682 | 0 | code = gs_remove_control_path(mem, gs_permit_file_control, f); |
683 | 0 | if (code < 0) |
684 | 0 | return code; |
685 | 0 | return gs_remove_control_path(mem, gs_permit_file_writing, f); |
686 | 0 | } |
687 | | |
688 | | int |
689 | | gs_add_explicit_control_path(gs_memory_t *mem, const char *arg, gs_path_control_t control) |
690 | 0 | { |
691 | 0 | char *p2, *p1 = (char *)arg; |
692 | 0 | const char *lim; |
693 | 0 | int code = 0; |
694 | |
|
695 | 0 | if (arg == NULL) |
696 | 0 | return 0; |
697 | 0 | lim = arg + strlen(arg); |
698 | 0 | while (code >= 0 && p1 < lim && (p2 = strchr(p1, (int)gp_file_name_list_separator)) != NULL) { |
699 | 0 | code = gs_add_control_path_len(mem, control, p1, (int)(p2 - p1)); |
700 | 0 | p1 = p2 + 1; |
701 | 0 | } |
702 | 0 | if (p1 < lim) |
703 | 0 | code = gs_add_control_path_len(mem, control, p1, (int)(lim - p1)); |
704 | 0 | return code; |
705 | 0 | } |
706 | | |
707 | | int |
708 | | gs_add_control_path_len(const gs_memory_t *mem, gs_path_control_t type, const char *path, size_t len) |
709 | 86.0k | { |
710 | 86.0k | return gs_add_control_path_len_flags(mem, type, path, len, 0); |
711 | 86.0k | } |
712 | | |
713 | | int |
714 | | gs_add_control_path_len_flags(const gs_memory_t *mem, gs_path_control_t type, const char *path, size_t len, int flags) |
715 | 90.1k | { |
716 | 90.1k | gs_path_control_set_t *control; |
717 | 90.1k | unsigned int n, i; |
718 | 90.1k | gs_lib_ctx_core_t *core; |
719 | 90.1k | char *buffer; |
720 | 90.1k | uint rlen; |
721 | | |
722 | 90.1k | if (path == NULL || len == 0) |
723 | 0 | return 0; |
724 | | |
725 | 90.1k | if (mem == NULL || mem->gs_lib_ctx == NULL || |
726 | 90.1k | (core = mem->gs_lib_ctx->core) == NULL) |
727 | 0 | return gs_error_unknownerror; |
728 | | |
729 | 90.1k | switch(type) { |
730 | 83.9k | case gs_permit_file_reading: |
731 | 83.9k | control = &core->permit_reading; |
732 | 83.9k | break; |
733 | 3.40k | case gs_permit_file_writing: |
734 | 3.40k | control = &core->permit_writing; |
735 | 3.40k | break; |
736 | 2.71k | case gs_permit_file_control: |
737 | 2.71k | control = &core->permit_control; |
738 | 2.71k | break; |
739 | 0 | default: |
740 | 0 | return gs_error_rangecheck; |
741 | 90.1k | } |
742 | | |
743 | 90.1k | rlen = len+1; |
744 | 90.1k | buffer = (char *)gs_alloc_bytes(core->memory, rlen, "gp_validate_path"); |
745 | 90.1k | if (buffer == NULL) |
746 | 0 | return gs_error_VMerror; |
747 | | |
748 | 90.1k | if (gp_file_name_reduce(path, (uint)len, buffer, &rlen) != gp_combine_success) |
749 | 0 | return gs_error_invalidfileaccess; |
750 | 90.1k | buffer[rlen] = 0; |
751 | | |
752 | 90.1k | n = control->num; |
753 | 809k | for (i = 0; i < n; i++) |
754 | 782k | { |
755 | 782k | if (strncmp(control->entry[i].path, buffer, rlen) == 0 && |
756 | 782k | control->entry[i].path[rlen] == 0) { |
757 | 62.8k | gs_free_object(core->memory, buffer, "gs_add_control_path_len"); |
758 | 62.8k | return 0; /* Already there! */ |
759 | 62.8k | } |
760 | 782k | } |
761 | | |
762 | 27.2k | if (control->num == control->max) { |
763 | 4.09k | gs_path_control_entry_t *p; |
764 | | |
765 | 4.09k | n = control->max * 2; |
766 | 4.09k | if (n == 0) { |
767 | 2.04k | n = 4; |
768 | 2.04k | p = (gs_path_control_entry_t *)gs_alloc_bytes(core->memory, sizeof(*p)*n, "gs_lib_ctx(entries)"); |
769 | 2.04k | } else |
770 | 2.04k | p = (gs_path_control_entry_t *)gs_resize_object(core->memory, control->entry, sizeof(*p)*n, "gs_lib_ctx(entries)"); |
771 | 4.09k | if (p == NULL) { |
772 | 0 | gs_free_object(core->memory, buffer, "gs_add_control_path_len"); |
773 | 0 | return gs_error_VMerror; |
774 | 0 | } |
775 | 4.09k | control->entry = p; |
776 | 4.09k | control->max = n; |
777 | 4.09k | } |
778 | | |
779 | 27.2k | n = control->num; |
780 | 27.2k | control->entry[n].path = buffer; |
781 | 27.2k | control->entry[n].path[len] = 0; |
782 | 27.2k | control->entry[n].flags = flags; |
783 | 27.2k | control->num++; |
784 | | |
785 | 27.2k | return 0; |
786 | 27.2k | } |
787 | | |
788 | | int |
789 | | gs_add_control_path(const gs_memory_t *mem, gs_path_control_t type, const char *path) |
790 | 2.04k | { |
791 | 2.04k | return gs_add_control_path_len_flags(mem, type, path, path ? strlen(path) : 0, 0); |
792 | 2.04k | } |
793 | | |
794 | | int |
795 | | gs_add_control_path_flags(const gs_memory_t *mem, gs_path_control_t type, const char *path, int flags) |
796 | 2.00k | { |
797 | 2.00k | return gs_add_control_path_len_flags(mem, type, path, path ? strlen(path) : 0, flags); |
798 | 2.00k | } |
799 | | |
800 | | int |
801 | | gs_remove_control_path_len(const gs_memory_t *mem, gs_path_control_t type, const char *path, size_t len) |
802 | 0 | { |
803 | 0 | return gs_remove_control_path_len_flags(mem, type, path, len, 0); |
804 | 0 | } |
805 | | |
806 | | int |
807 | | gs_remove_control_path_len_flags(const gs_memory_t *mem, gs_path_control_t type, const char *path, size_t len, int flags) |
808 | 0 | { |
809 | 0 | gs_path_control_set_t *control; |
810 | 0 | unsigned int n, i; |
811 | 0 | gs_lib_ctx_core_t *core; |
812 | 0 | char *buffer; |
813 | 0 | uint rlen; |
814 | |
|
815 | 0 | if (path == NULL || len == 0) |
816 | 0 | return 0; |
817 | | |
818 | 0 | if (mem == NULL || mem->gs_lib_ctx == NULL || |
819 | 0 | (core = mem->gs_lib_ctx->core) == NULL) |
820 | 0 | return gs_error_unknownerror; |
821 | | |
822 | 0 | switch(type) { |
823 | 0 | case gs_permit_file_reading: |
824 | 0 | control = &core->permit_reading; |
825 | 0 | break; |
826 | 0 | case gs_permit_file_writing: |
827 | 0 | control = &core->permit_writing; |
828 | 0 | break; |
829 | 0 | case gs_permit_file_control: |
830 | 0 | control = &core->permit_control; |
831 | 0 | break; |
832 | 0 | default: |
833 | 0 | return gs_error_rangecheck; |
834 | 0 | } |
835 | | |
836 | 0 | rlen = len+1; |
837 | 0 | buffer = (char *)gs_alloc_bytes(core->memory, rlen, "gp_validate_path"); |
838 | 0 | if (buffer == NULL) |
839 | 0 | return gs_error_VMerror; |
840 | | |
841 | 0 | if (gp_file_name_reduce(path, (uint)len, buffer, &rlen) != gp_combine_success) |
842 | 0 | return gs_error_invalidfileaccess; |
843 | 0 | buffer[rlen] = 0; |
844 | |
|
845 | 0 | n = control->num; |
846 | 0 | for (i = 0; i < n; i++) { |
847 | 0 | if (control->entry[i].flags == flags && |
848 | 0 | strncmp(control->entry[i].path, buffer, len) == 0 && |
849 | 0 | control->entry[i].path[len] == 0) |
850 | 0 | break; |
851 | 0 | } |
852 | 0 | gs_free_object(core->memory, buffer, "gs_remove_control_path_len"); |
853 | 0 | if (i == n) |
854 | 0 | return 0; |
855 | | |
856 | 0 | gs_free_object(core->memory, control->entry[i].path, "gs_lib_ctx(path)"); |
857 | 0 | for (;i < n-1; i++) |
858 | 0 | control->entry[i] = control->entry[i+1]; |
859 | 0 | control->num = n-1; |
860 | |
|
861 | 0 | return 0; |
862 | 0 | } |
863 | | |
864 | | int |
865 | | gs_remove_control_path(const gs_memory_t *mem, gs_path_control_t type, const char *path) |
866 | 0 | { |
867 | 0 | if (path == NULL) |
868 | 0 | return 0; |
869 | | |
870 | 0 | return gs_remove_control_path_len_flags(mem, type, path, strlen(path), 0); |
871 | 0 | } |
872 | | |
873 | | int |
874 | | gs_remove_control_path_flags(const gs_memory_t *mem, gs_path_control_t type, const char *path, int flags) |
875 | 0 | { |
876 | 0 | if (path == NULL) |
877 | 0 | return 0; |
878 | | |
879 | 0 | return gs_remove_control_path_len_flags(mem, type, path, strlen(path), flags); |
880 | 0 | } |
881 | | |
882 | | void |
883 | | gs_purge_control_paths(const gs_memory_t *mem, gs_path_control_t type) |
884 | 2.04k | { |
885 | 2.04k | gs_path_control_set_t *control; |
886 | 2.04k | unsigned int n, in, out; |
887 | 2.04k | gs_lib_ctx_core_t *core; |
888 | | |
889 | 2.04k | if (mem == NULL || mem->gs_lib_ctx == NULL || |
890 | 2.04k | (core = mem->gs_lib_ctx->core) == NULL) |
891 | 0 | return; |
892 | | |
893 | 2.04k | switch(type) { |
894 | 683 | case gs_permit_file_reading: |
895 | 683 | control = &core->permit_reading; |
896 | 683 | break; |
897 | 683 | case gs_permit_file_writing: |
898 | 683 | control = &core->permit_writing; |
899 | 683 | break; |
900 | 683 | case gs_permit_file_control: |
901 | 683 | control = &core->permit_control; |
902 | 683 | break; |
903 | 0 | default: |
904 | 0 | return; |
905 | 2.04k | } |
906 | | |
907 | 2.04k | n = control->num; |
908 | 27.3k | for (in = out = 0; in < n; in++) { |
909 | 25.2k | if (control->entry[in].flags & gs_path_control_flag_is_scratch_file) { |
910 | | /* Don't purge scratch files! */ |
911 | 0 | control->entry[out++] = control->entry[in]; |
912 | 0 | } else |
913 | 25.2k | gs_free_object(core->memory, control->entry[in].path, "gs_lib_ctx(path)"); |
914 | 25.2k | } |
915 | 2.04k | control->num = out; |
916 | 2.04k | if (out == 0) { |
917 | 2.04k | gs_free_object(core->memory, control->entry, "gs_lib_ctx(paths)"); |
918 | 2.04k | control->entry = NULL; |
919 | 2.04k | control->max = 0; |
920 | 2.04k | } |
921 | 2.04k | } |
922 | | |
923 | | void |
924 | | gs_purge_scratch_files(const gs_memory_t *mem) |
925 | 683 | { |
926 | 683 | gs_path_control_set_t *control; |
927 | 683 | gs_path_control_t type; |
928 | 683 | int n, in, out; |
929 | 683 | gs_lib_ctx_core_t *core; |
930 | | |
931 | 683 | if (mem == NULL || mem->gs_lib_ctx == NULL || |
932 | 683 | (core = mem->gs_lib_ctx->core) == NULL) |
933 | 0 | return; |
934 | | |
935 | 2.73k | for (type = gs_permit_file_reading; type <= gs_permit_file_control; type++) |
936 | 2.04k | { |
937 | 2.04k | switch(type) { |
938 | 0 | default: |
939 | 683 | case gs_permit_file_reading: |
940 | 683 | control = &core->permit_reading; |
941 | 683 | break; |
942 | 683 | case gs_permit_file_writing: |
943 | 683 | control = &core->permit_writing; |
944 | 683 | break; |
945 | 683 | case gs_permit_file_control: |
946 | 683 | control = &core->permit_control; |
947 | 683 | break; |
948 | 2.04k | } |
949 | | |
950 | 2.04k | n = control->num; |
951 | 29.3k | for (in = out = 0; in < n; in++) { |
952 | 27.2k | if ((control->entry[in].flags & gs_path_control_flag_is_scratch_file) == 0) { |
953 | | /* Only purge scratch files! */ |
954 | 25.2k | control->entry[out++] = control->entry[in]; |
955 | 25.2k | } else { |
956 | 2.00k | if (type == gs_permit_file_reading) { |
957 | | /* Call gp_unlink_impl, as we don't want gp_unlink |
958 | | * to go looking in the lists we are currently |
959 | | * manipulating! */ |
960 | 669 | gp_unlink_impl(core->memory, control->entry[in].path); |
961 | 669 | } |
962 | 2.00k | gs_free_object(core->memory, control->entry[in].path, "gs_lib_ctx(path)"); |
963 | 2.00k | } |
964 | 27.2k | } |
965 | 2.04k | control->num = out; |
966 | 2.04k | if (out == 0) { |
967 | 0 | gs_free_object(core->memory, control->entry, "gs_lib_ctx(paths)"); |
968 | 0 | control->entry = NULL; |
969 | 0 | control->max = 0; |
970 | 0 | } |
971 | 2.04k | } |
972 | 683 | } |
973 | | |
974 | | void |
975 | | gs_activate_path_control(gs_memory_t *mem, int enable) |
976 | 683 | { |
977 | 683 | gs_lib_ctx_core_t *core; |
978 | | |
979 | 683 | if (mem == NULL || mem->gs_lib_ctx == NULL || |
980 | 683 | (core = mem->gs_lib_ctx->core) == NULL) |
981 | 0 | return; |
982 | | |
983 | 683 | core->path_control_active = enable; |
984 | 683 | } |
985 | | |
986 | | int |
987 | | gs_is_path_control_active(const gs_memory_t *mem) |
988 | 15.7k | { |
989 | 15.7k | gs_lib_ctx_core_t *core; |
990 | | |
991 | 15.7k | if (mem == NULL || mem->gs_lib_ctx == NULL || |
992 | 15.7k | (core = mem->gs_lib_ctx->core) == NULL) |
993 | 0 | return 0; |
994 | | |
995 | 15.7k | return core->path_control_active; |
996 | 15.7k | } |
997 | | |
998 | | int |
999 | | gs_add_fs(const gs_memory_t *mem, |
1000 | | gs_fs_t *fs, |
1001 | | void *secret) |
1002 | 0 | { |
1003 | 0 | gs_fs_list_t *fsl; |
1004 | 0 | gs_lib_ctx_core_t *core; |
1005 | |
|
1006 | 0 | if (mem == NULL || mem->gs_lib_ctx == NULL || |
1007 | 0 | (core = mem->gs_lib_ctx->core) == NULL) |
1008 | 0 | return gs_error_unknownerror; |
1009 | | |
1010 | 0 | fsl = (gs_fs_list_t *)gs_alloc_bytes_immovable(mem->non_gc_memory, |
1011 | 0 | sizeof(gs_fs_list_t), |
1012 | 0 | "gs_fs_list_t"); |
1013 | 0 | if (fsl == NULL) |
1014 | 0 | return gs_error_VMerror; |
1015 | 0 | fsl->fs = *fs; |
1016 | 0 | fsl->secret = secret; |
1017 | 0 | fsl->memory = mem->non_gc_memory; |
1018 | 0 | fsl->next = core->fs; |
1019 | 0 | core->fs = fsl; |
1020 | |
|
1021 | 0 | return 0; |
1022 | 0 | } |
1023 | | |
1024 | | void |
1025 | | gs_remove_fs(const gs_memory_t *mem, |
1026 | | gs_fs_t *rfs, |
1027 | | void *secret) |
1028 | 0 | { |
1029 | 0 | gs_fs_list_t **pfs; |
1030 | 0 | gs_lib_ctx_core_t *core; |
1031 | |
|
1032 | 0 | if (mem == NULL || mem->gs_lib_ctx == NULL || |
1033 | 0 | (core = mem->gs_lib_ctx->core) == NULL) |
1034 | 0 | return; |
1035 | | |
1036 | 0 | pfs = &core->fs; |
1037 | 0 | while (*pfs) |
1038 | 0 | { |
1039 | 0 | gs_fs_list_t *fs = *pfs; |
1040 | 0 | if (fs->fs.open_file == rfs->open_file && |
1041 | 0 | fs->fs.open_pipe == rfs->open_pipe && |
1042 | 0 | fs->fs.open_scratch == rfs->open_scratch && |
1043 | 0 | fs->fs.open_printer == rfs->open_printer && |
1044 | 0 | fs->secret == secret) { |
1045 | 0 | *pfs = fs->next; |
1046 | 0 | gs_free_object(fs->memory, fs, "gs_fs_t"); |
1047 | 0 | } else |
1048 | 0 | pfs = &(*pfs)->next; |
1049 | 0 | } |
1050 | 0 | } |
1051 | | |
1052 | | int |
1053 | | gs_lib_ctx_stash_sanitized_arg(gs_lib_ctx_t *ctx, const char *arg) |
1054 | 11.6k | { |
1055 | 11.6k | gs_lib_ctx_core_t *core; |
1056 | 11.6k | size_t len; |
1057 | 11.6k | const char *p; |
1058 | 11.6k | int elide = 0; |
1059 | | |
1060 | 11.6k | if (ctx == NULL || ctx->core == NULL || arg == NULL) |
1061 | 0 | return 0; |
1062 | | |
1063 | | /* Sanitize arg */ |
1064 | 11.6k | switch(*arg) |
1065 | 11.6k | { |
1066 | 11.6k | case '-': |
1067 | 11.6k | switch (arg[1]) |
1068 | 11.6k | { |
1069 | 0 | case 0: /* We can let - through unchanged */ |
1070 | 0 | case '-': /* Need to check for permitted file lists */ |
1071 | | /* By default, we want to keep the key, but lose the value */ |
1072 | 0 | p = arg+2; |
1073 | 0 | while (*p && *p != '=') |
1074 | 0 | p++; |
1075 | 0 | if (*p == '=') |
1076 | 0 | p++; |
1077 | 0 | if (*p == 0) |
1078 | 0 | break; /* No value to elide */ |
1079 | | /* Check for our blocked values here */ |
1080 | 0 | #define ARG_MATCHES(STR, ARG, LEN) \ |
1081 | 0 | (strlen(STR) == LEN && !memcmp(STR, ARG, LEN)) |
1082 | 0 | if (ARG_MATCHES("permit-file-read", arg+2, p-arg-3)) |
1083 | 0 | elide=1; |
1084 | 0 | if (ARG_MATCHES("permit-file-write", arg+2, p-arg-3)) |
1085 | 0 | elide=1; |
1086 | 0 | if (ARG_MATCHES("permit-file-control", arg+2, p-arg-3)) |
1087 | 0 | elide=1; |
1088 | 0 | if (ARG_MATCHES("permit-file-all", arg+2, p-arg-3)) |
1089 | 0 | elide=1; |
1090 | 0 | #undef ARG_MATCHES |
1091 | | /* Didn't match a blocked value, so allow it. */ |
1092 | 0 | break; |
1093 | 6.83k | case 'd': /* We can let -dFoo=<whatever> through unchanged */ |
1094 | 6.83k | case 'D': /* We can let -DFoo=<whatever> through unchanged */ |
1095 | 7.51k | case 'r': /* We can let -r through unchanged */ |
1096 | 7.51k | case 'Z': /* We can let -Z through unchanged */ |
1097 | 7.51k | case 'g': /* We can let -g through unchanged */ |
1098 | 7.51k | case 'P': /* We can let -P through unchanged */ |
1099 | 7.51k | case '+': /* We can let -+ through unchanged */ |
1100 | 8.19k | case '_': /* We can let -_ through unchanged */ |
1101 | 8.19k | case 'u': /* We can let -u through unchanged */ |
1102 | 8.19k | case 'q': /* We can let -q through unchanged */ |
1103 | 8.19k | break; |
1104 | 0 | case 'I': /* Let through the I, but hide anything else */ |
1105 | 0 | case 'f': /* Let through the I, but hide anything else */ |
1106 | 0 | if (arg[2] == 0) |
1107 | 0 | break; |
1108 | 0 | p = arg+2; |
1109 | 0 | while (*p == 32) |
1110 | 0 | p++; |
1111 | 0 | elide = 1; |
1112 | 0 | break; |
1113 | 2.73k | case 's': |
1114 | 2.73k | case 'S': |
1115 | | /* By default, we want to keep the key, but lose the value */ |
1116 | 2.73k | p = arg+2; |
1117 | 28.0k | while (*p && *p != '=') |
1118 | 25.2k | p++; |
1119 | 2.73k | if (*p == '=') |
1120 | 2.73k | p++; |
1121 | 2.73k | if (*p == 0) |
1122 | 0 | break; /* No value to elide */ |
1123 | | /* Check for our whitelisted values here */ |
1124 | 2.73k | #define ARG_MATCHES(STR, ARG, LEN) \ |
1125 | 17.7k | (strlen(STR) == LEN && !memcmp(STR, ARG, LEN)) |
1126 | 2.73k | if (ARG_MATCHES("DEFAULTPAPERSIZE", arg+2, p-arg-3)) |
1127 | 0 | break; |
1128 | 2.73k | if (ARG_MATCHES("DEVICE", arg+2, p-arg-3)) |
1129 | 683 | break; |
1130 | 2.04k | if (ARG_MATCHES("PAPERSIZE", arg+2, p-arg-3)) |
1131 | 0 | break; |
1132 | 2.04k | if (ARG_MATCHES("SUBSTFONT", arg+2, p-arg-3)) |
1133 | 0 | break; |
1134 | 2.04k | if (ARG_MATCHES("ColorConversionStrategy", arg+2, p-arg-3)) |
1135 | 0 | break; |
1136 | 2.04k | if (ARG_MATCHES("NupControl", arg+2, p-arg-3)) |
1137 | 0 | break; |
1138 | 2.04k | if (ARG_MATCHES("PageList", arg+2, p-arg-3)) |
1139 | 0 | break; |
1140 | 2.04k | if (ARG_MATCHES("ProcessColorModel", arg+2, p-arg-3)) |
1141 | 0 | break; |
1142 | 2.04k | #undef ARG_MATCHES |
1143 | | /* Didn't match a whitelisted value, so elide it. */ |
1144 | 2.04k | elide = 1; |
1145 | 2.04k | break; |
1146 | 683 | default: |
1147 | | /* Shouldn't happen, but elide it just in case */ |
1148 | 683 | arg = "?"; |
1149 | 683 | break; |
1150 | 11.6k | } |
1151 | 11.6k | break; |
1152 | 11.6k | case '@': |
1153 | | /* Shouldn't happen */ |
1154 | 0 | default: |
1155 | | /* Anything else should be elided */ |
1156 | 0 | arg = "?"; |
1157 | 0 | break; |
1158 | 11.6k | } |
1159 | | |
1160 | 11.6k | core = ctx->core; |
1161 | 11.6k | if (elide) |
1162 | 2.04k | len = p-arg; |
1163 | 9.56k | else |
1164 | 9.56k | len = strlen(arg); |
1165 | | |
1166 | 11.6k | if (core->arg_max == core->argc) { |
1167 | 2.04k | char **argv; |
1168 | 2.04k | int newlen = core->arg_max * 2; |
1169 | 2.04k | if (newlen == 0) |
1170 | 0 | newlen = 4; |
1171 | 2.04k | argv = (char **)gs_alloc_bytes(ctx->core->memory, sizeof(char *) * newlen, |
1172 | 2.04k | "gs_lib_ctx_args"); |
1173 | 2.04k | if (argv == NULL) |
1174 | 0 | return gs_error_VMerror; |
1175 | 2.04k | if (core->argc > 0) { |
1176 | 2.04k | memcpy(argv, core->argv, sizeof(char *) * core->argc); |
1177 | 2.04k | gs_free_object(ctx->memory, core->argv, "gs_lib_ctx_args"); |
1178 | 2.04k | } |
1179 | 2.04k | core->argv = argv; |
1180 | 2.04k | core->arg_max = newlen; |
1181 | 2.04k | } |
1182 | | |
1183 | 11.6k | core->argv[core->argc] = (char *)gs_alloc_bytes(ctx->core->memory, len+1+elide, |
1184 | 11.6k | "gs_lib_ctx_arg"); |
1185 | 11.6k | if (core->argv[core->argc] == NULL) |
1186 | 0 | return gs_error_VMerror; |
1187 | 11.6k | memcpy(core->argv[core->argc], arg, len); |
1188 | 11.6k | if (elide) { |
1189 | 2.04k | core->argv[core->argc][len] = '?'; |
1190 | 2.04k | } |
1191 | 11.6k | core->argv[core->argc][len+elide] = 0; |
1192 | 11.6k | core->argc++; |
1193 | | |
1194 | 11.6k | return 0; |
1195 | 11.6k | } |
1196 | | |
1197 | | int |
1198 | | gs_lib_ctx_stash_exe(gs_lib_ctx_t *ctx, const char *arg) |
1199 | 683 | { |
1200 | 683 | gs_lib_ctx_core_t *core; |
1201 | 683 | size_t len; |
1202 | 683 | const char *p, *word; |
1203 | 683 | const char *sep = gp_file_name_directory_separator(); |
1204 | 683 | size_t seplen = strlen(sep); |
1205 | | |
1206 | 683 | if (ctx == NULL || ctx->core == NULL || arg == NULL) |
1207 | 0 | return 0; |
1208 | | |
1209 | | /* Sanitize arg */ |
1210 | 683 | p = arg; |
1211 | 683 | word = NULL; |
1212 | 2.04k | for (p = arg; *p; p++) { |
1213 | 1.36k | if (memcmp(sep, p, seplen) == 0) { |
1214 | 0 | word = p+seplen; |
1215 | 0 | p += seplen-1; |
1216 | 0 | } |
1217 | | #if defined(__WIN32__) || defined(__OS2__) || defined(METRO) |
1218 | | if (*p == '\\') |
1219 | | word = p+1; |
1220 | | #endif |
1221 | 1.36k | } |
1222 | 683 | len = p - (word ? word : arg) + 1; |
1223 | 683 | if (word) |
1224 | 0 | len += 5; |
1225 | | |
1226 | 683 | core = ctx->core; |
1227 | 683 | if (core->arg_max == core->argc) { |
1228 | 683 | char **argv; |
1229 | 683 | int newlen = core->arg_max * 2; |
1230 | 683 | if (newlen == 0) |
1231 | 683 | newlen = 4; |
1232 | 683 | argv = (char **)gs_alloc_bytes(ctx->core->memory, sizeof(char *) * newlen, |
1233 | 683 | "gs_lib_ctx_args"); |
1234 | 683 | if (argv == NULL) |
1235 | 0 | return gs_error_VMerror; |
1236 | 683 | if (core->argc > 0) { |
1237 | 0 | memcpy(argv, core->argv, sizeof(char *) * core->argc); |
1238 | 0 | gs_free_object(ctx->memory, core->argv, "gs_lib_ctx_args"); |
1239 | 0 | } |
1240 | 683 | core->argv = argv; |
1241 | 683 | core->arg_max = newlen; |
1242 | 683 | } |
1243 | | |
1244 | 683 | core->argv[core->argc] = (char *)gs_alloc_bytes(ctx->core->memory, len, |
1245 | 683 | "gs_lib_ctx_arg"); |
1246 | 683 | if (core->argv[core->argc] == NULL) |
1247 | 0 | return gs_error_VMerror; |
1248 | 683 | if (word) |
1249 | 0 | strcpy(core->argv[core->argc], "path/"); |
1250 | 683 | else |
1251 | 683 | core->argv[core->argc][0] = 0; |
1252 | 683 | strcat(core->argv[core->argc], word ? word : arg); |
1253 | 683 | core->argc++; |
1254 | | |
1255 | 683 | return 0; |
1256 | 683 | } |
1257 | | |
1258 | | int gs_lib_ctx_get_args(gs_lib_ctx_t *ctx, const char * const **argv) |
1259 | 0 | { |
1260 | 0 | gs_lib_ctx_core_t *core; |
1261 | |
|
1262 | 0 | if (ctx == NULL || ctx->core == NULL || argv == NULL) |
1263 | 0 | return 0; |
1264 | | |
1265 | 0 | core = ctx->core; |
1266 | 0 | *argv = (const char * const *)core->argv; |
1267 | 0 | return core->argc; |
1268 | 0 | } |
1269 | | |
1270 | | int gs_lib_ctx_register_callout(gs_memory_t *mem, gs_callout_fn fn, void *arg) |
1271 | 0 | { |
1272 | 0 | gs_lib_ctx_core_t *core; |
1273 | 0 | gs_callout_list_t *entry; |
1274 | |
|
1275 | 0 | if (mem == NULL || mem->gs_lib_ctx == NULL || |
1276 | 0 | mem->gs_lib_ctx->core == NULL || fn == NULL) |
1277 | 0 | return 0; |
1278 | | |
1279 | 0 | core = mem->gs_lib_ctx->core; |
1280 | 0 | entry = (gs_callout_list_t *)gs_alloc_bytes(core->memory, |
1281 | 0 | sizeof(*entry), |
1282 | 0 | "gs_callout_list_t"); |
1283 | 0 | if (entry == NULL) |
1284 | 0 | return_error(gs_error_VMerror); |
1285 | 0 | entry->next = core->callouts; |
1286 | 0 | entry->callout = fn; |
1287 | 0 | entry->handle = arg; |
1288 | 0 | core->callouts = entry; |
1289 | |
|
1290 | 0 | return 0; |
1291 | 0 | } |
1292 | | |
1293 | | void gs_lib_ctx_deregister_callout(gs_memory_t *mem, gs_callout_fn fn, void *arg) |
1294 | 0 | { |
1295 | 0 | gs_lib_ctx_core_t *core; |
1296 | 0 | gs_callout_list_t **entry; |
1297 | |
|
1298 | 0 | if (mem == NULL || mem->gs_lib_ctx == NULL || |
1299 | 0 | mem->gs_lib_ctx->core == NULL || fn == NULL) |
1300 | 0 | return; |
1301 | | |
1302 | 0 | core = mem->gs_lib_ctx->core; |
1303 | 0 | entry = &core->callouts; |
1304 | 0 | while (*entry) { |
1305 | 0 | if ((*entry)->callout == fn && (*entry)->handle == arg) { |
1306 | 0 | gs_callout_list_t *next = (*entry)->next; |
1307 | 0 | gs_free_object(core->memory, *entry, "gs_callout_list_t"); |
1308 | 0 | *entry = next; |
1309 | 0 | } else { |
1310 | 0 | entry = &(*entry)->next; |
1311 | 0 | } |
1312 | 0 | } |
1313 | 0 | } |
1314 | | |
1315 | | int gs_lib_ctx_callout(gs_memory_t *mem, const char *dev_name, |
1316 | | int id, int size, void *data) |
1317 | 0 | { |
1318 | 0 | gs_lib_ctx_core_t *core; |
1319 | 0 | gs_callout_list_t *entry; |
1320 | |
|
1321 | 0 | if (mem == NULL || mem->gs_lib_ctx == NULL || mem->gs_lib_ctx->core == NULL) |
1322 | 0 | return -1; |
1323 | | |
1324 | 0 | core = mem->gs_lib_ctx->core; |
1325 | 0 | entry = core->callouts; |
1326 | 0 | while (entry) { |
1327 | 0 | int code = entry->callout(mem->gs_lib_ctx->top_of_system, |
1328 | 0 | entry->handle, dev_name, id, size, data); |
1329 | 0 | if (code >= 0) |
1330 | 0 | return code; |
1331 | 0 | if (code != gs_error_unknownerror) |
1332 | 0 | return code; |
1333 | 0 | entry = entry->next; |
1334 | 0 | } |
1335 | 0 | return -1; |
1336 | 0 | } |
1337 | | |
1338 | | int gs_lib_ctx_nts_adjust(gs_memory_t *mem, int adjust) |
1339 | 0 | { |
1340 | 0 | gs_lib_ctx_core_t *core; |
1341 | 0 | int ret = 0; |
1342 | 0 | gs_globals *globals; |
1343 | |
|
1344 | 0 | if (adjust == 0) |
1345 | 0 | return 0; |
1346 | | |
1347 | 0 | if (mem == NULL || mem->gs_lib_ctx == NULL || mem->gs_lib_ctx->core == NULL) |
1348 | 0 | return_error(gs_error_unknownerror); |
1349 | | |
1350 | 0 | core = mem->gs_lib_ctx->core; |
1351 | 0 | globals = core->globals; |
1352 | 0 | if (globals == NULL) |
1353 | 0 | return 0; /* No globals means just once instance. Adjustment is pointless. */ |
1354 | | |
1355 | 0 | gp_global_lock(globals); |
1356 | 0 | if (adjust > 0 && globals->non_threadsafe_count != 0) |
1357 | 0 | ret = gs_error_unknownerror; /* We already have one non threadsafe device running. */ |
1358 | 0 | else if (adjust < 0 && globals->non_threadsafe_count == 0) |
1359 | 0 | ret = gs_error_unknownerror; /* This indicates something has gone very wrong! */ |
1360 | 0 | else |
1361 | 0 | globals->non_threadsafe_count += adjust; |
1362 | 0 | gp_global_unlock(globals); |
1363 | |
|
1364 | 0 | if (ret) |
1365 | 0 | ret = gs_note_error(ret); |
1366 | 0 | return ret; |
1367 | 0 | } |
1368 | | |
1369 | | void gs_globals_init(gs_globals *globals) |
1370 | 1 | { |
1371 | 1 | memset(globals, 0, sizeof(*globals)); |
1372 | 1 | } |