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