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