/* Copyright (C) 1989, 1996, 1997, 1998, 1999, 2001 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: imain.c,v 1.19.2.2.2.1 2003/01/17 00:49:04 giles Exp $ */ /* Common support for interpreter front ends */ #include "memory_.h" #include "string_.h" /* Capture stdin/out/err before gs.h redefines them. */ #include #include private void set_stdfiles(FILE * stdfiles[3]) { stdfiles[0] = stdin; stdfiles[1] = stdout; stdfiles[2] = stderr; } #include "ghost.h" #include "gp.h" #include "gscdefs.h" /* for gs_init_file */ #include "gslib.h" #include "gsmatrix.h" /* for gxdevice.h */ #include "gsutil.h" /* for bytes_compare */ #include "gxdevice.h" #include "errors.h" #include "oper.h" #include "iconf.h" /* for gs_init_* imports */ #include "idebug.h" #include "idict.h" #include "iname.h" /* for name_init */ #include "dstack.h" #include "estack.h" #include "ostack.h" /* put here for files.h */ #include "stream.h" /* for files.h */ #include "files.h" #include "ialloc.h" #include "iinit.h" #include "strimpl.h" /* for sfilter.h */ #include "sfilter.h" /* for iscan.h */ #include "iscan.h" #include "main.h" #include "store.h" #include "isave.h" /* for prototypes */ #include "interp.h" #include "ivmspace.h" #include "idisp.h" /* for setting display device callback */ /* ------ Exported data ------ */ /* Define the default instance of the interpreter. */ /* Currently, this is the *only possible* instance, because most of */ /* the places that need to take an explicit instance argument don't. */ private gs_main_instance the_gs_main_instance; gs_main_instance * gs_main_instance_default(void) { /* Determine whether the instance has been initialized. */ if (the_gs_main_instance.memory_chunk_size == 0) the_gs_main_instance = gs_main_instance_init_values; return &the_gs_main_instance; } /* Define the interpreter's name table. We'll move it somewhere better */ /* eventually.... */ name_table *the_gs_name_table; /* ------ Forward references ------ */ private int gs_run_init_file(P3(gs_main_instance *, int *, ref *)); private void print_resource_usage(P3(const gs_main_instance *, gs_dual_memory_t *, const char *)); /* ------ Initialization ------ */ /* Save the real stdio files. */ void gs_get_real_stdio(FILE * stdfiles[3]) { set_stdfiles(stdfiles); } /* Initialization to be done before anything else. */ int gs_main_init0(gs_main_instance * minst, FILE * in, FILE * out, FILE * err, int max_lib_paths) { gs_memory_t *heap; ref *paths; /* Set our versions of stdin/out/err. */ gs_stdin = minst->fstdin = in; gs_stdout = minst->fstdout = out; gs_stderr = minst->fstderr = err; /* Do platform-dependent initialization. */ /* We have to do this as the very first thing, */ /* because it detects attempts to run 80N86 executables (N>0) */ /* on incompatible processors. */ gp_init(); gp_get_usertime(minst->base_time); /* Initialize the imager. */ heap = gs_lib_init0(gs_stdout); if (heap == 0) return_error(e_VMerror); minst->heap = heap; /* Initialize the file search paths. */ paths = (ref *) gs_alloc_byte_array(heap, max_lib_paths, sizeof(ref), "lib_path array"); if (paths == 0) { gs_lib_finit(1, e_VMerror); return_error(e_VMerror); } make_array(&minst->lib_path.container, avm_foreign, max_lib_paths, (ref *) gs_alloc_byte_array(heap, max_lib_paths, sizeof(ref), "lib_path array")); make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0, minst->lib_path.container.value.refs); minst->lib_path.env = 0; minst->lib_path.final = 0; minst->lib_path.count = 0; minst->user_errors = 1; minst->init_done = 0; return 0; } /* Initialization to be done before constructing any objects. */ int gs_main_init1(gs_main_instance * minst) { if (minst->init_done < 1) { gs_dual_memory_t idmem; int code = ialloc_init(&idmem, (gs_raw_memory_t *)&gs_memory_default, minst->memory_chunk_size, gs_have_level2()); if (code < 0) return code; code = gs_lib_init1((gs_memory_t *)idmem.space_system); if (code < 0) return code; alloc_save_init(&idmem); { gs_memory_t *mem = (gs_memory_t *)idmem.space_system; name_table *nt = names_init(minst->name_table_size, idmem.space_system); if (nt == 0) return_error(e_VMerror); the_gs_name_table = nt; code = gs_register_struct_root(mem, NULL, (void **)&the_gs_name_table, "the_gs_name_table"); if (code < 0) return code; } code = obj_init(&minst->i_ctx_p, &idmem); /* requires name_init */ if (code < 0) return code; minst->init_done = 1; } return 0; } /* Initialization to be done before running any files. */ private void init2_make_string_array(i_ctx_t *i_ctx_p, const ref * srefs, const char *aname) { const ref *ifp = srefs; ref ifa; for (; ifp->value.bytes != 0; ifp++); make_tasv(&ifa, t_array, a_readonly | avm_foreign, ifp - srefs, const_refs, srefs); initial_enter_name(aname, &ifa); } /* * Invoke the interpreter, handling stdio callouts * e_NeedStdin, e_NeedStdout and e_NeedStderr. * We don't yet pass callouts all the way out because they * occur within gs_main_init2() and swproc(). */ private int gs_main_interpret(gs_main_instance *minst, ref * pref, int user_errors, int *pexit_code, ref * perror_object) { i_ctx_t *i_ctx_p; ref refnul; ref refpop; int code; code = gs_interpret(&minst->i_ctx_p, pref, user_errors, pexit_code, perror_object); while ((code == e_NeedStdin) || (code == e_NeedStdout) || (code == e_NeedStderr)) { i_ctx_p = minst->i_ctx_p; if (code == e_NeedStdout) { /* * On entry: * esp[0] = string, data to write to stdout * esp[-1] = bool, EOF (ignored) * esp[-2] = array, procedure (ignored) * esp[-3] = file, stdout stream * We print the string then pop these 4 items. */ if (r_type(&esp[0]) == t_string) { const char *str = (const char *)(esp[0].value.const_bytes); int count = esp[0].tas.rsize; int rcode = 0; if (str != NULL) rcode = gs_main_outwrite(minst, str, count); if (rcode < 0) return_error(e_ioerror); } /* On return, we need to set * osp[-1] = string buffer, * osp[0] = file */ gs_push_string(minst, (byte *)minst->stdout_buf, sizeof(minst->stdout_buf), false); gs_push_integer(minst, 0); /* push integer */ osp[0] = esp[-3]; /* then replace with file */ /* remove items from execution stack */ esp -= 4; } else if (code == e_NeedStderr) { if (r_type(&esp[0]) == t_string) { const char *str = (const char *)(esp[0].value.const_bytes); int count = esp[0].tas.rsize; int rcode = 0; if (str != NULL) rcode = gs_main_errwrite(minst, str, count); if (rcode < 0) return_error(e_ioerror); } gs_push_string(minst, (byte *)minst->stderr_buf, sizeof(minst->stderr_buf), false); gs_push_integer(minst, 0); osp[0] = esp[-3]; esp -= 4; } else if (code == e_NeedStdin) { int count = sizeof(minst->stdin_buf); /* * On entry: * esp[0] = array, procedure (ignored) * esp[-1] = file, stdin stream * We read from stdin then pop these 2 items. */ if (minst->stdin_fn) count = (*minst->stdin_fn)(minst->caller_handle, minst->stdin_buf, count); else count = gp_stdin_read(minst->stdin_buf, count, minst->stdin_is_interactive, minst->fstdin); if (count < 0) return_error(e_ioerror); /* On return, we need to set * osp[-1] = string buffer, * osp[0] = file */ gs_push_string(minst, (byte *)minst->stdin_buf, count, false); gs_push_integer(minst, 0); /* push integer */ osp[0] = esp[-1]; /* then replace with file */ /* remove items from execution stack */ esp -= 2; } /* * To resume the interpreter, we call gs_interpret with a null ref. * This copies the literal null onto the operand stack. * To remove this we push a zpop onto the execution stack. */ make_null(&refnul); make_oper(&refpop, 0, zpop); esp += 1; *esp = refpop; code = gs_interpret(&minst->i_ctx_p, &refnul, user_errors, pexit_code, perror_object); } return code; } int gs_main_init2(gs_main_instance * minst) { i_ctx_t *i_ctx_p; int code = gs_main_init1(minst); if (code < 0) return code; i_ctx_p = minst->i_ctx_p; if (minst->init_done < 2) { int code, exit_code; ref error_object; code = zop_init(i_ctx_p); if (code < 0) return code; { /* * gs_iodev_init has to be called here (late), rather than * with the rest of the library init procedures, because of * some hacks specific to MS Windows for patching the * stdxxx IODevices. */ extern init_proc(gs_iodev_init); code = gs_iodev_init(imemory); if (code < 0) return code; } code = op_init(i_ctx_p); /* requires obj_init */ if (code < 0) return code; /* Set up the array of additional initialization files. */ init2_make_string_array(i_ctx_p, gs_init_file_array, "INITFILES"); /* Set up the array of emulator names. */ init2_make_string_array(i_ctx_p, gs_emulator_name_array, "EMULATORS"); /* Pass the search path. */ code = initial_enter_name("LIBPATH", &minst->lib_path.list); if (code < 0) return code; /* Execute the standard initialization file. */ code = gs_run_init_file(minst, &exit_code, &error_object); if (code < 0) return code; minst->init_done = 2; i_ctx_p = minst->i_ctx_p; /* init file may change it */ if (minst->display) code = display_set_callback(minst, minst->display); if (code < 0) return code; } if (gs_debug_c(':')) print_resource_usage(minst, &gs_imemory, "Start"); gp_readline_init(&minst->readline_data, imemory_system); return 0; } /* ------ Search paths ------ */ /* Internal routine to add a set of directories to a search list. */ /* Returns 0 or an error code. */ private int file_path_add(gs_file_path * pfp, const char *dirs) { uint len = r_size(&pfp->list); const char *dpath = dirs; if (dirs == 0) return 0; for (;;) { /* Find the end of the next directory name. */ const char *npath = dpath; while (*npath != 0 && *npath != gp_file_name_list_separator) npath++; if (npath > dpath) { if (len == r_size(&pfp->container)) return_error(e_limitcheck); make_const_string(&pfp->container.value.refs[len], avm_foreign | a_readonly, npath - dpath, (const byte *)dpath); ++len; } if (!*npath) break; dpath = npath + 1; } r_set_size(&pfp->list, len); return 0; } /* Add a library search path to the list. */ int gs_main_add_lib_path(gs_main_instance * minst, const char *lpath) { /* Account for the possibility that the first element */ /* is gp_current_directory name added by set_lib_paths. */ int first_is_here = (r_size(&minst->lib_path.list) != 0 && minst->lib_path.container.value.refs[0].value.bytes == (const byte *)gp_current_directory_name ? 1 : 0); int code; r_set_size(&minst->lib_path.list, minst->lib_path.count + first_is_here); code = file_path_add(&minst->lib_path, lpath); minst->lib_path.count = r_size(&minst->lib_path.list) - first_is_here; if (code < 0) return code; return gs_main_set_lib_paths(minst); } /* ------ Execution ------ */ /* Complete the list of library search paths. */ /* This may involve adding or removing the current directory */ /* as the first element. */ int gs_main_set_lib_paths(gs_main_instance * minst) { ref *paths = minst->lib_path.container.value.refs; int first_is_here = (r_size(&minst->lib_path.list) != 0 && paths[0].value.bytes == (const byte *)gp_current_directory_name ? 1 : 0); int count = minst->lib_path.count; int code = 0; if (minst->search_here_first) { if (!(first_is_here || (r_size(&minst->lib_path.list) != 0 && !bytes_compare((const byte *)gp_current_directory_name, strlen(gp_current_directory_name), paths[0].value.bytes, r_size(&paths[0])))) ) { memmove(paths + 1, paths, count * sizeof(*paths)); make_const_string(paths, avm_foreign | a_readonly, strlen(gp_current_directory_name), (const byte *)gp_current_directory_name); } } else { if (first_is_here) memmove(paths, paths + 1, count * sizeof(*paths)); } r_set_size(&minst->lib_path.list, count + (minst->search_here_first ? 1 : 0)); if (minst->lib_path.env != 0) code = file_path_add(&minst->lib_path, minst->lib_path.env); if (minst->lib_path.final != 0 && code >= 0) code = file_path_add(&minst->lib_path, minst->lib_path.final); return code; } /* Open a file, using the search paths. */ int gs_main_lib_open(gs_main_instance * minst, const char *file_name, ref * pfile) { /* This is a separate procedure only to avoid tying up */ /* extra stack space while running the file. */ i_ctx_t *i_ctx_p = minst->i_ctx_p; #define maxfn 200 byte fn[maxfn]; uint len; return lib_file_open(file_name, strlen(file_name), fn, maxfn, &len, pfile, imemory); } /* Open and execute a file. */ int gs_main_run_file(gs_main_instance * minst, const char *file_name, int user_errors, int *pexit_code, ref * perror_object) { ref initial_file; int code = gs_main_run_file_open(minst, file_name, &initial_file); if (code < 0) return code; return gs_main_interpret(minst, &initial_file, user_errors, pexit_code, perror_object); } int gs_main_run_file_open(gs_main_instance * minst, const char *file_name, ref * pfref) { gs_main_set_lib_paths(minst); if (gs_main_lib_open(minst, file_name, pfref) < 0) { eprintf1("Can't find initialization file %s.\n", file_name); return_error(e_Fatal); } r_set_attrs(pfref, a_execute + a_executable); return 0; } /* Open and run the very first initialization file. */ private int gs_run_init_file(gs_main_instance * minst, int *pexit_code, ref * perror_object) { i_ctx_t *i_ctx_p = minst->i_ctx_p; ref ifile; ref first_token; int code; scanner_state state; gs_main_set_lib_paths(minst); if (gs_init_string_sizeof == 0) { /* Read from gs_init_file. */ code = gs_main_run_file_open(minst, gs_init_file, &ifile); } else { /* Read from gs_init_string. */ code = file_read_string(gs_init_string, gs_init_string_sizeof, &ifile, iimemory); } if (code < 0) { *pexit_code = 255; return code; } /* Check to make sure the first token is an integer */ /* (for the version number check.) */ scanner_state_init(&state, false); code = scan_token(i_ctx_p, ifile.value.pfile, &first_token, &state); if (code != 0 || !r_has_type(&first_token, t_integer)) { eprintf1("Initialization file %s does not begin with an integer.\n", gs_init_file); *pexit_code = 255; return_error(e_Fatal); } *++osp = first_token; r_set_attrs(&ifile, a_executable); return gs_main_interpret(minst, &ifile, minst->user_errors, pexit_code, perror_object); } /* Run a string. */ int gs_main_run_string(gs_main_instance * minst, const char *str, int user_errors, int *pexit_code, ref * perror_object) { return gs_main_run_string_with_length(minst, str, (uint) strlen(str), user_errors, pexit_code, perror_object); } int gs_main_run_string_with_length(gs_main_instance * minst, const char *str, uint length, int user_errors, int *pexit_code, ref * perror_object) { int code; code = gs_main_run_string_begin(minst, user_errors, pexit_code, perror_object); if (code < 0) return code; code = gs_main_run_string_continue(minst, str, length, user_errors, pexit_code, perror_object); if (code != e_NeedInput) return code; return gs_main_run_string_end(minst, user_errors, pexit_code, perror_object); } /* Set up for a suspendable run_string. */ int gs_main_run_string_begin(gs_main_instance * minst, int user_errors, int *pexit_code, ref * perror_object) { const char *setup = ".runstringbegin"; ref rstr; int code; gs_main_set_lib_paths(minst); make_const_string(&rstr, avm_foreign | a_readonly | a_executable, strlen(setup), (const byte *)setup); code = gs_main_interpret(minst, &rstr, user_errors, pexit_code, perror_object); return (code == e_NeedInput ? 0 : code == 0 ? e_Fatal : code); } /* Continue running a string with the option of suspending. */ int gs_main_run_string_continue(gs_main_instance * minst, const char *str, uint length, int user_errors, int *pexit_code, ref * perror_object) { ref rstr; if (length == 0) return 0; /* empty string signals EOF */ make_const_string(&rstr, avm_foreign | a_readonly, length, (const byte *)str); return gs_main_interpret(minst, &rstr, user_errors, pexit_code, perror_object); } /* Signal EOF when suspended. */ int gs_main_run_string_end(gs_main_instance * minst, int user_errors, int *pexit_code, ref * perror_object) { ref rstr; make_empty_const_string(&rstr, avm_foreign | a_readonly); return gs_main_interpret(minst, &rstr, user_errors, pexit_code, perror_object); } /* ------ Operand stack access ------ */ /* These are built for comfort, not for speed. */ private int push_value(gs_main_instance *minst, ref * pvalue) { i_ctx_t *i_ctx_p = minst->i_ctx_p; int code = ref_stack_push(&o_stack, 1); if (code < 0) return code; *ref_stack_index(&o_stack, 0L) = *pvalue; return 0; } int gs_push_boolean(gs_main_instance * minst, bool value) { ref vref; make_bool(&vref, value); return push_value(minst, &vref); } int gs_push_integer(gs_main_instance * minst, long value) { ref vref; make_int(&vref, value); return push_value(minst, &vref); } int gs_push_real(gs_main_instance * minst, floatp value) { ref vref; make_real(&vref, value); return push_value(minst, &vref); } int gs_push_string(gs_main_instance * minst, byte * chars, uint length, bool read_only) { ref vref; make_string(&vref, avm_foreign | (read_only ? a_readonly : a_all), length, (byte *) chars); return push_value(minst, &vref); } private int pop_value(i_ctx_t *i_ctx_p, ref * pvalue) { if (!ref_stack_count(&o_stack)) return_error(e_stackunderflow); *pvalue = *ref_stack_index(&o_stack, 0L); return 0; } int gs_pop_boolean(gs_main_instance * minst, bool * result) { i_ctx_t *i_ctx_p = minst->i_ctx_p; ref vref; int code = pop_value(i_ctx_p, &vref); if (code < 0) return code; check_type_only(vref, t_boolean); *result = vref.value.boolval; ref_stack_pop(&o_stack, 1); return 0; } int gs_pop_integer(gs_main_instance * minst, long *result) { i_ctx_t *i_ctx_p = minst->i_ctx_p; ref vref; int code = pop_value(i_ctx_p, &vref); if (code < 0) return code; check_type_only(vref, t_integer); *result = vref.value.intval; ref_stack_pop(&o_stack, 1); return 0; } int gs_pop_real(gs_main_instance * minst, float *result) { i_ctx_t *i_ctx_p = minst->i_ctx_p; ref vref; int code = pop_value(i_ctx_p, &vref); if (code < 0) return code; switch (r_type(&vref)) { case t_real: *result = vref.value.realval; break; case t_integer: *result = (float)(vref.value.intval); break; default: return_error(e_typecheck); } ref_stack_pop(&o_stack, 1); return 0; } int gs_pop_string(gs_main_instance * minst, gs_string * result) { i_ctx_t *i_ctx_p = minst->i_ctx_p; ref vref; int code = pop_value(i_ctx_p, &vref); if (code < 0) return code; switch (r_type(&vref)) { case t_name: name_string_ref(&vref, &vref); code = 1; goto rstr; case t_string: code = (r_has_attr(&vref, a_write) ? 0 : 1); rstr:result->data = vref.value.bytes; result->size = r_size(&vref); break; default: return_error(e_typecheck); } ref_stack_pop(&o_stack, 1); return code; } /* ------ Termination ------ */ /* Get the names of temporary files. * Each name is null terminated, and the last name is * terminated by a double null. * We retrieve the names of temporary files just before * the interpreter finishes, and then delete the files * after the interpreter has closed all files. */ private char *gs_main_tempnames(gs_main_instance *minst) { i_ctx_t *i_ctx_p = minst->i_ctx_p; ref *SAFETY; ref *tempfiles; ref keyval[2]; /* for key and value */ char *tempnames = NULL; int i; int idict; int len = 0; const byte *data = NULL; uint size; if (minst->init_done >= 2) { if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 || dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0) return NULL; /* get lengths of temporary filenames */ idict = dict_first(tempfiles); while ((idict = dict_next(tempfiles, idict, &keyval[0])) >= 0) { if (obj_string_data(&keyval[0], &data, &size) >= 0) len += size + 1; } if (len != 0) tempnames = (char *)malloc(len+1); if (tempnames) { memset(tempnames, 0, len+1); /* copy temporary filenames */ idict = dict_first(tempfiles); i = 0; while ((idict = dict_next(tempfiles, idict, &keyval[0])) >= 0) { if (obj_string_data(&keyval[0], &data, &size) >= 0) { memcpy(tempnames+i, (char *)data, size); i+= size; tempnames[i++] = '\0'; } } } } return tempnames; } /* Free all resources and exit. */ void gs_main_finit(gs_main_instance * minst, int exit_status, int code) { i_ctx_t *i_ctx_p = minst->i_ctx_p; int exit_code; ref error_object; char *tempnames; /* * Previous versions of this code closed the devices in the * device list here. Since these devices are now prototypes, * they cannot be opened, so they do not need to be closed; * alloc_restore_all will close dynamically allocated devices. */ tempnames = gs_main_tempnames(minst); /* Flush stdout and stderr */ if (minst->init_done >= 2) gs_main_run_string(minst, "(%stdout) (w) file closefile (%stderr) (w) file closefile quit", 0 , &exit_code, &error_object); gp_readline_finit(minst->readline_data); if (gs_debug_c(':')) print_resource_usage(minst, &gs_imemory, "Final"); /* Do the equivalent of a restore "past the bottom". */ /* This will release all memory, close all open files, etc. */ if (minst->init_done >= 1) alloc_restore_all(idmemory); /* clean up redirected stdout */ if (minst->fstdout2 && (minst->fstdout2 != minst->fstdout) && (minst->fstdout2 != minst->fstderr)) { fclose(minst->fstdout2); minst->fstdout2 = (FILE *)NULL; } minst->stdout_is_redirected = 0; minst->stdout_to_stderr = 0; /* remove any temporary files, after ghostscript has closed files */ if (tempnames) { char *p = tempnames; while (*p) { unlink(p); p += strlen(p) + 1; } free(tempnames); } gs_lib_finit(exit_status, code); } void gs_exit_with_code(int exit_status, int code) { gs_finit(exit_status, code); gp_do_exit(exit_status); } void gs_exit(int exit_status) { gs_exit_with_code(exit_status, 0); } void gs_abort(void) { gs_exit(1); /* This is the ONLY reference to exit() */ /* Even this one should be removed */ exit(1); } /* ------ Debugging ------ */ /* Print resource usage statistics. */ private void print_resource_usage(const gs_main_instance * minst, gs_dual_memory_t * dmem, const char *msg) { ulong allocated = 0, used = 0; long utime[2]; gp_get_usertime(utime); { int i; for (i = 0; i < countof(dmem->spaces_indexed); ++i) { gs_ref_memory_t *mem = dmem->spaces_indexed[i]; if (mem != 0 && (i == 0 || mem != dmem->spaces_indexed[i - 1])) { gs_memory_status_t status; gs_ref_memory_t *mem_stable = (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)mem); gs_memory_status((gs_memory_t *)mem, &status); allocated += status.allocated; used += status.used; if (mem_stable != mem) { gs_memory_status((gs_memory_t *)mem_stable, &status); allocated += status.allocated; used += status.used; } } } } dprintf4("%% %s time = %g, memory allocated = %lu, used = %lu\n", msg, utime[0] - minst->base_time[0] + (utime[1] - minst->base_time[1]) / 1000000000.0, allocated, used); } /* Dump the stacks after interpretation */ void gs_main_dump_stack(gs_main_instance *minst, int code, ref * perror_object) { i_ctx_t *i_ctx_p = minst->i_ctx_p; zflush(i_ctx_p); /* force out buffered output */ dprintf1("\nUnexpected interpreter error %d.\n", code); if (perror_object != 0) { dputs("Error object: "); debug_print_ref(perror_object); dputc('\n'); } debug_dump_stack(&o_stack, "Operand stack"); debug_dump_stack(&e_stack, "Execution stack"); debug_dump_stack(&d_stack, "Dictionary stack"); } /* Backward compatibility */ void gs_debug_dump_stack(int code, ref * perror_object) { gs_main_dump_stack(gs_main_instance_default(), code, perror_object); } /* Provide a single point for all "C" stdout and stderr. * Eventually these will always be referenced through an instance structure. * We don't know which instance is running (and currently only one * instance is possible) so use the default instance. */ int gs_main_outwrite(gs_main_instance *minst, const char *str, int len) { int code; FILE *fout; if (len == 0) return 0; if (minst->stdout_is_redirected) { if (minst->stdout_to_stderr) return gs_main_errwrite(minst, str, len); fout = minst->fstdout2; } else if (minst->stdout_fn) { return (*minst->stdout_fn)(minst->caller_handle, str, len); } else { fout = minst->fstdout; } code = fwrite(str, 1, len, fout); fflush(fout); return code; } int gs_main_errwrite(gs_main_instance *minst, const char *str, int len) { int code; if (len == 0) return 0; if (minst->stderr_fn) return (*minst->stderr_fn)(minst->caller_handle, str, len); code = fwrite(str, 1, len, minst->fstderr); fflush(minst->fstderr); return code; } int outwrite(const char *str, int len) { return gs_main_outwrite(gs_main_instance_default(), str, len); } int errwrite(const char *str, int len) { return gs_main_errwrite(gs_main_instance_default(), str, len); } void outflush(void) { gs_main_instance * minst = gs_main_instance_default(); if (minst->stdout_is_redirected) { if (minst->stdout_to_stderr) { if (!minst->stderr_fn) fflush(minst->fstderr); } else fflush(minst->fstdout2); } else if (!minst->stdout_fn) fflush(minst->fstdout); } void errflush(void) { gs_main_instance * minst = gs_main_instance_default(); if (!minst->stderr_fn) fflush(minst->fstderr); }