/src/ghostpdl/psi/imain.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Common support for interpreter front ends */ |
18 | | |
19 | | |
20 | | #include "malloc_.h" |
21 | | #include "memory_.h" |
22 | | #include "string_.h" |
23 | | #include "ghost.h" |
24 | | #include "gp.h" |
25 | | #include "gscdefs.h" /* for gs_init_file */ |
26 | | #include "gslib.h" |
27 | | #include "gsmatrix.h" /* for gxdevice.h */ |
28 | | #include "gsutil.h" /* for bytes_compare */ |
29 | | #include "gspaint.h" /* for gs_erasepage */ |
30 | | #include "gxdevice.h" |
31 | | #include "gxdevsop.h" /* for gxdso_* enums */ |
32 | | #include "gxclpage.h" |
33 | | #include "gdevprn.h" |
34 | | #include "gxalloc.h" |
35 | | #include "gxiodev.h" /* for iodev struct */ |
36 | | #include "gzstate.h" |
37 | | #include "ierrors.h" |
38 | | #include "oper.h" |
39 | | #include "iconf.h" /* for gs_init_* imports */ |
40 | | #include "idebug.h" |
41 | | #include "iddict.h" |
42 | | #include "iname.h" /* for name_init */ |
43 | | #include "dstack.h" |
44 | | #include "estack.h" |
45 | | #include "ostack.h" /* put here for files.h */ |
46 | | #include "stream.h" /* for files.h */ |
47 | | #include "files.h" |
48 | | #include "ialloc.h" |
49 | | #include "iinit.h" |
50 | | #include "strimpl.h" /* for sfilter.h */ |
51 | | #include "sfilter.h" /* for iscan.h */ |
52 | | #include "iscan.h" |
53 | | #include "main.h" |
54 | | #include "store.h" |
55 | | #include "isave.h" /* for prototypes */ |
56 | | #include "interp.h" |
57 | | #include "ivmspace.h" |
58 | | #include "idisp.h" /* for setting display device callback */ |
59 | | #include "iplugin.h" |
60 | | #include "zfile.h" |
61 | | |
62 | | #include "valgrind.h" |
63 | | |
64 | | /* ------ Exported data ------ */ |
65 | | |
66 | | /** using backpointers retrieve minst from any memory pointer |
67 | | * |
68 | | */ |
69 | | gs_main_instance* |
70 | | get_minst_from_memory(const gs_memory_t *mem) |
71 | 475M | { |
72 | 475M | return (gs_main_instance*)mem->gs_lib_ctx->top_of_system; |
73 | 475M | } |
74 | | |
75 | | /** construct main instance caller needs to retain */ |
76 | | gs_main_instance * |
77 | | gs_main_alloc_instance(gs_memory_t *mem) |
78 | 162k | { |
79 | 162k | gs_main_instance *minst; |
80 | 162k | if (mem == NULL) |
81 | 0 | return NULL; |
82 | | |
83 | 162k | minst = (gs_main_instance *)gs_alloc_bytes_immovable(mem, |
84 | 162k | sizeof(gs_main_instance), |
85 | 162k | "init_main_instance"); |
86 | 162k | if (minst == NULL) |
87 | 0 | return NULL; |
88 | 162k | memset(minst, 0, sizeof(gs_main_instance)); |
89 | 162k | memcpy(minst, &gs_main_instance_init_values, sizeof(gs_main_instance_init_values)); |
90 | 162k | minst->heap = mem; |
91 | 162k | mem->gs_lib_ctx->top_of_system = minst; |
92 | 162k | return minst; |
93 | 162k | } |
94 | | |
95 | | op_array_table * |
96 | | get_op_array(const gs_memory_t *mem, int size) |
97 | 248M | { |
98 | 248M | gs_main_instance *minst = get_minst_from_memory(mem); |
99 | 248M | return op_index_op_array_table(minst->i_ctx_p,size); |
100 | 248M | } |
101 | | |
102 | | op_array_table * |
103 | | get_global_op_array(const gs_memory_t *mem) |
104 | 325k | { |
105 | 325k | gs_main_instance *minst = get_minst_from_memory(mem); |
106 | 325k | return &minst->i_ctx_p->op_array_table_global; |
107 | 325k | } |
108 | | |
109 | | op_array_table * |
110 | | get_local_op_array(const gs_memory_t *mem) |
111 | 325k | { |
112 | 325k | gs_main_instance *minst = get_minst_from_memory(mem); |
113 | 325k | return &minst->i_ctx_p->op_array_table_local; |
114 | 325k | } |
115 | | |
116 | | /* ------ Forward references ------ */ |
117 | | |
118 | | static int gs_run_init_file(gs_main_instance *, int *, ref *); |
119 | | void print_resource_usage(const gs_main_instance *, |
120 | | gs_dual_memory_t *, const char *); |
121 | | |
122 | | /* ------ Initialization ------ */ |
123 | | |
124 | | /* Initialization to be done before anything else. */ |
125 | | int |
126 | | gs_main_init0(gs_main_instance * minst, gp_file * in, gp_file * out, gp_file * err, |
127 | | int max_lib_paths) |
128 | 162k | { |
129 | 162k | ref *array; |
130 | 162k | int code = 0; |
131 | | |
132 | 162k | if (gs_debug_c(gs_debug_flag_init_details)) |
133 | 162k | dmprintf1(minst->heap, "%% Init phase 0 started, instance "PRI_INTPTR"\n", |
134 | 162k | (intptr_t)minst); |
135 | | |
136 | | /* Do platform-dependent initialization. */ |
137 | | /* We have to do this as the very first thing, */ |
138 | | /* because it detects attempts to run 80N86 executables (N>0) */ |
139 | | /* on incompatible processors. */ |
140 | 162k | gp_init(); |
141 | | |
142 | | /* Initialize the imager. */ |
143 | | |
144 | | /* Reset debugging flags */ |
145 | 162k | #ifdef PACIFY_VALGRIND |
146 | 162k | VALGRIND_HG_DISABLE_CHECKING(gs_debug, 128); |
147 | 162k | #endif |
148 | 162k | memset(gs_debug, 0, 128); |
149 | 162k | gs_log_errors = 0; /* gs_debug['#'] = 0 */ |
150 | | |
151 | 162k | gp_get_realtime(minst->base_time); |
152 | | |
153 | | /* Initialize the file search paths. */ |
154 | 162k | array = (ref *) gs_alloc_byte_array(minst->heap, max_lib_paths, sizeof(ref), |
155 | 162k | "lib_path array"); |
156 | 162k | if (array == 0) { |
157 | 0 | gs_lib_finit(1, gs_error_VMerror, minst->heap); |
158 | 0 | code = gs_note_error(gs_error_VMerror); |
159 | 0 | goto fail; |
160 | 0 | } |
161 | 162k | make_array(&minst->lib_path.container, avm_foreign, max_lib_paths, |
162 | 162k | array); |
163 | 162k | make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0, |
164 | 162k | minst->lib_path.container.value.refs); |
165 | 162k | minst->lib_path.env = NULL; |
166 | 162k | minst->lib_path.final = NULL; |
167 | 162k | minst->lib_path.count = 0; |
168 | 162k | minst->lib_path.first_is_current = 0; |
169 | 162k | minst->user_errors = 1; |
170 | 162k | minst->init_done = 0; |
171 | | |
172 | 162k | fail: |
173 | 162k | if (gs_debug_c(gs_debug_flag_init_details)) |
174 | 162k | dmprintf2(minst->heap, "%% Init phase 0 %s, instance "PRI_INTPTR"\n", |
175 | 162k | code < 0 ? "failed" : "done", (intptr_t)minst); |
176 | | |
177 | 162k | return code; |
178 | 162k | } |
179 | | |
180 | | /* Initialization to be done before constructing any objects. */ |
181 | | int |
182 | | gs_main_init1(gs_main_instance * minst) |
183 | 2.88M | { |
184 | 2.88M | gs_dual_memory_t idmem; |
185 | 2.88M | name_table *nt = NULL; |
186 | 2.88M | int code; |
187 | | |
188 | 2.88M | if (minst->init_done >= 1) |
189 | 2.71M | return 0; |
190 | | |
191 | 162k | if (gs_debug_c(gs_debug_flag_init_details)) |
192 | 162k | dmprintf1(minst->heap, "%% Init phase 1 started, instance "PRI_INTPTR"\n", |
193 | 162k | (intptr_t)minst); |
194 | | |
195 | 162k | code = ialloc_init(&idmem, minst->heap, |
196 | 162k | minst->memory_clump_size, gs_have_level2()); |
197 | | |
198 | 162k | if (code < 0) |
199 | 0 | goto fail_early; |
200 | | |
201 | 162k | code = gs_lib_init1((gs_memory_t *)idmem.space_system); |
202 | 162k | if (code < 0) |
203 | 0 | goto fail; |
204 | 162k | alloc_save_init(&idmem); |
205 | 162k | { |
206 | 162k | gs_memory_t *mem = (gs_memory_t *)idmem.space_system; |
207 | 162k | nt = names_init(minst->name_table_size, idmem.space_system); |
208 | | |
209 | 162k | if (nt == 0) { |
210 | 0 | code = gs_note_error(gs_error_VMerror); |
211 | 0 | goto fail; |
212 | 0 | } |
213 | 162k | mem->gs_lib_ctx->gs_name_table = nt; |
214 | 162k | code = gs_register_struct_root(mem, &mem->gs_lib_ctx->name_table_root, |
215 | 162k | (void **)&mem->gs_lib_ctx->gs_name_table, |
216 | 162k | "the_gs_name_table"); |
217 | 162k | if (code < 0) |
218 | 0 | goto fail; |
219 | 162k | mem->gs_lib_ctx->client_check_file_permission = z_check_file_permissions; |
220 | 162k | } |
221 | 0 | code = obj_init(&minst->i_ctx_p, &idmem); /* requires name_init */ |
222 | 162k | if (code < 0) |
223 | 0 | goto fail; |
224 | 162k | minst->init_done = 1; |
225 | 162k | code = i_plugin_init(minst->i_ctx_p); |
226 | 162k | if (code < 0) |
227 | 0 | goto fail; |
228 | 162k | code = i_iodev_init(&idmem); |
229 | 162k | if (code < 0) { |
230 | 0 | fail: |
231 | 0 | names_free(nt); |
232 | 0 | if (minst->i_ctx_p == NULL) |
233 | 0 | ialloc_finit(&idmem); |
234 | 0 | } |
235 | 162k | fail_early: |
236 | | |
237 | 162k | if (gs_debug_c(gs_debug_flag_init_details)) |
238 | 162k | dmprintf2(minst->heap, "%% Init phase 1 %s, instance "PRI_INTPTR"\n", |
239 | 162k | code < 0 ? "failed" : "done", (intptr_t)minst); |
240 | | |
241 | 162k | return code; |
242 | 162k | } |
243 | | |
244 | | /* |
245 | | * Invoke the interpreter. This layer doesn't do much (previously stdio |
246 | | * callouts were handled here instead of in the stream processing. |
247 | | */ |
248 | | static int |
249 | | gs_main_interpret(gs_main_instance *minst, ref * pref, int user_errors, |
250 | | int *pexit_code, ref * perror_object) |
251 | 2.92M | { |
252 | 2.92M | int code; |
253 | | |
254 | | /* set interpreter pointer to lib_path */ |
255 | 2.92M | minst->i_ctx_p->lib_path = &minst->lib_path; |
256 | | |
257 | 2.92M | code = gs_interpret(&minst->i_ctx_p, pref, |
258 | 2.92M | user_errors, pexit_code, perror_object); |
259 | 2.92M | return code; |
260 | 2.92M | } |
261 | | |
262 | | /* gcc wants prototypes for all external functions. */ |
263 | | int gs_main_init2aux(gs_main_instance * minst); |
264 | | |
265 | | static const op_array_table empty_table = { { { 0 } } }; |
266 | | |
267 | | /* This is an external function to work around */ |
268 | | /* a bug in gcc 4.5.1 optimizer. See bug 692684. */ |
269 | 162k | int gs_main_init2aux(gs_main_instance * minst) { |
270 | 162k | i_ctx_t * i_ctx_p = minst->i_ctx_p; |
271 | | |
272 | 162k | if (minst->init_done < 2) { |
273 | 162k | int code, exit_code; |
274 | 162k | ref error_object, ifa; |
275 | | |
276 | | /* Set up enough so that we can safely be garbage collected */ |
277 | 162k | i_ctx_p->op_array_table_global = empty_table; |
278 | 162k | i_ctx_p->op_array_table_local = empty_table; |
279 | | |
280 | 162k | code = zop_init(i_ctx_p); |
281 | 162k | if (code < 0) |
282 | 0 | return code; |
283 | 162k | code = op_init(i_ctx_p); /* requires obj_init */ |
284 | 162k | if (code < 0) |
285 | 0 | return code; |
286 | | |
287 | | /* Set up the array of additional initialization files. */ |
288 | 162k | make_const_string(&ifa, a_readonly | avm_foreign, gs_init_files_sizeof - 2, gs_init_files); |
289 | 162k | code = i_initial_enter_name(i_ctx_p, "INITFILES", &ifa); |
290 | 162k | if (code < 0) |
291 | 0 | return code; |
292 | | |
293 | | /* Set up the array of emulator names. */ |
294 | 162k | make_const_string(&ifa, a_readonly | avm_foreign, gs_emulators_sizeof - 2, gs_emulators); |
295 | 162k | code = i_initial_enter_name(i_ctx_p, "EMULATORS", &ifa); |
296 | 162k | if (code < 0) |
297 | 0 | return code; |
298 | | |
299 | | /* Pass the search path. */ |
300 | 162k | code = i_initial_enter_name(i_ctx_p, "LIBPATH", &minst->lib_path.list); |
301 | 162k | if (code < 0) |
302 | 0 | return code; |
303 | | |
304 | | /* Execute the standard initialization file. */ |
305 | 162k | code = gs_run_init_file(minst, &exit_code, &error_object); |
306 | 162k | if (code < 0) |
307 | 20 | return code; |
308 | 162k | minst->init_done = 2; |
309 | | |
310 | | /* NB this is to be done with device parameters |
311 | | * both minst->display and display_set_callback() are going away |
312 | | */ |
313 | 162k | if ((code = reopen_device_if_required(minst)) < 0) |
314 | 0 | return code; |
315 | | |
316 | 162k | if ((code = gs_main_run_string(minst, |
317 | 162k | "JOBSERVER " |
318 | 162k | " { false 0 .startnewjob } " |
319 | 162k | " { NOOUTERSAVE not { save pop } if } " |
320 | 162k | "ifelse", 0, &exit_code, |
321 | 162k | &error_object)) < 0) |
322 | 0 | return code; |
323 | 162k | } |
324 | 162k | return 0; |
325 | 162k | } |
326 | | |
327 | | int |
328 | | gs_main_set_language_param(gs_main_instance *minst, |
329 | | gs_param_list *plist) |
330 | 0 | { |
331 | 0 | ref value; |
332 | 0 | int code = 0; |
333 | 0 | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
334 | 0 | uint space = icurrent_space; |
335 | 0 | gs_param_enumerator_t enumerator; |
336 | 0 | gs_param_key_t key; |
337 | 0 | gs_lib_ctx_t *ctx = minst->heap->gs_lib_ctx; |
338 | 0 | ref error_object; |
339 | | |
340 | | /* If we're up and running as a jobserver, exit encapsulation. */ |
341 | 0 | if (minst->init_done > 1) { |
342 | 0 | code = gs_main_run_string(minst, |
343 | 0 | "JOBSERVER {true 0 startjob pop} if", |
344 | 0 | 0, &code, &error_object); |
345 | 0 | if (code < 0) return code; |
346 | 0 | } |
347 | | |
348 | 0 | ialloc_set_space(idmemory, avm_system); |
349 | |
|
350 | 0 | param_init_enumerator(&enumerator); |
351 | 0 | while ((code = param_get_next_key(plist, &enumerator, &key)) == 0) { |
352 | 0 | char string_key[256]; /* big enough for any reasonable key */ |
353 | 0 | gs_param_typed_value pvalue; |
354 | |
|
355 | 0 | if (key.size > sizeof(string_key) - 1) { |
356 | 0 | code = gs_note_error(gs_error_rangecheck); |
357 | 0 | break; |
358 | 0 | } |
359 | 0 | memcpy(string_key, key.data, key.size); |
360 | 0 | string_key[key.size] = 0; |
361 | 0 | if ((code = param_read_typed(plist, string_key, &pvalue)) != 0) { |
362 | 0 | code = (code > 0 ? gs_note_error(gs_error_unknownerror) : code); |
363 | 0 | break; |
364 | 0 | } |
365 | 0 | switch (pvalue.type) { |
366 | 0 | case gs_param_type_null: |
367 | 0 | make_null(&value); |
368 | 0 | break; |
369 | 0 | case gs_param_type_bool: |
370 | 0 | if (pvalue.value.b) |
371 | 0 | make_true(&value); |
372 | 0 | else |
373 | 0 | make_false(&value); |
374 | 0 | break; |
375 | 0 | case gs_param_type_int: |
376 | 0 | make_int(&value, (ps_int)pvalue.value.i); |
377 | 0 | break; |
378 | 0 | case gs_param_type_i64: |
379 | 0 | make_int(&value, (ps_int)pvalue.value.i64); |
380 | 0 | break; |
381 | 0 | case gs_param_type_long: |
382 | 0 | make_int(&value, (ps_int)pvalue.value.l); |
383 | 0 | break; |
384 | 0 | case gs_param_type_size_t: |
385 | 0 | make_int(&value, (ps_int)pvalue.value.z); |
386 | 0 | break; |
387 | 0 | case gs_param_type_float: |
388 | 0 | make_real(&value, pvalue.value.f); |
389 | 0 | break; |
390 | 0 | case gs_param_type_dict: |
391 | | /* We don't support dicts for now */ |
392 | 0 | continue; |
393 | 0 | case gs_param_type_dict_int_keys: |
394 | | /* We don't support dicts with int keys for now */ |
395 | 0 | continue; |
396 | 0 | case gs_param_type_array: |
397 | | /* We don't support arrays for now */ |
398 | 0 | continue; |
399 | 0 | case gs_param_type_string: |
400 | 0 | if (pvalue.value.s.data == NULL || pvalue.value.s.size == 0) |
401 | 0 | make_empty_string(&value, a_readonly); |
402 | 0 | else { |
403 | 0 | size_t len = pvalue.value.s.size; |
404 | 0 | byte *body = ialloc_string(len, "-s"); |
405 | |
|
406 | 0 | if (body == NULL) |
407 | 0 | return gs_error_Fatal; |
408 | 0 | memcpy(body, pvalue.value.s.data, len); |
409 | 0 | make_const_string(&value, a_readonly | avm_system, len, body); |
410 | 0 | } |
411 | 0 | break; |
412 | 0 | case gs_param_type_name: |
413 | 0 | code = name_ref(ctx->memory, pvalue.value.n.data, pvalue.value.n.size, &value, 1); |
414 | 0 | break; |
415 | 0 | case gs_param_type_int_array: |
416 | | /* We don't support arrays for now */ |
417 | 0 | continue; |
418 | 0 | case gs_param_type_float_array: |
419 | | /* We don't support arrays for now */ |
420 | 0 | continue; |
421 | 0 | case gs_param_type_string_array: |
422 | | /* We don't support arrays for now */ |
423 | 0 | continue; |
424 | 0 | default: |
425 | 0 | continue; |
426 | 0 | } |
427 | 0 | if (code < 0) |
428 | 0 | break; |
429 | | |
430 | 0 | ialloc_set_space(idmemory, space); |
431 | | /* Enter the name in systemdict. */ |
432 | 0 | i_initial_enter_name_copy(minst->i_ctx_p, string_key, &value); |
433 | 0 | } |
434 | | |
435 | 0 | if (minst->init_done > 1) { |
436 | 0 | int code2 = 0; |
437 | 0 | code2 = gs_main_run_string(minst, |
438 | 0 | "JOBSERVER {false 0 startjob pop} if", |
439 | 0 | 0, &code2, &error_object); |
440 | 0 | if (code >= 0) |
441 | 0 | code = code2; |
442 | 0 | } |
443 | |
|
444 | 0 | return code; |
445 | 0 | } |
446 | | |
447 | | static int |
448 | | gs_main_push_params(gs_main_instance *minst) |
449 | 280k | { |
450 | 280k | int code; |
451 | 280k | gs_c_param_list *plist; |
452 | | |
453 | 280k | plist = minst->param_list; |
454 | 280k | if (!plist) |
455 | 280k | return 0; |
456 | | |
457 | 0 | code = gs_putdeviceparams(minst->i_ctx_p->pgs->device, |
458 | 0 | (gs_param_list *)plist); |
459 | 0 | if (code < 0) |
460 | 0 | return code; |
461 | | |
462 | 0 | code = gs_main_set_language_param(minst, (gs_param_list *)plist); |
463 | 0 | if (code < 0) |
464 | 0 | return code; |
465 | | |
466 | 0 | gs_c_param_list_release(plist); |
467 | |
|
468 | 0 | return code; |
469 | 0 | } |
470 | | |
471 | | int |
472 | | gs_main_init2(gs_main_instance * minst) |
473 | 280k | { |
474 | 280k | i_ctx_t *i_ctx_p; |
475 | 280k | int code = gs_main_init1(minst); |
476 | | |
477 | 280k | if (code < 0) |
478 | 0 | return code; |
479 | | |
480 | 280k | code = gs_main_push_params(minst); |
481 | 280k | if (code < 0) |
482 | 0 | return code; |
483 | | |
484 | 280k | if (minst->init_done >= 2) |
485 | 118k | return 0; |
486 | | |
487 | 162k | if (gs_debug_c(gs_debug_flag_init_details)) |
488 | 162k | dmprintf1(minst->heap, "%% Init phase 2 started, instance "PRI_INTPTR"\n", |
489 | 162k | (intptr_t)minst); |
490 | | |
491 | 162k | code = gs_main_init2aux(minst); |
492 | 162k | if (code < 0) |
493 | 20 | goto fail; |
494 | | |
495 | 162k | i_ctx_p = minst->i_ctx_p; /* reopen_device_if_display or run_string may change it */ |
496 | | |
497 | | /* Now process the initial saved-pages=... argument, if any as well as saved-pages-test */ |
498 | 162k | { |
499 | 162k | gx_device *pdev = gs_currentdevice(minst->i_ctx_p->pgs); /* get the current device */ |
500 | 162k | gx_device_printer *ppdev = (gx_device_printer *)pdev; |
501 | | |
502 | 162k | if (minst->saved_pages_test_mode) { |
503 | 0 | if ((dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_saved_pages, NULL, 0) <= 0)) { |
504 | | /* no warning or error if saved-pages-test mode is used, just disable it */ |
505 | 0 | minst->saved_pages_test_mode = false; /* device doesn't support it */ |
506 | 0 | } else { |
507 | 0 | if ((code = gx_saved_pages_param_process(ppdev, (byte *)"begin", 5)) < 0) |
508 | 0 | goto fail; |
509 | 0 | if (code > 0) |
510 | 0 | if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0) |
511 | 0 | goto fail; |
512 | 0 | } |
513 | 162k | } else if (minst->saved_pages_initial_arg != NULL) { |
514 | 0 | if (dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_saved_pages, NULL, 0) <= 0) { |
515 | 0 | while (pdev->child) |
516 | 0 | pdev = pdev->child; /* so we get the real device name */ |
517 | 0 | outprintf(minst->heap, |
518 | 0 | " --saved-pages not supported by the '%s' device.\n", |
519 | 0 | pdev->dname); |
520 | 0 | code = gs_error_Fatal; |
521 | 0 | goto fail; |
522 | 0 | } |
523 | 0 | code = gx_saved_pages_param_process(ppdev, (byte *)minst->saved_pages_initial_arg, |
524 | 0 | strlen(minst->saved_pages_initial_arg)); |
525 | 0 | if (code > 0) |
526 | 0 | if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0) |
527 | 0 | goto fail; |
528 | 0 | } |
529 | 162k | } |
530 | | |
531 | 162k | if (code >= 0) { |
532 | 162k | if (gs_debug_c(':')) |
533 | 0 | print_resource_usage(minst, i_ctx_p ? &gs_imemory : NULL, "Start"); |
534 | 162k | gp_readline_init(&minst->readline_data, minst->heap); /* lgtm [cpp/useless-expression] */ |
535 | 162k | } |
536 | | |
537 | 162k | fail: |
538 | 162k | if (gs_debug_c(gs_debug_flag_init_details)) |
539 | 162k | dmprintf2(minst->heap, "%% Init phase 2 %s, instance "PRI_INTPTR"\n", |
540 | 162k | code < 0 ? "failed" : "done", (intptr_t)minst); |
541 | | |
542 | 162k | return code; |
543 | 162k | } |
544 | | |
545 | | /* ------ Search paths ------ */ |
546 | | |
547 | | #define LIB_PATH_EXTEND 5 |
548 | | |
549 | | /* If the existing array is full, extend it */ |
550 | | static int |
551 | | extend_path_list_container (gs_main_instance * minst, gs_file_path * pfp) |
552 | 0 | { |
553 | 0 | uint len = r_size(&minst->lib_path.container); |
554 | 0 | ref *paths, *opaths = minst->lib_path.container.value.refs; |
555 | | |
556 | | /* Add 5 entries at a time to reduce VM thrashing */ |
557 | 0 | paths = (ref *) gs_alloc_byte_array(minst->heap, len + LIB_PATH_EXTEND, sizeof(ref), |
558 | 0 | "extend_path_list_container array"); |
559 | |
|
560 | 0 | if (paths == 0) { |
561 | 0 | return_error(gs_error_VMerror); |
562 | 0 | } |
563 | 0 | make_array(&minst->lib_path.container, avm_foreign, len + LIB_PATH_EXTEND, paths); |
564 | 0 | make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0, |
565 | 0 | minst->lib_path.container.value.refs); |
566 | |
|
567 | 0 | memcpy(paths, opaths, len * sizeof(ref)); |
568 | 0 | r_set_size(&minst->lib_path.list, len); |
569 | |
|
570 | 0 | gs_free_object (minst->heap, opaths, "extend_path_list_container"); |
571 | 0 | return(0); |
572 | 0 | } |
573 | | |
574 | | static int |
575 | | lib_path_insert_copy_of_string(gs_main_instance *minst, int pos, size_t strlen, const char *str) |
576 | 19.0M | { |
577 | 19.0M | ref *paths; |
578 | 19.0M | int listlen = r_size(&minst->lib_path.list); /* The real number of entries currently in the list */ |
579 | 19.0M | byte *newstr; |
580 | 19.0M | int code; |
581 | | |
582 | | /* Extend the container if required */ |
583 | 19.0M | if (listlen == r_size(&minst->lib_path.container)) { |
584 | 0 | code = extend_path_list_container(minst, &minst->lib_path); |
585 | 0 | if (code < 0) { |
586 | 0 | emprintf(minst->heap, "\nAdding path to search paths failed.\n"); |
587 | 0 | return(code); |
588 | 0 | } |
589 | 0 | } |
590 | | |
591 | | /* Copy the string */ |
592 | 19.0M | newstr = gs_alloc_string(minst->heap, strlen, "lib_path_add"); |
593 | 19.0M | if (newstr == NULL) |
594 | 0 | return gs_error_VMerror; |
595 | 19.0M | memcpy(newstr, str, strlen); |
596 | | |
597 | | /* Shuffle up the entries */ |
598 | 19.0M | paths = &minst->lib_path.container.value.refs[pos]; |
599 | 19.0M | if (pos != listlen) |
600 | 0 | memmove(paths + 1, paths, (listlen-pos) * sizeof(*paths)); |
601 | | |
602 | | /* And insert the new string, converted to a PS thing */ |
603 | 19.0M | make_const_string(paths, avm_foreign | a_readonly, |
604 | 19.0M | strlen, |
605 | 19.0M | newstr); |
606 | | |
607 | | /* Update the list length */ |
608 | 19.0M | r_set_size(&minst->lib_path.list, listlen+1); |
609 | | |
610 | 19.0M | return 0; |
611 | 19.0M | } |
612 | | |
613 | | /* Internal routine to add a set of directories to a search list. */ |
614 | | /* Returns 0 or an error code. */ |
615 | | |
616 | | static int |
617 | | lib_path_add(gs_main_instance * minst, const char *dirs) |
618 | 4.38M | { |
619 | 4.38M | gs_file_path * pfp = &minst->lib_path; |
620 | 4.38M | uint len = r_size(&pfp->list); |
621 | 4.38M | const char *dpath = dirs; |
622 | 4.38M | int code; |
623 | | |
624 | 4.38M | if (dirs == 0) |
625 | 0 | return 0; |
626 | | |
627 | 19.0M | for (;;) { /* Find the end of the next directory name. */ |
628 | 19.0M | const char *npath = dpath; |
629 | | |
630 | 652M | while (*npath != 0 && *npath != gp_file_name_list_separator) |
631 | 633M | npath++; |
632 | 19.0M | if (npath > dpath) { |
633 | 19.0M | code = gs_add_control_path_len(minst->heap, gs_permit_file_reading, dpath, npath - dpath); |
634 | 19.0M | if (code < 0) return code; |
635 | | |
636 | 19.0M | code = lib_path_insert_copy_of_string(minst, len, npath - dpath, dpath); |
637 | 19.0M | if (code < 0) |
638 | 0 | return code; |
639 | 19.0M | len++; |
640 | | /* Update length/count so we don't lose entries if a later |
641 | | * iteration fails. */ |
642 | 19.0M | r_set_size(&pfp->list, len); |
643 | 19.0M | } |
644 | 19.0M | if (!*npath) |
645 | 4.38M | break; |
646 | 14.6M | dpath = npath + 1; |
647 | 14.6M | } |
648 | 4.38M | return 0; |
649 | 4.38M | } |
650 | | |
651 | | static void |
652 | | set_lib_path_length(gs_main_instance * minst, int newsize) |
653 | 1.62M | { |
654 | 1.62M | gs_file_path * pfp = &minst->lib_path; |
655 | 1.62M | uint len = r_size(&pfp->list); /* Yes, list, not container */ |
656 | 1.62M | uint i; |
657 | | |
658 | | /* Free any entries that are discarded by us shortening the list */ |
659 | 20.6M | for (i = newsize; i < len; i++) |
660 | 19.0M | gs_free_object(minst->heap, pfp->container.value.refs[i].value.bytes, "lib_path entry"); |
661 | 1.62M | r_set_size(&pfp->list, newsize); |
662 | 1.62M | } |
663 | | |
664 | | /* Add a library search path to the list. */ |
665 | | int |
666 | | gs_main_add_lib_path(gs_main_instance * minst, const char *lpath) |
667 | 0 | { |
668 | 0 | gs_file_path * pfp = &minst->lib_path; |
669 | 0 | int code; |
670 | | |
671 | | /* Throw away all the elements after the user ones. */ |
672 | 0 | set_lib_path_length(minst, pfp->count + pfp->first_is_current); |
673 | | |
674 | | /* Now add the new one */ |
675 | 0 | code = lib_path_add(minst, lpath); |
676 | 0 | if (code < 0) |
677 | 0 | return code; |
678 | | |
679 | | /* Update the count of user paths */ |
680 | 0 | pfp->count = r_size(&pfp->list) - pfp->first_is_current; |
681 | | |
682 | | /* Now add back in the others */ |
683 | 0 | return gs_main_set_lib_paths(minst); |
684 | 0 | } |
685 | | |
686 | | /* ------ Execution ------ */ |
687 | | |
688 | | extern_gx_io_device_table(); |
689 | | |
690 | | /* Complete the list of library search paths. */ |
691 | | /* This may involve adding the %rom%Resource/Init and %rom%lib/ paths (for COMPILE_INITS) */ |
692 | | /* and adding or removing the current directory as the first element (for -P and -P-). */ |
693 | | int |
694 | | gs_main_set_lib_paths(gs_main_instance * minst) |
695 | 1.46M | { |
696 | 1.46M | int code = 0; |
697 | 1.46M | int i, have_rom_device = 0; |
698 | | |
699 | | /* We are entered with a list potentially full of stuff already. |
700 | | * In general the list is of the form: |
701 | | * <optionally, the current directory> |
702 | | * <any -I specified directories, "count" of them> |
703 | | * <any directories from the environment variable> |
704 | | * <if there is a romfs, the romfs paths> |
705 | | * <any directories from the 'final' variable> |
706 | | * |
707 | | * Unfortunately, the value read from the environment variable may not |
708 | | * have actually been read the first time this function is called, so |
709 | | * we can see the value change over time. Short of a complete rewrite |
710 | | * this requires us to clear the latter part of the list and repopulate |
711 | | * it each time we run through. |
712 | | */ |
713 | | |
714 | | /* First step, ensure that we have the current directory as our |
715 | | * first entry, iff we need it. */ |
716 | 1.46M | if (minst->search_here_first && !minst->lib_path.first_is_current) { |
717 | | /* We should have a "gp_current_directory" at the start, and we haven't. |
718 | | * So insert one. */ |
719 | |
|
720 | 0 | code = gs_add_control_path_len(minst->heap, gs_permit_file_reading, |
721 | 0 | gp_current_directory_name, strlen(gp_current_directory_name)); |
722 | 0 | if (code < 0) return code; |
723 | | |
724 | 0 | code = lib_path_insert_copy_of_string(minst, 0, |
725 | 0 | strlen(gp_current_directory_name), |
726 | 0 | gp_current_directory_name); |
727 | 0 | if (code < 0) |
728 | 0 | return code; |
729 | 1.46M | } else if (!minst->search_here_first && minst->lib_path.first_is_current) { |
730 | | /* We have a "gp_current_directory" at the start, and we shouldn't have. |
731 | | * Remove it. */ |
732 | 0 | int listlen = r_size(&minst->lib_path.list); /* The real number of entries currently in the list */ |
733 | 0 | ref *paths = minst->lib_path.container.value.refs; |
734 | |
|
735 | 0 | gs_free_object(minst->heap, paths->value.bytes, "lib_path entry"); |
736 | 0 | --listlen; |
737 | 0 | memmove(paths, paths + 1, listlen * sizeof(*paths)); |
738 | 0 | r_set_size(&minst->lib_path.list, listlen); |
739 | |
|
740 | 0 | code = gs_remove_control_path_len(minst->heap, gs_permit_file_reading, |
741 | 0 | gp_current_directory_name, strlen(gp_current_directory_name)); |
742 | 0 | if (code < 0) return code; |
743 | 0 | } |
744 | 1.46M | minst->lib_path.first_is_current = minst->search_here_first; |
745 | | |
746 | | /* Now, the horrid bit. We throw away all the entries after the user entries */ |
747 | 1.46M | set_lib_path_length(minst, minst->lib_path.count + minst->lib_path.first_is_current); |
748 | | |
749 | | /* Now we (re)populate the end of the list. */ |
750 | 1.46M | if (minst->lib_path.env != 0) { |
751 | 0 | code = lib_path_add(minst, minst->lib_path.env); |
752 | 0 | if (code < 0) return code; |
753 | | |
754 | 0 | code = gs_add_control_path(minst->heap, gs_permit_file_reading, minst->lib_path.env); |
755 | 0 | if (code < 0) return code; |
756 | 0 | } |
757 | | /* now put the %rom%lib/ device path before the gs_lib_default_path on the list */ |
758 | 10.2M | for (i = 0; i < gx_io_device_table_count; i++) { |
759 | 10.2M | const gx_io_device *iodev = gx_io_device_table[i]; |
760 | 10.2M | const char *dname = iodev->dname; |
761 | 10.2M | const char *fcheckname = "Resource/Init/gs_init.ps"; |
762 | | |
763 | 10.2M | if (dname && strlen(dname) == 5 && !memcmp("%rom%", dname, 5)) { |
764 | 1.46M | struct stat pstat; |
765 | | /* gs_error_unregistered means no usable romfs is available */ |
766 | | /* gpdl always includes a real romfs, but it may or may have the Postscript |
767 | | resources in it, so we explicitly check for a fairly vital file |
768 | | */ |
769 | 1.46M | int code = iodev->procs.file_status((gx_io_device *)iodev, fcheckname, &pstat); |
770 | 1.46M | if (code != gs_error_unregistered && code != gs_error_undefinedfilename){ |
771 | 1.46M | have_rom_device = 1; |
772 | 1.46M | } |
773 | 1.46M | break; |
774 | 1.46M | } |
775 | 10.2M | } |
776 | 1.46M | if (have_rom_device) { |
777 | 1.46M | code = lib_path_add(minst, "%rom%Resource/Init/"); |
778 | 1.46M | if (code < 0) |
779 | 0 | return code; |
780 | 1.46M | code = lib_path_add(minst, "%rom%lib/"); |
781 | 1.46M | } |
782 | 0 | else code = 0; |
783 | | |
784 | 1.46M | if (minst->lib_path.final != NULL && code >= 0) |
785 | 1.46M | code = lib_path_add(minst, minst->lib_path.final); |
786 | 1.46M | return code; |
787 | 1.46M | } |
788 | | |
789 | | /* Open a file, using the search paths. */ |
790 | | int |
791 | | gs_main_lib_open(gs_main_instance * minst, const char *file_name, ref * pfile) |
792 | 162k | { |
793 | | /* This is a separate procedure only to avoid tying up */ |
794 | | /* extra stack space while running the file. */ |
795 | 162k | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
796 | 162k | #define maxfn 2048 |
797 | 162k | char fn[maxfn]; |
798 | 162k | uint len; |
799 | | |
800 | 162k | return lib_file_open(&minst->lib_path, imemory, |
801 | 162k | NULL, /* Don't check permissions here, because permlist |
802 | | isn't ready running init files. */ |
803 | 162k | file_name, strlen(file_name), fn, maxfn, &len, pfile); |
804 | 162k | } |
805 | | |
806 | | /* Open and execute a file. */ |
807 | | int |
808 | | gs_main_run_file(gs_main_instance * minst, const char *file_name, int user_errors, int *pexit_code, ref * perror_object) |
809 | 0 | { |
810 | 0 | ref initial_file; |
811 | 0 | int code = gs_main_run_file_open(minst, file_name, &initial_file); |
812 | |
|
813 | 0 | if (code < 0) |
814 | 0 | return code; |
815 | 0 | return gs_main_interpret(minst, &initial_file, user_errors, |
816 | 0 | pexit_code, perror_object); |
817 | 0 | } |
818 | | int |
819 | | gs_main_run_file_open(gs_main_instance * minst, const char *file_name, ref * pfref) |
820 | 162k | { |
821 | 162k | gs_main_set_lib_paths(minst); |
822 | 162k | if (gs_main_lib_open(minst, file_name, pfref) < 0) { |
823 | 0 | emprintf1(minst->heap, |
824 | 0 | "Can't find initialization file %s.\n", |
825 | 0 | file_name); |
826 | 0 | return_error(gs_error_Fatal); |
827 | 0 | } |
828 | 162k | r_set_attrs(pfref, a_execute + a_executable); |
829 | 162k | return 0; |
830 | 162k | } |
831 | | |
832 | | /* Open and run the very first initialization file. */ |
833 | | static int |
834 | | gs_run_init_file(gs_main_instance * minst, int *pexit_code, ref * perror_object) |
835 | 162k | { |
836 | 162k | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
837 | 162k | ref ifile; |
838 | 162k | ref first_token; |
839 | 162k | int code; |
840 | 162k | scanner_state state; |
841 | | |
842 | 162k | gs_main_set_lib_paths(minst); |
843 | 162k | code = gs_main_run_file_open(minst, gs_init_file, &ifile); |
844 | 162k | if (code < 0) { |
845 | 0 | *pexit_code = 255; |
846 | 0 | return code; |
847 | 0 | } |
848 | | /* Check to make sure the first token is an integer */ |
849 | | /* (for the version number check.) */ |
850 | 162k | gs_scanner_init(&state, &ifile); |
851 | 162k | code = gs_scan_token(i_ctx_p, &first_token, &state); |
852 | 162k | if (code != 0 || !r_has_type(&first_token, t_integer)) { |
853 | 0 | emprintf1(minst->heap, |
854 | 0 | "Initialization file %s does not begin with an integer.\n", |
855 | 0 | gs_init_file); |
856 | 0 | *pexit_code = 255; |
857 | 0 | return_error(gs_error_Fatal); |
858 | 0 | } |
859 | 162k | *++osp = first_token; |
860 | 162k | r_set_attrs(&ifile, a_executable); |
861 | 162k | return gs_main_interpret(minst, &ifile, minst->user_errors, |
862 | 162k | pexit_code, perror_object); |
863 | 162k | } |
864 | | |
865 | | /* Run a string. */ |
866 | | int |
867 | | gs_main_run_string(gs_main_instance * minst, const char *str, int user_errors, |
868 | | int *pexit_code, ref * perror_object) |
869 | 974k | { |
870 | 974k | return gs_main_run_string_with_length(minst, str, (uint) strlen(str), |
871 | 974k | user_errors, |
872 | 974k | pexit_code, perror_object); |
873 | 974k | } |
874 | | int |
875 | | gs_main_run_string_with_length(gs_main_instance * minst, const char *str, |
876 | | uint length, int user_errors, int *pexit_code, ref * perror_object) |
877 | 974k | { |
878 | 974k | int code; |
879 | | |
880 | 974k | code = gs_main_run_string_begin(minst, user_errors, |
881 | 974k | pexit_code, perror_object); |
882 | 974k | if (code < 0) |
883 | 3 | return code; |
884 | 974k | code = gs_main_run_string_continue(minst, str, length, user_errors, |
885 | 974k | pexit_code, perror_object); |
886 | 974k | if (code != gs_error_NeedInput) |
887 | 162k | return code; |
888 | | |
889 | 812k | code = gs_main_run_string_end(minst, user_errors, |
890 | 812k | pexit_code, perror_object); |
891 | | /* Not okay for user to use .needinput |
892 | | * This treats it as a fatal error. |
893 | | */ |
894 | 812k | if (code == gs_error_NeedInput) |
895 | 0 | return_error(gs_error_Fatal); |
896 | 812k | return code; |
897 | 812k | } |
898 | | |
899 | | /* Set up for a suspendable run_string. */ |
900 | | int |
901 | | gs_main_run_string_begin(gs_main_instance * minst, int user_errors, |
902 | | int *pexit_code, ref * perror_object) |
903 | 974k | { |
904 | 974k | const char *setup = ".runstringbegin"; |
905 | 974k | ref rstr; |
906 | 974k | int code; |
907 | | |
908 | 974k | gs_main_set_lib_paths(minst); |
909 | 974k | make_const_string(&rstr, avm_foreign | a_readonly | a_executable, |
910 | 974k | strlen(setup), (const byte *)setup); |
911 | 974k | code = gs_main_interpret(minst, &rstr, user_errors, pexit_code, |
912 | 974k | perror_object); |
913 | 974k | return (code == gs_error_NeedInput ? 0 : code == 0 ? gs_error_Fatal : code); |
914 | 974k | } |
915 | | /* Continue running a string with the option of suspending. */ |
916 | | int |
917 | | gs_main_run_string_continue(gs_main_instance * minst, const char *str, |
918 | | uint length, int user_errors, int *pexit_code, ref * perror_object) |
919 | 974k | { |
920 | 974k | ref rstr; |
921 | | |
922 | 974k | if (length == 0) |
923 | 0 | return 0; /* empty string signals EOF */ |
924 | 974k | make_const_string(&rstr, avm_foreign | a_readonly, length, |
925 | 974k | (const byte *)str); |
926 | 974k | return gs_main_interpret(minst, &rstr, user_errors, pexit_code, |
927 | 974k | perror_object); |
928 | 974k | } |
929 | | uint |
930 | | gs_main_get_uel_offset(gs_main_instance * minst) |
931 | 0 | { |
932 | 0 | if (minst->i_ctx_p == NULL) |
933 | 0 | return 0; |
934 | 0 | return (uint)minst->i_ctx_p->uel_position; |
935 | 0 | } |
936 | | |
937 | | /* Signal EOF when suspended. */ |
938 | | int |
939 | | gs_main_run_string_end(gs_main_instance * minst, int user_errors, |
940 | | int *pexit_code, ref * perror_object) |
941 | 812k | { |
942 | 812k | ref rstr; |
943 | | |
944 | 812k | make_empty_const_string(&rstr, avm_foreign | a_readonly); |
945 | 812k | return gs_main_interpret(minst, &rstr, user_errors, pexit_code, |
946 | 812k | perror_object); |
947 | 812k | } |
948 | | |
949 | | gs_memory_t * |
950 | | gs_main_get_device_memory(gs_main_instance * minst) |
951 | 0 | { |
952 | 0 | gs_memory_t *dev_mem = NULL; |
953 | 0 | if (minst && minst->init_done >= 1) { |
954 | 0 | i_ctx_t * i_ctx_p = minst->i_ctx_p; |
955 | 0 | dev_mem = imemory_global->stable_memory; |
956 | 0 | } |
957 | 0 | return dev_mem; |
958 | 0 | } |
959 | | |
960 | | int |
961 | | gs_main_set_device(gs_main_instance * minst, gx_device *pdev) |
962 | 0 | { |
963 | 0 | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
964 | 0 | ref error_object; |
965 | 0 | int code; |
966 | |
|
967 | 0 | if (pdev == NULL) { |
968 | | /* Leave job encapsulation, restore the graphics state gsaved below (so back to the nullpage device) |
969 | | and re-enter job encapsulation. |
970 | | We rely on the end of job encapsulation restore to the put the gstate stack back how it was when |
971 | | we entered job encapsulation below, so the grestore will pickup the correct gstate. |
972 | | */ |
973 | 0 | code = gs_main_run_string(minst, |
974 | 0 | "true 0 startjob pop grestore false 0 startjob pop", |
975 | 0 | 0, &code, &error_object); |
976 | 0 | if (code < 0) goto done; |
977 | 0 | } |
978 | 0 | else { |
979 | | /* Leave job encapsulation, and save the graphics state (including the device: nullpage) |
980 | | Store the page size in a dictionary, which we'll use to configure the incoming device */ |
981 | 0 | code = gs_main_run_string(minst, |
982 | 0 | "true 0 startjob pop gsave " |
983 | | /* /PageSize /GetDeviceParam .special_op will either return: |
984 | | * /PageSize [ <width> <height> ] true (if it exists) or |
985 | | * false (if it does not) */ |
986 | 0 | "<< /PageSize /GetDeviceParam .special_op " |
987 | | /* If we wanted to force a default pagesize, we'd do: |
988 | | * "not { /PageSize [595 842] } if " |
989 | | * but for now we'll just leave the default as it is, and do: */ |
990 | 0 | "pop " |
991 | 0 | ">> " |
992 | 0 | , 0, &code, &error_object); |
993 | 0 | if (code < 0) goto done; |
994 | | /* First call goes to the C directly to actually set the device. This |
995 | | * avoids the SAFER checks. */ |
996 | 0 | code = zsetdevice_no_safer(i_ctx_p, pdev); |
997 | 0 | if (code < 0) goto done; |
998 | 0 | code = zcurrentdevice(i_ctx_p); |
999 | 0 | if (code < 0) goto done; |
1000 | 0 | code = gs_main_run_string(minst, |
1001 | | /* Set the device again to the same one. This determines |
1002 | | * whether to erase page or not, but passes the safer |
1003 | | * checks as the device is unchanged. */ |
1004 | 0 | "setdevice " |
1005 | 0 | "setpagedevice " |
1006 | | /* GS specifics: Force the cached copy of the params to be updated. */ |
1007 | 0 | "currentpagedevice pop " |
1008 | | /* Setup the halftone */ |
1009 | 0 | ".setdefaultscreen " |
1010 | | /* Re-run the scheduled initialisation procs, in case we've just set pdfwrite */ |
1011 | 0 | "1183615869 internaldict /.execute_scheduled_inits get exec " |
1012 | | /* Re-enter job encapsulation */ |
1013 | 0 | "false 0 startjob pop " |
1014 | 0 | , 0, &code, &error_object); |
1015 | 0 | if (code < 0) goto done; |
1016 | 0 | } |
1017 | 0 | done: |
1018 | 0 | return code; |
1019 | 0 | } |
1020 | | |
1021 | | /* ------ Operand stack access ------ */ |
1022 | | |
1023 | | /* These are built for comfort, not for speed. */ |
1024 | | |
1025 | | static int |
1026 | | push_value(gs_main_instance *minst, ref * pvalue) |
1027 | 0 | { |
1028 | 0 | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
1029 | 0 | int code = ref_stack_push(&o_stack, 1); |
1030 | 0 | ref *o = ref_stack_index(&o_stack, 0L); |
1031 | |
|
1032 | 0 | if (o == NULL) |
1033 | 0 | return_error(gs_error_stackoverflow); |
1034 | | |
1035 | 0 | if (code < 0) |
1036 | 0 | return code; |
1037 | 0 | *o = *pvalue; |
1038 | 0 | return 0; |
1039 | 0 | } |
1040 | | |
1041 | | int |
1042 | | gs_push_boolean(gs_main_instance * minst, bool value) |
1043 | 0 | { |
1044 | 0 | ref vref; |
1045 | |
|
1046 | 0 | make_bool(&vref, value); |
1047 | 0 | return push_value(minst, &vref); |
1048 | 0 | } |
1049 | | |
1050 | | int |
1051 | | gs_push_integer(gs_main_instance * minst, long value) |
1052 | 0 | { |
1053 | 0 | ref vref; |
1054 | |
|
1055 | 0 | make_int(&vref, value); |
1056 | 0 | return push_value(minst, &vref); |
1057 | 0 | } |
1058 | | |
1059 | | int |
1060 | | gs_push_real(gs_main_instance * minst, double value) |
1061 | 0 | { |
1062 | 0 | ref vref; |
1063 | |
|
1064 | 0 | make_real(&vref, value); |
1065 | 0 | return push_value(minst, &vref); |
1066 | 0 | } |
1067 | | |
1068 | | int |
1069 | | gs_push_string(gs_main_instance * minst, byte * chars, uint length, |
1070 | | bool read_only) |
1071 | 0 | { |
1072 | 0 | ref vref; |
1073 | |
|
1074 | 0 | make_string(&vref, avm_foreign | (read_only ? a_readonly : a_all), |
1075 | 0 | length, (byte *) chars); |
1076 | 0 | return push_value(minst, &vref); |
1077 | 0 | } |
1078 | | |
1079 | | static int |
1080 | | pop_value(i_ctx_t *i_ctx_p, ref * pvalue) |
1081 | 0 | { |
1082 | 0 | if (!ref_stack_count(&o_stack)) |
1083 | 0 | return_error(gs_error_stackunderflow); |
1084 | 0 | *pvalue = *ref_stack_index(&o_stack, 0L); |
1085 | 0 | return 0; |
1086 | 0 | } |
1087 | | |
1088 | | int |
1089 | | gs_pop_boolean(gs_main_instance * minst, bool * result) |
1090 | 0 | { |
1091 | 0 | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
1092 | 0 | ref vref; |
1093 | 0 | int code = pop_value(i_ctx_p, &vref); |
1094 | |
|
1095 | 0 | if (code < 0) |
1096 | 0 | return code; |
1097 | 0 | check_type_only(vref, t_boolean); |
1098 | 0 | *result = vref.value.boolval; |
1099 | 0 | ref_stack_pop(&o_stack, 1); |
1100 | 0 | return 0; |
1101 | 0 | } |
1102 | | |
1103 | | int |
1104 | | gs_pop_integer(gs_main_instance * minst, long *result) |
1105 | 0 | { |
1106 | 0 | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
1107 | 0 | ref vref; |
1108 | 0 | int code = pop_value(i_ctx_p, &vref); |
1109 | |
|
1110 | 0 | if (code < 0) |
1111 | 0 | return code; |
1112 | 0 | check_type_only(vref, t_integer); |
1113 | 0 | *result = vref.value.intval; |
1114 | 0 | ref_stack_pop(&o_stack, 1); |
1115 | 0 | return 0; |
1116 | 0 | } |
1117 | | |
1118 | | int |
1119 | | gs_pop_real(gs_main_instance * minst, float *result) |
1120 | 0 | { |
1121 | 0 | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
1122 | 0 | ref vref; |
1123 | 0 | int code = pop_value(i_ctx_p, &vref); |
1124 | |
|
1125 | 0 | if (code < 0) |
1126 | 0 | return code; |
1127 | 0 | switch (r_type(&vref)) { |
1128 | 0 | case t_real: |
1129 | 0 | *result = vref.value.realval; |
1130 | 0 | break; |
1131 | 0 | case t_integer: |
1132 | 0 | *result = (float)(vref.value.intval); |
1133 | 0 | break; |
1134 | 0 | default: |
1135 | 0 | return_error(gs_error_typecheck); |
1136 | 0 | } |
1137 | 0 | ref_stack_pop(&o_stack, 1); |
1138 | 0 | return 0; |
1139 | 0 | } |
1140 | | |
1141 | | int |
1142 | | gs_pop_string(gs_main_instance * minst, gs_string * result) |
1143 | 0 | { |
1144 | 0 | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
1145 | 0 | ref vref; |
1146 | 0 | int code = pop_value(i_ctx_p, &vref); |
1147 | |
|
1148 | 0 | if (code < 0) |
1149 | 0 | return code; |
1150 | 0 | switch (r_type(&vref)) { |
1151 | 0 | case t_name: |
1152 | 0 | name_string_ref(minst->heap, &vref, &vref); |
1153 | 0 | code = 1; |
1154 | 0 | goto rstr; |
1155 | 0 | case t_string: |
1156 | 0 | code = (r_has_attr(&vref, a_write) ? 0 : 1); |
1157 | 0 | rstr:result->data = vref.value.bytes; |
1158 | 0 | result->size = r_size(&vref); |
1159 | 0 | break; |
1160 | 0 | default: |
1161 | 0 | return_error(gs_error_typecheck); |
1162 | 0 | } |
1163 | 0 | ref_stack_pop(&o_stack, 1); |
1164 | 0 | return code; |
1165 | 0 | } |
1166 | | |
1167 | | /* ------ Termination ------ */ |
1168 | | |
1169 | | /* Get the names of temporary files. |
1170 | | * Each name is null terminated, and the last name is |
1171 | | * terminated by a double null. |
1172 | | * We retrieve the names of temporary files just before |
1173 | | * the interpreter finishes, and then delete the files |
1174 | | * after the interpreter has closed all files. |
1175 | | */ |
1176 | | static char *gs_main_tempnames(gs_main_instance *minst) |
1177 | 162k | { |
1178 | 162k | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
1179 | 162k | ref *SAFETY; |
1180 | 162k | ref *tempfiles; |
1181 | 162k | ref keyval[2]; /* for key and value */ |
1182 | 162k | char *tempnames = NULL; |
1183 | 162k | int i; |
1184 | 162k | int idict; |
1185 | 162k | int len = 0; |
1186 | 162k | const byte *data = NULL; |
1187 | 162k | uint size; |
1188 | 162k | if (minst->init_done >= 2) { |
1189 | 162k | if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 || |
1190 | 162k | dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0) |
1191 | 0 | return NULL; |
1192 | | /* get lengths of temporary filenames */ |
1193 | 162k | idict = dict_first(tempfiles); |
1194 | 257k | while ((idict = dict_next(tempfiles, idict, &keyval[0])) >= 0) { |
1195 | 94.5k | if (obj_string_data(minst->heap, &keyval[0], &data, &size) >= 0) |
1196 | 94.5k | len += size + 1; |
1197 | 94.5k | } |
1198 | 162k | if (len != 0) |
1199 | 94.5k | tempnames = (char *)malloc(len+1); |
1200 | 162k | if (tempnames) { |
1201 | 94.5k | memset(tempnames, 0, len+1); |
1202 | | /* copy temporary filenames */ |
1203 | 94.5k | idict = dict_first(tempfiles); |
1204 | 94.5k | i = 0; |
1205 | 189k | while ((idict = dict_next(tempfiles, idict, &keyval[0])) >= 0) { |
1206 | 94.5k | if (obj_string_data(minst->heap, &keyval[0], &data, &size) >= 0) { |
1207 | 94.5k | memcpy(tempnames+i, (const char *)data, size); |
1208 | 94.5k | i+= size; |
1209 | 94.5k | tempnames[i++] = '\0'; |
1210 | 94.5k | } |
1211 | 94.5k | } |
1212 | 94.5k | } |
1213 | 162k | } |
1214 | 162k | return tempnames; |
1215 | 162k | } |
1216 | | |
1217 | | static void |
1218 | | gs_finit_push_systemdict(i_ctx_t *i_ctx_p) |
1219 | 162k | { |
1220 | 162k | if (i_ctx_p == NULL) |
1221 | 0 | return; |
1222 | 162k | if (dsp == dstop ) { |
1223 | 7 | if (ref_stack_extend(&d_stack, 1) < 0) { |
1224 | | /* zend() cannot fail */ |
1225 | 0 | (void)zend(i_ctx_p); |
1226 | 0 | } |
1227 | 7 | } |
1228 | 162k | dsp++; |
1229 | 162k | ref_assign(dsp, systemdict); |
1230 | 162k | } |
1231 | | |
1232 | | /* Free all resources and return. */ |
1233 | | int |
1234 | | gs_main_finit(gs_main_instance * minst, int exit_status, int env_code) |
1235 | 162k | { |
1236 | 162k | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
1237 | 162k | gs_dual_memory_t dmem = {0}; |
1238 | 162k | int exit_code; |
1239 | 162k | ref error_object; |
1240 | 162k | char *tempnames = NULL; |
1241 | 162k | gs_lib_ctx_core_t *core; |
1242 | | |
1243 | | /* NB: need to free gs_name_table |
1244 | | */ |
1245 | | |
1246 | | /* |
1247 | | * Previous versions of this code closed the devices in the |
1248 | | * device list here. Since these devices are now prototypes, |
1249 | | * they cannot be opened, so they do not need to be closed; |
1250 | | * alloc_restore_all will close dynamically allocated devices. |
1251 | | */ |
1252 | 162k | tempnames = gs_main_tempnames(minst); |
1253 | | |
1254 | | /* by the time we get here, we *must* avoid any random redefinitions of |
1255 | | * operators etc, so we push systemdict onto the top of the dict stack. |
1256 | | * We do this in C to avoid running into any other re-defininitions in the |
1257 | | * Postscript world. |
1258 | | */ |
1259 | 162k | gs_finit_push_systemdict(i_ctx_p); |
1260 | | |
1261 | | /* We have to disable BGPrint before we call interp_reclaim() to prevent the |
1262 | | * parent rendering thread initialising for the next page, whilst we are |
1263 | | * removing objects it may want to access - for example, the I/O device table. |
1264 | | * We also have to mess with the BeginPage/EndPage procs so that we don't |
1265 | | * trigger a spurious extra page to be emitted. |
1266 | | */ |
1267 | 162k | if (minst->init_done >= 2) { |
1268 | 162k | gs_main_run_string(minst, |
1269 | 162k | "/BGPrint /GetDeviceParam .special_op \ |
1270 | 162k | {{ <</BeginPage {pop} /EndPage {pop pop //false } \ |
1271 | 162k | /BGPrint false /NumRenderingThreads 0>> setpagedevice} if} if \ |
1272 | 162k | serverdict /.jobsavelevel get 0 eq {/quit} {/stop} ifelse \ |
1273 | 162k | .systemvar exec", |
1274 | 162k | 0 , &exit_code, &error_object); |
1275 | 162k | } |
1276 | | |
1277 | | /* |
1278 | | * Close the "main" device, because it may need to write out |
1279 | | * data before destruction. pdfwrite needs so. |
1280 | | */ |
1281 | 162k | if (minst->init_done >= 2) { |
1282 | 162k | int code = 0; |
1283 | | |
1284 | 162k | if (idmemory->reclaim != 0) { |
1285 | | /* In extreme error conditions, these references can persist, despite the |
1286 | | * arrays themselves having been restored away. |
1287 | | */ |
1288 | 162k | gs_main_run_string(minst, |
1289 | 162k | "$error /dstack undef \ |
1290 | 162k | $error /estack undef \ |
1291 | 162k | $error /ostack undef \ |
1292 | 162k | serverdict /.jobsavelevel get 0 eq {/quit} {/stop} ifelse \ |
1293 | 162k | .systemvar exec", |
1294 | 162k | 0 , &exit_code, &error_object); |
1295 | | |
1296 | 162k | ref_stack_clear(&o_stack); |
1297 | 162k | code = interp_reclaim(&minst->i_ctx_p, avm_global); |
1298 | | |
1299 | | /* We ignore gs_error_VMerror because it comes from gs_vmreclaim() |
1300 | | calling context_state_load(), and we don't seem to depend on the |
1301 | | missing fields. */ |
1302 | 162k | if (code == gs_error_VMerror) { |
1303 | 1 | if (exit_status == 0 || exit_status == gs_error_Quit) { |
1304 | 1 | exit_status = gs_error_VMerror; |
1305 | 1 | } |
1306 | 1 | } |
1307 | 162k | else if (code < 0) { |
1308 | 0 | ref error_name; |
1309 | 0 | if (tempnames) |
1310 | 0 | free(tempnames); |
1311 | |
|
1312 | 0 | if (gs_errorname(i_ctx_p, code, &error_name) >= 0) { |
1313 | 0 | char err_str[32] = {0}; |
1314 | 0 | name_string_ref(imemory, &error_name, &error_name); |
1315 | 0 | memcpy(err_str, error_name.value.const_bytes, r_size(&error_name)); |
1316 | 0 | emprintf2(imemory, "ERROR: %s (%d) reclaiming the memory while the interpreter finalization.\n", err_str, code); |
1317 | 0 | } |
1318 | 0 | else { |
1319 | 0 | emprintf1(imemory, "UNKNOWN ERROR %d reclaiming the memory while the interpreter finalization.\n", code); |
1320 | 0 | } |
1321 | | #ifdef MEMENTO |
1322 | | if (Memento_squeezing() && code != gs_error_VMerror ) return gs_error_Fatal; |
1323 | | #endif |
1324 | 0 | return gs_error_Fatal; |
1325 | 0 | } |
1326 | 162k | i_ctx_p = minst->i_ctx_p; /* interp_reclaim could change it. */ |
1327 | 162k | } |
1328 | | |
1329 | 162k | if (i_ctx_p->pgs != NULL && i_ctx_p->pgs->device != NULL && |
1330 | 162k | gx_device_is_null(i_ctx_p->pgs->device)) { |
1331 | | /* if the job replaced the device with the nulldevice, we we need to grestore |
1332 | | away that device, so the block below can properly dispense |
1333 | | with the default device. |
1334 | | */ |
1335 | 97 | int code = gs_grestoreall(i_ctx_p->pgs); |
1336 | 97 | if (code < 0) { |
1337 | 0 | free(tempnames); |
1338 | 0 | return_error(gs_error_Fatal); |
1339 | 0 | } |
1340 | 97 | } |
1341 | | |
1342 | 162k | if (i_ctx_p->pgs != NULL && i_ctx_p->pgs->device != NULL) { |
1343 | 162k | gx_device *pdev = i_ctx_p->pgs->device; |
1344 | 162k | const char * dname = pdev->dname; |
1345 | 162k | gs_gc_root_t dev_root; |
1346 | 162k | gs_gc_root_t *dev_root_ptr = &dev_root; |
1347 | | /* There is a chance that, during the call to gs_main_run_string(), the interpreter may |
1348 | | * decide to call the garbager - the device is in gc memory, and the only reference to it |
1349 | | * (in the gstate) has been removed, thus it can be destroyed by the garbager. |
1350 | | * Counter-intuitively, adjusting the reference count makes not difference to that. |
1351 | | * Register the device as a gc 'root' so it will be implicitely marked by garbager, and |
1352 | | * and thus surive until control returns here. |
1353 | | */ |
1354 | 162k | if (gs_register_struct_root(pdev->memory, &dev_root_ptr, (void **)&pdev, "gs_main_finit") < 0) { |
1355 | 0 | free(tempnames); |
1356 | 0 | return_error(gs_error_Fatal); |
1357 | 0 | } |
1358 | | |
1359 | | /* make sure device doesn't isn't freed by .uninstalldevice */ |
1360 | 162k | rc_adjust(pdev, 1, "gs_main_finit"); |
1361 | | /* deactivate the device just before we close it for the last time */ |
1362 | 162k | gs_main_run_string(minst, |
1363 | | /* we need to do the 'quit' so we don't loop for input (double quit) */ |
1364 | 162k | ".uninstallpagedevice serverdict \ |
1365 | 162k | /.jobsavelevel get 0 eq {/quit} {/stop} ifelse .systemvar exec", |
1366 | 162k | 0 , &exit_code, &error_object); |
1367 | 162k | code = gs_closedevice(pdev); |
1368 | 162k | if (code < 0) { |
1369 | 0 | ref error_name; |
1370 | 0 | if (gs_errorname(i_ctx_p, code, &error_name) >= 0) { |
1371 | 0 | char err_str[32] = {0}; |
1372 | 0 | name_string_ref(imemory, &error_name, &error_name); |
1373 | 0 | memcpy(err_str, error_name.value.const_bytes, r_size(&error_name)); |
1374 | 0 | emprintf3(imemory, "ERROR: %s (%d) on closing %s device.\n", err_str, code, dname); |
1375 | 0 | } |
1376 | 0 | else { |
1377 | 0 | emprintf2(imemory, "UNKNOWN ERROR %d closing %s device.\n", code, dname); |
1378 | 0 | } |
1379 | 0 | } |
1380 | 162k | gs_unregister_root(pdev->memory, dev_root_ptr, "gs_main_finit"); |
1381 | 162k | rc_decrement(pdev, "gs_main_finit"); /* device might be freed */ |
1382 | 162k | if (exit_status == 0 || exit_status == gs_error_Quit) |
1383 | 162k | exit_status = code; |
1384 | 162k | } |
1385 | | |
1386 | | /* Flush stdout and stderr */ |
1387 | 162k | gs_main_run_string(minst, |
1388 | 162k | "(%stdout) (w) file closefile (%stderr) (w) file closefile \ |
1389 | 162k | serverdict /.jobsavelevel get 0 eq {/quit} {/stop} ifelse .systemexec \ |
1390 | 162k | systemdict /savedinitialgstate .forceundef", |
1391 | 162k | 0 , &exit_code, &error_object); |
1392 | 162k | } |
1393 | 162k | gp_readline_finit(minst->readline_data); |
1394 | 162k | gs_free_object(minst->heap, minst->saved_pages_initial_arg, "gs_main_finit"); |
1395 | 162k | i_ctx_p = minst->i_ctx_p; /* get current interp context */ |
1396 | 162k | if (gs_debug_c(':')) { |
1397 | 0 | print_resource_usage(minst, i_ctx_p ? &gs_imemory : NULL, "Final"); |
1398 | 0 | dmprintf1(minst->heap, "%% Exiting instance "PRI_INTPTR"\n", (intptr_t)minst); |
1399 | 0 | } |
1400 | | /* Do the equivalent of a restore "past the bottom". */ |
1401 | | /* This will release all memory, close all open files, etc. */ |
1402 | 162k | if (minst->init_done >= 1) { |
1403 | 162k | gs_memory_t *mem_raw = i_ctx_p->memory.current->non_gc_memory; |
1404 | 162k | i_plugin_holder *h = i_ctx_p->plugin_list; |
1405 | | |
1406 | 162k | dmem = *idmemory; |
1407 | 162k | env_code = alloc_restore_all(i_ctx_p); |
1408 | 162k | if (env_code < 0) |
1409 | 0 | emprintf1(mem_raw, |
1410 | 162k | "ERROR %d while the final restore. See gs/psi/ierrors.h for code explanation.\n", |
1411 | 162k | env_code); |
1412 | 162k | i_iodev_finit(&dmem); |
1413 | 162k | i_plugin_finit(mem_raw, h); |
1414 | 162k | } |
1415 | | |
1416 | | /* clean up redirected stdout */ |
1417 | 162k | core = minst->heap->gs_lib_ctx->core; |
1418 | 162k | if (core->fstdout2 |
1419 | 162k | && (gp_get_file(core->fstdout2) != core->fstdout) |
1420 | 162k | && (gp_get_file(core->fstdout2) != core->fstderr)) { |
1421 | 162k | gp_fclose(core->fstdout2); |
1422 | 162k | core->fstdout2 = NULL; |
1423 | 162k | } |
1424 | | |
1425 | 162k | minst->heap->gs_lib_ctx->core->stdout_is_redirected = 0; |
1426 | 162k | minst->heap->gs_lib_ctx->core->stdout_to_stderr = 0; |
1427 | | /* remove any temporary files, after ghostscript has closed files */ |
1428 | 162k | if (tempnames) { |
1429 | 94.5k | char *p = tempnames; |
1430 | 189k | while (*p) { |
1431 | 94.5k | gp_unlink(minst->heap, p); |
1432 | 94.5k | p += strlen(p) + 1; |
1433 | 94.5k | } |
1434 | 94.5k | free(tempnames); |
1435 | 94.5k | } |
1436 | 162k | gs_lib_finit(exit_status, env_code, minst->heap); |
1437 | | |
1438 | 162k | set_lib_path_length(minst, 0); |
1439 | 162k | gs_free_object(minst->heap, minst->lib_path.container.value.refs, "lib_path array"); |
1440 | 162k | if (minst->init_done == 0 && i_ctx_p) { |
1441 | | /* This fixes leak if memento forces failure in gs_main_init1(). */ |
1442 | 0 | dmem = *idmemory; |
1443 | 0 | } |
1444 | 162k | ialloc_finit(&dmem); |
1445 | 162k | return exit_status; |
1446 | 162k | } |
1447 | | int |
1448 | | gs_to_exit_with_code(const gs_memory_t *mem, int exit_status, int code) |
1449 | 162k | { |
1450 | 162k | return gs_main_finit(get_minst_from_memory(mem), exit_status, code); |
1451 | 162k | } |
1452 | | int |
1453 | | gs_to_exit(const gs_memory_t *mem, int exit_status) |
1454 | 162k | { |
1455 | 162k | return gs_to_exit_with_code(mem, exit_status, 0); |
1456 | 162k | } |
1457 | | void |
1458 | | gs_abort(const gs_memory_t *mem) |
1459 | 0 | { |
1460 | | /* In previous versions, we tried to do a cleanup (using gs_to_exit), |
1461 | | * but more often than not, that will trip another abort and create |
1462 | | * an infinite recursion. So just abort without trying to cleanup. |
1463 | | */ |
1464 | 0 | gp_do_exit(1); |
1465 | 0 | } |
1466 | | |
1467 | | /* ------ Debugging ------ */ |
1468 | | |
1469 | | /* Print resource usage statistics. */ |
1470 | | void |
1471 | | print_resource_usage(const gs_main_instance * minst, gs_dual_memory_t * dmem, |
1472 | | const char *msg) |
1473 | 0 | { |
1474 | 0 | ulong used = 0; /* this we accumulate for the PS memories */ |
1475 | 0 | long utime[2]; |
1476 | 0 | int i; |
1477 | 0 | gs_memory_status_t status = { 0 }; |
1478 | |
|
1479 | 0 | gp_get_realtime(utime); |
1480 | |
|
1481 | 0 | if (dmem) |
1482 | 0 | { |
1483 | 0 | for (i = 0; i < countof(dmem->spaces_indexed); ++i) { |
1484 | 0 | gs_ref_memory_t *mem = dmem->spaces_indexed[i]; |
1485 | |
|
1486 | 0 | if (mem != 0 && (i == 0 || mem != dmem->spaces_indexed[i - 1])) { |
1487 | 0 | gs_ref_memory_t *mem_stable = |
1488 | 0 | (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)mem); |
1489 | |
|
1490 | 0 | gs_memory_status((gs_memory_t *)mem, &status); |
1491 | 0 | used += status.used; |
1492 | 0 | if (mem_stable != mem) { |
1493 | 0 | gs_memory_status((gs_memory_t *)mem_stable, &status); |
1494 | 0 | used += status.used; |
1495 | 0 | } |
1496 | 0 | } |
1497 | 0 | } |
1498 | 0 | } |
1499 | | /* Now get the overall values from the heap memory */ |
1500 | 0 | gs_memory_status(minst->heap, &status); |
1501 | 0 | dmprintf5(minst->heap, "%% %s time = %g, memory allocated = %lu, used = %lu, max_used = %lu\n", |
1502 | 0 | msg, utime[0] - minst->base_time[0] + |
1503 | 0 | (utime[1] - minst->base_time[1]) / 1000000000.0, |
1504 | 0 | status.allocated, used, status.max_used); |
1505 | 0 | } |
1506 | | |
1507 | | /* Dump the stacks after interpretation */ |
1508 | | void |
1509 | | gs_main_dump_stack(gs_main_instance *minst, int code, ref * perror_object) |
1510 | 0 | { |
1511 | 0 | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
1512 | |
|
1513 | 0 | zflush(i_ctx_p); /* force out buffered output */ |
1514 | 0 | dmprintf1(minst->heap, "\nUnexpected interpreter error %d.\n", code); |
1515 | 0 | if (perror_object != 0) { |
1516 | 0 | dmputs(minst->heap, "Error object: "); |
1517 | 0 | debug_print_ref(minst->heap, perror_object); |
1518 | 0 | dmputc(minst->heap, '\n'); |
1519 | 0 | } |
1520 | 0 | debug_dump_stack(minst->heap, &o_stack, "Operand stack"); |
1521 | 0 | debug_dump_stack(minst->heap, &e_stack, "Execution stack"); |
1522 | 0 | debug_dump_stack(minst->heap, &d_stack, "Dictionary stack"); |
1523 | 0 | } |
1524 | | |
1525 | | int |
1526 | | gs_main_force_resolutions(gs_main_instance * minst, const float *resolutions) |
1527 | 162k | { |
1528 | 162k | ref value; |
1529 | 162k | int code; |
1530 | | |
1531 | 162k | if (resolutions == NULL) |
1532 | 0 | return 0; |
1533 | | |
1534 | 162k | if (minst == NULL) |
1535 | 0 | return gs_error_Fatal; |
1536 | | |
1537 | 162k | make_true(&value); |
1538 | 162k | code = i_initial_enter_name(minst->i_ctx_p, "FIXEDRESOLUTION", &value); |
1539 | 162k | if (code < 0) |
1540 | 0 | return code; |
1541 | 162k | make_real(&value, resolutions[0]); |
1542 | 162k | code = i_initial_enter_name(minst->i_ctx_p, "DEVICEXRESOLUTION", &value); |
1543 | 162k | if (code < 0) |
1544 | 0 | return code; |
1545 | 162k | make_real(&value, resolutions[1]); |
1546 | 162k | return i_initial_enter_name(minst->i_ctx_p, "DEVICEYRESOLUTION", &value); |
1547 | 162k | } |
1548 | | |
1549 | | int |
1550 | | gs_main_force_dimensions(gs_main_instance *minst, const long *dimensions) |
1551 | 0 | { |
1552 | 0 | ref value; |
1553 | 0 | int code = 0; |
1554 | |
|
1555 | 0 | if (dimensions == NULL) |
1556 | 0 | return 0; |
1557 | 0 | if (minst == NULL) |
1558 | 0 | return gs_error_Fatal; |
1559 | | |
1560 | 0 | make_true(&value); |
1561 | 0 | code = i_initial_enter_name(minst->i_ctx_p, "FIXEDMEDIA", &value); |
1562 | 0 | if (code < 0) |
1563 | 0 | return code; |
1564 | 0 | make_int(&value, dimensions[0]); |
1565 | 0 | code = i_initial_enter_name(minst->i_ctx_p, "DEVICEWIDTH", &value); |
1566 | 0 | if (code < 0) |
1567 | 0 | return code; |
1568 | 0 | make_int(&value, dimensions[1]); |
1569 | 0 | return i_initial_enter_name(minst->i_ctx_p, "DEVICEHEIGHT", &value); |
1570 | 0 | } |