1 /* Copyright (C) 1992, 1994, 1996 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gserver.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */
20 /* Server front end for Ghostscript, replacing gs.c. */
21 #include "memory_.h"
22 #include "string_.h"
23 #include "ghost.h"
24 #include "imemory.h"		/* for iutil.h */
25 #include "interp.h"		/* for gs_interp_reset */
26 #include "iutil.h"		/* for obj_cvs */
27 #include "main.h"
28 #include "ostack.h"
29 #include "store.h"
30 #include "gspaint.h"		/* for gs_erasepage */
31 
32 /*
33  * This file provides a very simple procedural interface to the Ghostscript
34  * PostScript/PDF language interpreter, with a rudimentary provision for
35  * saving and restoring state around "jobs".
36  * See below for the descriptions of individual procedures.
37  *
38  * All routines in this file return an integer value which is 0 if the
39  * routine completed successfully, non-0 if an error occurred.
40  */
41 
42 /* ------ Public interface ------ */
43 
44 /*
45  * Initialize Ghostscript.  fno_stdin, fno_stdout, and fno_stderr are
46  * file handles that Ghostscript will use in place of stdin, stdout,
47  * and stderr respectively.  This routine should be called once at the
48  * beginning of execution, and not after that.
49  *
50  * This routine establishes a "baseline" initial state for Ghostscript,
51  * which includes reading in the standard Ghostscript initialization files
52  * such as gs_init.ps and gs_fonts.ps.  This baseline is always used as the
53  * starting state for gs_server_run_string and gs_server_run_files, unless
54  * modified as described below.
55  *
56  * This routine 'opens' the default driver.
57  */
58 
59 int gs_server_initialize(P4(int fno_stdin, int fno_stdout, int fno_stderr,
60 			    const char *init_str));
61 
62 /*
63  * Execute a string containing PostScript code.  The effects of this code
64  * do modify the baseline state for future calls of ...run_string and
65  * ...run_files.  There are four cases of return values:
66  *      value = 0: normal return.
67  *      value = e_Quit: the PostScript code executed a `quit'.
68  *      value = e_Fatal: the PostScript code encountered a fatal error.
69  *              *exit_code_ptr holds the C exit code.
70  *      other value: the PostScript code encountered a PostScript error
71  *              while processing another error, or some other fatal
72  *              PostScript error.
73  *
74  * errstr points to a string area of length errstr_max_len for reporting
75  * the PostScript object causing the error.  In the case of an error return,
76  * the characters from errstr[0] through errstr[*errstr_len_ptr-1] will
77  * contain a representation of the error object.  *errstr_len_ptr will not
78  * exceed errstr_max_len.
79  */
80 
81 int gs_server_run_string(P5(const char *str, int *exit_code_ptr,
82 			    char *errstr, int errstr_max_len,
83 			    int *errstr_len_ptr));
84 
85 /*
86  * Run a sequence of files containing PostScript code.  If permanent is 0,
87  * the files do not affect the baseline state; if permanent is 1, they do
88  * affect the baseline state, just like ...run_string.  The returned value,
89  * exit code, and error string are the same as for gs_server_run_string.
90  *
91  * If permanent is 0, the output page buffer is cleared before running the
92  * first file (equivalent to `erasepage').
93  */
94 
95 int gs_server_run_files(P6(const char **file_names, int permanent,
96 			   int *exit_code_ptr, char *errstr,
97 			   int errstr_max_len, int *errstr_len_ptr));
98 
99 /*
100  * Terminate Ghostscript.  Ghostscript will release all memory and close
101  * all files it has opened, including the ones referenced by fno_stdin,
102  * fno_stdout, and fno_stderr.
103  *
104  * This routine 'closes' the default driver.
105  */
106 
107 int gs_server_terminate(P0());
108 
109 /* ------ Example of use ------ */
110 
111 /* To run this example, change the 0 to 1 in the following line. */
112 #if 0
113 
114 /*
115  * This example predefines the name fubar, prints out its value,
116  * and then renders the golfer art file supplied with Ghostscript.
117  */
118 
119 #include <fcntl.h>
120 #include <sys/stat.h>
121 int
122 main(int argc, char *argv[])
123 {
124     int code, exit_code;
125 
126 #define emax 50
127     char errstr[emax + 1];
128     int errlen;
129     static const char *fnames[] =
130     {"golfer.ps", 0};
131     FILE *cin = fopen("stdin.tmp", "w+");
132     int sout = open("stdout.tmp", O_WRONLY | O_CREAT | O_TRUNC,
133 		    S_IREAD | S_IWRITE);
134     int serr = open("stderr.tmp", O_WRONLY | O_CREAT | O_TRUNC,
135 		    S_IREAD | S_IWRITE);
136 
137     code = gs_server_initialize(fileno(cin), sout, serr,
138 				"/fubar 42 def");
139     fprintf(stdout, "init: code %d\n", code);
140     if (code < 0)
141 	goto x;
142     code = gs_server_run_string("fubar == flush", &exit_code,
143 				errstr, emax, &errlen);
144     fprintf(stdout, "print: code %d\n", code);
145     if (code < 0)
146 	goto x;
147     code = gs_server_run_files(fnames, 0, &exit_code,
148 			       errstr, emax, &errlen);
149     fprintf(stdout, "golfer: code %d\n", code);
150     if (code < 0)
151 	goto x;
152     errlen = 0;
153     code = gs_server_run_string("fubar 0 div", &exit_code,
154 				errstr, emax, &errlen);
155     errstr[errlen] = 0;
156     fprintf(stdout, "0 div: code %d object %s\n", code, errstr);
157     errlen = 0;
158     code = gs_server_run_string("xxx", &exit_code,
159 				errstr, emax, &errlen);
160     errstr[errlen] = 0;
161     fprintf(stdout, "undef: code %d object %s\n", code, errstr);
162   x:code = gs_server_terminate();
163     fprintf(stdout, "end: code %d\n", code);
164     fflush(stdout);
165     close(serr);
166     close(sout);
167     fclose(cin);
168     return code;
169 }
170 
171 #endif
172 
173 /* ------ Private definitions ------ */
174 
175 /* Forward references */
176 private int job_begin(P0());
177 private int job_end(P0());
178 private void errstr_report(P4(ref *, char *, int, int *));
179 
180 /* ------ Public routines ------ */
181 
182 /* Initialize Ghostscript. */
183 
184 int
gs_server_initialize(int fno_stdin,int fno_stdout,int fno_stderr,const char * init_str)185 gs_server_initialize(int fno_stdin, int fno_stdout, int fno_stderr,
186 		     const char *init_str)
187 {
188     int code, exit_code;	/* discard exit_code for now */
189     int errstr_len;		/* discard */
190     FILE *c_stdin, *c_stdout, *c_stderr;
191 
192     /* Establish C-compatible files for stdout and stderr. */
193     c_stdin = fdopen(fno_stdin, "r");
194     if (c_stdin == NULL)
195 	return -1;
196     c_stdout = fdopen(fno_stdout, "w");
197     if (c_stdout == NULL)
198 	return -1;
199     c_stderr = fdopen(fno_stderr, "w");
200     if (c_stderr == NULL)
201 	return -1;
202     /* Initialize the Ghostscript interpreter. */
203     gs_init0(c_stdin, c_stdout, c_stderr, 0);
204     gs_init1();
205     gs_init2();
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(P1(os_ptr)), zrestore(P1(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