1 /* Copyright (C) 1992, 1994, 1996 artofcode LLC. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gserver.c,v 1.7 2002/06/16 05:48:55 lpd Exp $ */
18 /* Server front end for Ghostscript, replacing gs.c. */
19 #include "memory_.h"
20 #include "string_.h"
21 #include "ghost.h"
22 #include "imemory.h" /* for iutil.h */
23 #include "interp.h" /* for gs_interp_reset */
24 #include "iutil.h" /* for obj_cvs */
25 #include "main.h"
26 #include "ostack.h"
27 #include "store.h"
28 #include "gspaint.h" /* for gs_erasepage */
29
30 /*
31 * This file provides a very simple procedural interface to the Ghostscript
32 * PostScript/PDF language interpreter, with a rudimentary provision for
33 * saving and restoring state around "jobs".
34 * See below for the descriptions of individual procedures.
35 *
36 * All routines in this file return an integer value which is 0 if the
37 * routine completed successfully, non-0 if an error occurred.
38 */
39
40 /* ------ Public interface ------ */
41
42 /*
43 * Initialize Ghostscript. fno_stdin, fno_stdout, and fno_stderr are
44 * file handles that Ghostscript will use in place of stdin, stdout,
45 * and stderr respectively. This routine should be called once at the
46 * beginning of execution, and not after that.
47 *
48 * This routine establishes a "baseline" initial state for Ghostscript,
49 * which includes reading in the standard Ghostscript initialization files
50 * such as gs_init.ps and gs_fonts.ps. This baseline is always used as the
51 * starting state for gs_server_run_string and gs_server_run_files, unless
52 * modified as described below.
53 *
54 * This routine 'opens' the default driver.
55 */
56
57 int gs_server_initialize(int fno_stdin, int fno_stdout, int fno_stderr,
58 const char *init_str);
59
60 /*
61 * Execute a string containing PostScript code. The effects of this code
62 * do modify the baseline state for future calls of ...run_string and
63 * ...run_files. There are four cases of return values:
64 * value = 0: normal return.
65 * value = e_Quit: the PostScript code executed a `quit'.
66 * value = e_Fatal: the PostScript code encountered a fatal error.
67 * *exit_code_ptr holds the C exit code.
68 * other value: the PostScript code encountered a PostScript error
69 * while processing another error, or some other fatal
70 * PostScript error.
71 *
72 * errstr points to a string area of length errstr_max_len for reporting
73 * the PostScript object causing the error. In the case of an error return,
74 * the characters from errstr[0] through errstr[*errstr_len_ptr-1] will
75 * contain a representation of the error object. *errstr_len_ptr will not
76 * exceed errstr_max_len.
77 */
78
79 int gs_server_run_string(const char *str, int *exit_code_ptr,
80 char *errstr, int errstr_max_len,
81 int *errstr_len_ptr);
82
83 /*
84 * Run a sequence of files containing PostScript code. If permanent is 0,
85 * the files do not affect the baseline state; if permanent is 1, they do
86 * affect the baseline state, just like ...run_string. The returned value,
87 * exit code, and error string are the same as for gs_server_run_string.
88 *
89 * If permanent is 0, the output page buffer is cleared before running the
90 * first file (equivalent to `erasepage').
91 */
92
93 int gs_server_run_files(const char **file_names, int permanent,
94 int *exit_code_ptr, char *errstr,
95 int errstr_max_len, int *errstr_len_ptr);
96
97 /*
98 * Terminate Ghostscript. Ghostscript will release all memory and close
99 * all files it has opened, including the ones referenced by fno_stdin,
100 * fno_stdout, and fno_stderr.
101 *
102 * This routine 'closes' the default driver.
103 */
104
105 int gs_server_terminate();
106
107 /* ------ Example of use ------ */
108
109 /* To run this example, change the 0 to 1 in the following line. */
110 #if 0
111
112 /*
113 * This example predefines the name fubar, prints out its value,
114 * and then renders the golfer art file supplied with Ghostscript.
115 */
116
117 #include <fcntl.h>
118 #include <sys/stat.h>
119 int
120 main(int argc, char *argv[])
121 {
122 int code, exit_code;
123
124 #define emax 50
125 char errstr[emax + 1];
126 int errlen;
127 static const char *fnames[] =
128 {"golfer.eps", 0};
129 FILE *cin = fopen("stdin.tmp", "w+");
130 int sout = open("stdout.tmp", O_WRONLY | O_CREAT | O_TRUNC,
131 S_IREAD | S_IWRITE);
132 int serr = open("stderr.tmp", O_WRONLY | O_CREAT | O_TRUNC,
133 S_IREAD | S_IWRITE);
134
135 code = gs_server_initialize(fileno(cin), sout, serr,
136 "/fubar 42 def");
137 fprintf(stdout, "init: code %d\n", code);
138 if (code < 0)
139 goto x;
140 code = gs_server_run_string("fubar == flush", &exit_code,
141 errstr, emax, &errlen);
142 fprintf(stdout, "print: code %d\n", code);
143 if (code < 0)
144 goto x;
145 code = gs_server_run_files(fnames, 0, &exit_code,
146 errstr, emax, &errlen);
147 fprintf(stdout, "golfer: code %d\n", code);
148 if (code < 0)
149 goto x;
150 errlen = 0;
151 code = gs_server_run_string("fubar 0 div", &exit_code,
152 errstr, emax, &errlen);
153 errstr[errlen] = 0;
154 fprintf(stdout, "0 div: code %d object %s\n", code, errstr);
155 errlen = 0;
156 code = gs_server_run_string("xxx", &exit_code,
157 errstr, emax, &errlen);
158 errstr[errlen] = 0;
159 fprintf(stdout, "undef: code %d object %s\n", code, errstr);
160 x:code = gs_server_terminate();
161 fprintf(stdout, "end: code %d\n", code);
162 fflush(stdout);
163 close(serr);
164 close(sout);
165 fclose(cin);
166 return code;
167 }
168
169 #endif
170
171 /* ------ Private definitions ------ */
172
173 /* Forward references */
174 private int job_begin(void);
175 private int job_end(void);
176 private void errstr_report(ref *, char *, int, int *);
177
178 /* ------ Public routines ------ */
179
180 /* Initialize Ghostscript. */
181
182 int
gs_server_initialize(int fno_stdin,int fno_stdout,int fno_stderr,const char * init_str)183 gs_server_initialize(int fno_stdin, int fno_stdout, int fno_stderr,
184 const char *init_str)
185 {
186 int code, exit_code; /* discard exit_code for now */
187 int errstr_len; /* discard */
188 FILE *c_stdin, *c_stdout, *c_stderr;
189
190 /* Establish C-compatible files for stdout and stderr. */
191 c_stdin = fdopen(fno_stdin, "r");
192 if (c_stdin == NULL)
193 return -1;
194 c_stdout = fdopen(fno_stdout, "w");
195 if (c_stdout == NULL)
196 return -1;
197 c_stderr = fdopen(fno_stderr, "w");
198 if (c_stderr == NULL)
199 return -1;
200 /* Initialize the Ghostscript interpreter. */
201 if ((code = gs_init0(c_stdin, c_stdout, c_stderr, 0)) < 0 ||
202 (code = gs_init1()) < 0 ||
203 (code = gs_init2()) < 0
204 )
205 return code;
206 code = gs_server_run_string("/QUIET true def /NOPAUSE true def",
207 &exit_code,
208 (char *)0, 0, &errstr_len);
209 if (code < 0)
210 return code;
211 return (init_str == NULL ? 0 :
212 gs_server_run_string(init_str, &exit_code,
213 (char *)0, 0, &errstr_len));
214 }
215
216 /* Run a string. */
217
218 int
gs_server_run_string(const char * str,int * exit_code_ptr,char * errstr,int errstr_max_len,int * errstr_len_ptr)219 gs_server_run_string(const char *str, int *exit_code_ptr,
220 char *errstr, int errstr_max_len, int *errstr_len_ptr)
221 {
222 ref error_object;
223 int code;
224
225 make_tasv(&error_object, t_string, 0, 0, bytes, 0);
226 code = gs_run_string(str, 0, exit_code_ptr, &error_object);
227 if (code < 0)
228 errstr_report(&error_object, errstr, errstr_max_len,
229 errstr_len_ptr);
230 return code;
231 }
232
233 /* Run files. */
234
235 int
gs_server_run_files(const char ** file_names,int permanent,int * exit_code_ptr,char * errstr,int errstr_max_len,int * errstr_len_ptr)236 gs_server_run_files(const char **file_names, int permanent,
237 int *exit_code_ptr, char *errstr, int errstr_max_len, int *errstr_len_ptr)
238 {
239 int code = 0;
240 ref error_object;
241 const char **pfn;
242
243 if (!permanent)
244 job_begin();
245 make_tasv(&error_object, t_string, 0, 0, bytes, 0);
246 for (pfn = file_names; *pfn != NULL && code == 0; pfn++)
247 code = gs_run_file(*pfn, 0, exit_code_ptr, &error_object);
248 if (!permanent)
249 job_end();
250 if (code < 0)
251 errstr_report(&error_object, errstr, errstr_max_len,
252 errstr_len_ptr);
253 return code;
254 }
255
256 /* Terminate Ghostscript. */
257
258 int
gs_server_terminate()259 gs_server_terminate()
260 {
261 gs_finit(0, 0);
262 return 0;
263 }
264
265 /* ------ Private routines ------ */
266
267 private ref job_save; /* 'save' object for baseline state */
268
269 extern int zsave(os_ptr), zrestore(os_ptr);
270
271 /* Start a 'job' by restoring the baseline state. */
272
273 private int
job_begin()274 job_begin()
275 {
276 int code;
277
278 /* Ghostscript doesn't provide erasepage as an operator. */
279 /* However, we can get the same effect by calling gs_erasepage. */
280 extern gs_state *igs;
281
282 if ((code = gs_erasepage(igs)) < 0)
283 return code;
284 code = zsave(osp);
285 if (code == 0)
286 job_save = *osp--;
287 return code;
288 }
289
290 /* End a 'job'. */
291
292 private int
job_end()293 job_end()
294 {
295 gs_interp_reset();
296 *++osp = job_save;
297 return zrestore(osp);
298 }
299
300 /* Produce a printable representation of an error object. */
301
302 private void
errstr_report(ref * perror_object,char * errstr,int errstr_max_len,int * errstr_len_ptr)303 errstr_report(ref * perror_object, char *errstr, int errstr_max_len,
304 int *errstr_len_ptr)
305 {
306 int code = obj_cvs(perror_object, (byte *) errstr,
307 (uint) errstr_max_len, (uint *) errstr_len_ptr,
308 false);
309
310 if (code < 0) {
311 const char *ustr = "[unprintable]";
312 int len = min(strlen(ustr), errstr_max_len);
313
314 memcpy(errstr, ustr, len);
315 *errstr_len_ptr = len;
316 }
317 }
318