Commit | Line | Data |
---|---|---|
c2d43213 WJ |
1 | /* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
19 | ||
20 | /* gs.c */ | |
21 | /* Driver program for Ghostscript */ | |
22 | #include <stdio.h> | |
23 | #include "memory_.h" | |
24 | #include "string_.h" | |
25 | #include "ghost.h" | |
26 | #include "alloc.h" | |
27 | #include "estack.h" | |
28 | #include "ostack.h" | |
29 | #include "store.h" | |
30 | #include "stream.h" | |
31 | ||
32 | #ifndef GS_LIB | |
33 | # define GS_LIB "GS_LIB" | |
34 | #endif | |
35 | ||
36 | #ifndef PROGRAM_NAME | |
37 | # define PROGRAM_NAME "Ghostscript" | |
38 | #endif | |
39 | ||
40 | /* Library routines not declared in a standard header */ | |
41 | extern char *getenv(P1(const char *)); | |
42 | ||
43 | /* Exported data */ | |
44 | uint memory_chunk_size = 20000; | |
45 | /* File name search paths */ | |
46 | const char **gs_lib_paths; | |
47 | private int gs_lib_count; | |
48 | private char *gs_lib_env_path; | |
49 | ||
50 | /* Configuration information imported from gconfig.c. */ | |
51 | extern const char *gs_lib_default_path; | |
52 | extern const char *gs_init_file; | |
53 | extern ref gs_init_file_array[]; | |
54 | ||
55 | /* Device procedures imported from gsdevice.c. */ | |
56 | typedef struct gx_device_s gx_device; | |
57 | extern gx_device *gs_getdevice(P1(int)); | |
58 | extern char *gs_devicename(P1(gx_device *)); | |
59 | ||
60 | /* Help string */ | |
61 | private char *gs_help1 = "\ | |
62 | Usage: gs [switches] [file1.ps file2.ps ...]\n\ | |
63 | or : gs [switches] [file1.ps ...] -- filen.ps arg1 arg2 ...\n\ | |
64 | The latter passes arg1 ... to the program in filen.ps.\n\ | |
65 | Available devices:\n "; | |
66 | /* We have to break help2 up into two parts, because the Watcom compiler */ | |
67 | /* has a limit of 510 characters for a single token. */ | |
68 | private char *gs_help2a = "\n\ | |
69 | Switches: (you can use # in place of =)\n\ | |
70 | -d<name>[=<token>] define name as token, or null if no token given\n\ | |
71 | -g<width>x<height> set width and height (`geometry') for device\n\ | |
72 | -I<prefix> add prefix to search path\n\ | |
73 | -q `quiet' mode, suppress most messages\n\ | |
74 | -r<res> set resolution for initial device\n"; | |
75 | private char *gs_help2b = "\ | |
76 | -r<xres>x<yres> set device X and Y resolution separately\n\ | |
77 | -s<name>=<string> define name as string\n\ | |
78 | -sDEVICE=<devname> select initial device\n\ | |
79 | -sOUTPUTFILE=<file> select output file, embed %d for page #,\n\ | |
80 | |command to pipe\n\ | |
81 | `-' alone as a file name means read from stdin non-interactively.\n\ | |
82 | For more information, please read the use.doc file.\n"; | |
83 | ||
84 | /* Forward references */ | |
85 | private int esc_strlen(P1(const char *)); | |
86 | private void esc_strcat(P2(char *, const char *)); | |
87 | private void runarg(P4(char **, char *, char *, int)); | |
88 | private void run_string(P1(char *)); | |
89 | private void init1(), init2(); | |
90 | private void set_lib_paths(); | |
91 | private void run_file(P2(const char *file_name, int user_errors)); | |
92 | private void debug_dump_stack(P1(int code)); | |
93 | ||
94 | /* Parameters set by swproc */ | |
95 | private int user_errors; | |
96 | private int quiet; | |
97 | private int batch; | |
98 | ||
99 | /* Static versions of argc and argv (for -- only) */ | |
100 | private int static_argc; | |
101 | private char **static_argv; | |
102 | ||
103 | main(int argc, char *argv[]) | |
104 | { int num_files; | |
105 | int swproc(P2(char **, char *)); | |
106 | void argproc(P2(char **, int)); | |
107 | static_argc = argc; | |
108 | static_argv = argv; | |
109 | /* Do platform-dependent initialization. */ | |
110 | /* We have to do this as the very first thing, */ | |
111 | /* because it detects attempts to run 80N86 executables (N>0) */ | |
112 | /* on incompatible processors. */ | |
113 | gp_init(); | |
114 | /* Initialize the file search paths */ | |
115 | gs_lib_env_path = 0; | |
116 | { char *lib = getenv(GS_LIB); | |
117 | if ( lib != 0 ) | |
118 | { int len = strlen(lib); | |
119 | gs_lib_env_path = gs_malloc(len + 1, 1, "GS_LIB"); | |
120 | strcpy(gs_lib_env_path, lib); | |
121 | } | |
122 | } | |
123 | gs_lib_paths = | |
124 | (const char **)gs_malloc(argc + 2, sizeof(char *), "-I array"); | |
125 | gs_lib_count = 0; | |
126 | set_lib_paths(); | |
127 | /* Execute files named in the command line, */ | |
128 | /* processing options along the way. */ | |
129 | /* Wait until the first file name (or the end */ | |
130 | /* of the line) to finish initialization. */ | |
131 | batch = 0; | |
132 | quiet = 0; | |
133 | user_errors = 1; | |
134 | num_files = gs_main(argc, argv, swproc, argproc); | |
135 | if ( num_files == 0 ) | |
136 | { init2(); | |
137 | } | |
138 | if ( !batch ) run_string("start"); | |
139 | gs_exit(0); | |
140 | } | |
141 | ||
142 | /* Process switches */ | |
143 | int | |
144 | swproc(char **swp, char *arg) | |
145 | { char sw = (*swp)[1]; | |
146 | switch ( sw ) | |
147 | { | |
148 | default: | |
149 | return -1; | |
150 | case 0: /* read stdin as a file */ | |
151 | batch = 1; | |
152 | /* Set NOPAUSE so showpage won't try to read from stdin. */ | |
153 | { char *d = "-dNOPAUSE"; | |
154 | swproc(&d, d); | |
155 | } | |
156 | init2(); /* Finish initialization */ | |
157 | run_string("(%stdin) (r) file cvx execute"); | |
158 | break; | |
159 | case '-': /* run with command line args */ | |
160 | case '+': /* ditto */ | |
161 | { int nstrs = static_argv + static_argc - (swp + 2); | |
162 | if ( nstrs < 0 ) /* no file to run! */ | |
163 | { printf("Usage: gs ... -- file.ps arg1 ... argn\n"); | |
164 | gs_exit(1); | |
165 | } | |
166 | runarg(swp + 1, "{userdict /ARGUMENTS [", "] put (", nstrs); | |
167 | } | |
168 | gs_exit(0); | |
169 | case 'h': /* print help */ | |
170 | case '?': | |
171 | fputs(gs_help1, stdout); | |
172 | { int i; | |
173 | gx_device *pdev; | |
174 | for ( i = 0; (pdev = gs_getdevice(i)) != 0; i++ ) | |
175 | printf(" %s", gs_devicename(pdev)); | |
176 | } | |
177 | fputs(gs_help2a, stdout); | |
178 | fputs(gs_help2b, stdout); | |
179 | gs_exit(0); | |
180 | case 'I': /* specify search path */ | |
181 | gs_lib_paths[gs_lib_count] = arg; | |
182 | gs_lib_count++; | |
183 | set_lib_paths(); | |
184 | break; | |
185 | case 'q': /* quiet startup */ | |
186 | { ref vnull; | |
187 | quiet = 1; | |
188 | init1(); | |
189 | make_null(&vnull); | |
190 | initial_enter_name("QUIET", &vnull); | |
191 | } break; | |
192 | case 'D': /* define name */ | |
193 | case 'd': | |
194 | case 'S': /* define name as string */ | |
195 | case 's': | |
196 | { char *eqp = strchr(arg, '='); | |
197 | int isd = (sw == 'D' || sw == 'd'); | |
198 | ref value; | |
199 | if ( eqp == NULL ) eqp = strchr(arg, '#'); | |
200 | /* Initialize the object memory, scanner, and */ | |
201 | /* name table now if needed. */ | |
202 | init1(); | |
203 | if ( eqp == arg ) | |
204 | { printf("Usage: -dname, -dname=token, -sname=string\n"); | |
205 | gs_exit(1); | |
206 | } | |
207 | if ( eqp == NULL ) | |
208 | { if ( isd ) make_null(&value); | |
209 | else make_tasv(&value, t_string, a_read+a_execute, | |
210 | 0, bytes, (byte *)""); | |
211 | } | |
212 | else | |
213 | { int code; | |
214 | *eqp++ = 0; /* delimit name */ | |
215 | if ( isd ) | |
216 | { stream astream; | |
217 | sread_string(&astream, | |
218 | (byte *)eqp, strlen(eqp)); | |
219 | code = scan_token(&astream, 0, &value); | |
220 | if ( code ) | |
221 | { printf("-dname= must be followed by a valid token\n"); | |
222 | gs_exit(1); | |
223 | } | |
224 | } | |
225 | else | |
226 | { int len = strlen(eqp); | |
227 | char *str = gs_malloc((uint)len, 1, "-s"); | |
228 | if ( str == 0 ) | |
229 | { lprintf("Out of memory!\n"); | |
230 | gs_exit(1); | |
231 | } | |
232 | memcpy(str, eqp, len); | |
233 | make_tasv(&value, t_string, a_read+a_execute, | |
234 | len, bytes, (byte *)str); | |
235 | } | |
236 | } | |
237 | /* Enter the name in systemdict */ | |
238 | initial_enter_name(arg, &value); | |
239 | break; | |
240 | } | |
241 | case 'g': /* define device geometry */ | |
242 | { long width, height; | |
243 | ref value; | |
244 | init1(); | |
245 | if ( sscanf(arg, "%ldx%ld", &width, &height) != 2 ) | |
246 | { printf("-g must be followed by <width>x<height>\n"); | |
247 | gs_exit(1); | |
248 | } | |
249 | make_int(&value, width); | |
250 | initial_enter_name("DEVICEWIDTH", &value); | |
251 | make_int(&value, height); | |
252 | initial_enter_name("DEVICEHEIGHT", &value); | |
253 | break; | |
254 | } | |
255 | case 'M': /* set memory allocation increment */ | |
256 | { unsigned msize = 0; | |
257 | sscanf(arg, "%d", &msize); | |
258 | if ( msize <= 0 || msize >= 64 ) | |
259 | { printf("-M must be between 1 and 64\n"); | |
260 | gs_exit(1); | |
261 | } | |
262 | memory_chunk_size = msize << 10; | |
263 | } | |
264 | break; | |
265 | case 'r': /* define device resolution */ | |
266 | { long xres, yres; | |
267 | ref value; | |
268 | init1(); | |
269 | switch ( sscanf(arg, "%ldx%ld", &xres, &yres) ) | |
270 | { | |
271 | default: | |
272 | printf("-r must be followed by <res> or <xres>x<yres>\n"); | |
273 | gs_exit(1); | |
274 | case 1: /* -r<res> */ | |
275 | yres = xres; | |
276 | case 2: /* -r<xres>x<yres> */ | |
277 | make_int(&value, xres); | |
278 | initial_enter_name("DEVICEXRESOLUTION", &value); | |
279 | make_int(&value, yres); | |
280 | initial_enter_name("DEVICEYRESOLUTION", &value); | |
281 | } | |
282 | break; | |
283 | } | |
284 | } | |
285 | return 0; | |
286 | } | |
287 | ||
288 | /* Define versions of strlen and strcat that insert \ escapes */ | |
289 | /* before \, (, and ). */ | |
290 | #define needs_esc(ch) ((ch) == '(' || (ch) == ')' || (ch) == '\\') | |
291 | private int | |
292 | esc_strlen(const char *str) | |
293 | { int n = strlen(str); | |
294 | const char *p; | |
295 | for ( p = str; *p; p++ ) if ( needs_esc(*p) ) n++; | |
296 | return n; | |
297 | } | |
298 | private void | |
299 | esc_strcat(char *dest, const char *src) | |
300 | { char *d = dest + strlen(dest); | |
301 | const char *p; | |
302 | for ( p = src; *p; p++ ) | |
303 | { if ( needs_esc(*p) ) *d++ = '\\'; | |
304 | *d++ = *p; | |
305 | } | |
306 | *d = 0; | |
307 | } | |
308 | ||
309 | /* Process file names */ | |
310 | void | |
311 | argproc(char **argp, int index) | |
312 | { runarg(argp, "{", "(", 0); | |
313 | } | |
314 | private void | |
315 | runarg(char **argp, char *pre, char *post, int nstrs) | |
316 | { char *arg = *argp; | |
317 | static char *pex = ")run}execute"; | |
318 | int len = strlen(pre) + esc_strlen(arg) + strlen(post) + strlen(pex) + 1; | |
319 | char *line; | |
320 | int i; | |
321 | for ( i = 1; i <= nstrs; i++ ) | |
322 | len += esc_strlen(argp[i]) + 2; | |
323 | init2(); /* Finish initialization */ | |
324 | line = gs_malloc(len, 1, "argproc"); | |
325 | if ( line == 0 ) | |
326 | { lprintf("Out of memory!\n"); | |
327 | gs_exit(1); | |
328 | } | |
329 | strcpy(line, pre); | |
330 | for ( i = 1; i <= nstrs; i++ ) | |
331 | { strcat(line, "("); | |
332 | esc_strcat(line, argp[i]); | |
333 | strcat(line, ")"); | |
334 | } | |
335 | strcat(line, post); | |
336 | esc_strcat(line, arg); | |
337 | strcat(line, pex); | |
338 | run_string(line); | |
339 | } | |
340 | private void | |
341 | run_string(char *str) | |
342 | { int code; | |
343 | ref stref; | |
344 | make_tasv(&stref, t_string, a_executable + a_read + a_execute, | |
345 | strlen(str), bytes, (byte *)str); | |
346 | code = gs_interpret(&stref, user_errors); | |
347 | zflush((ref *)0); /* flush stdout */ | |
348 | zflushpage((ref *)0); /* force display update */ | |
349 | if ( code ) debug_dump_stack(code), gs_exit(2); | |
350 | } | |
351 | ||
352 | private int init1_done = 0, init2_done = 0; | |
353 | private void | |
354 | init1() | |
355 | { if ( !init1_done ) | |
356 | { alloc_init(gs_malloc, gs_free, memory_chunk_size); | |
357 | name_init(); | |
358 | obj_init(); /* requires name_init */ | |
359 | scan_init(); /* ditto */ | |
360 | init1_done = 1; | |
361 | } | |
362 | } | |
363 | private void | |
364 | init2() | |
365 | { init1(); | |
366 | if ( !init2_done ) | |
367 | { gs_init(); | |
368 | zop_init(); | |
369 | interp_init(1); /* requires obj_init */ | |
370 | op_init(); /* requires obj_init, scan_init */ | |
371 | /* Set up the array of additional initialization files. */ | |
372 | { ref *ifp = gs_init_file_array; | |
373 | ref ifa; | |
374 | for ( ; ifp->value.bytes != 0; ifp++ ) | |
375 | r_set_size(ifp, strlen((const char *)ifp->value.bytes)); | |
376 | make_tasv(&ifa, t_array, a_read + a_execute, | |
377 | ifp - gs_init_file_array, refs, | |
378 | gs_init_file_array); | |
379 | initial_enter_name("INITFILES", &ifa); | |
380 | } | |
381 | /* Execute the standard initialization file. */ | |
382 | run_file(gs_init_file, user_errors); | |
383 | init2_done = 1; | |
384 | } | |
385 | } | |
386 | ||
387 | /* Complete the list of library search paths. */ | |
388 | private void | |
389 | set_lib_paths() | |
390 | { const char **ppath = &gs_lib_paths[gs_lib_count]; | |
391 | if ( gs_lib_env_path != 0 ) *ppath++ = gs_lib_env_path; | |
392 | if ( gs_lib_default_path != 0 ) *ppath++ = gs_lib_default_path; | |
393 | *ppath = 0; | |
394 | } | |
395 | ||
396 | /* Open and execute a file */ | |
397 | private int | |
398 | run_open(const char *file_name, ref *pfile) | |
399 | { | |
400 | #define maxfn 200 | |
401 | byte fn[maxfn]; | |
402 | uint len; | |
403 | return lib_file_open(file_name, strlen(file_name), fn, maxfn, | |
404 | &len, pfile); | |
405 | } | |
406 | private void | |
407 | run_file(const char *file_name, int user_errors) | |
408 | { ref initial_file; | |
409 | int code; | |
410 | if ( run_open(file_name, &initial_file) < 0 ) | |
411 | { eprintf1("Can't find initialization file %s\n", file_name); | |
412 | gs_exit(1); | |
413 | } | |
414 | r_set_attrs(&initial_file, a_execute + a_executable); | |
415 | code = gs_interpret(&initial_file, user_errors); | |
416 | if ( code < 0 ) | |
417 | debug_dump_stack(code), gs_exit(1); | |
418 | } | |
419 | ||
420 | /* Debugging code */ | |
421 | extern void debug_print_ref(P1(ref *)); | |
422 | extern void debug_dump_refs(P3(ref *, uint, char *)); | |
423 | extern ref error_object; | |
424 | ||
425 | /* Dump the stacks after interpretation */ | |
426 | private void | |
427 | debug_dump_stack(int code) | |
428 | { zflush(osp); /* force out buffered output */ | |
429 | dprintf1("\nUnexpected interpreter error %d!\nError object: ", code); | |
430 | debug_print_ref(&error_object); | |
431 | dputc('\n'); | |
432 | debug_dump_refs(osbot, osp + 1 - osbot, "Operand stack"); | |
433 | debug_dump_refs(esbot, esp + 1 - esbot, "Execution stack"); | |
434 | } |