xref: /plan9/sys/src/cmd/gs/src/gserver.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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