/src/ghostpdl/psi/imainarg.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 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, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Command line parsing and dispatching */ |
18 | | |
19 | | #include "ctype_.h" |
20 | | #include "memory_.h" |
21 | | #include "string_.h" |
22 | | #include <stdlib.h> /* for qsort */ |
23 | | |
24 | | #include "ghost.h" |
25 | | #include "gp.h" |
26 | | #include "gsargs.h" |
27 | | #include "gscdefs.h" |
28 | | #include "gsmalloc.h" /* for gs_malloc_limit */ |
29 | | #include "gsmdebug.h" |
30 | | #include "gspaint.h" /* for gs_erasepage */ |
31 | | #include "gxdevice.h" |
32 | | #include "gxdevmem.h" |
33 | | #include "gsdevice.h" |
34 | | #include "gxdevsop.h" /* for gxdso_* enums */ |
35 | | #include "gxclpage.h" |
36 | | #include "gdevprn.h" |
37 | | #include "stream.h" |
38 | | #include "ierrors.h" |
39 | | #include "estack.h" |
40 | | #include "ialloc.h" |
41 | | #include "strimpl.h" /* for sfilter.h */ |
42 | | #include "sfilter.h" /* for iscan.h */ |
43 | | #include "ostack.h" /* must precede iscan.h */ |
44 | | #include "iscan.h" |
45 | | #include "iconf.h" |
46 | | #include "imain.h" |
47 | | #include "imainarg.h" |
48 | | #include "iapi.h" |
49 | | #include "iminst.h" |
50 | | #include "iname.h" |
51 | | #include "store.h" |
52 | | #include "files.h" /* requires stream.h */ |
53 | | #include "interp.h" |
54 | | #include "iutil.h" |
55 | | #include "ivmspace.h" |
56 | | |
57 | | /* Import operator procedures */ |
58 | | extern int zflush(i_ctx_t *); |
59 | | extern int zflushpage(i_ctx_t *); |
60 | | |
61 | | #ifndef GS_LIB |
62 | 162k | # define GS_LIB "GS_LIB" |
63 | | #endif |
64 | | |
65 | | #ifndef GS_OPTIONS |
66 | 162k | # define GS_OPTIONS "GS_OPTIONS" |
67 | | #endif |
68 | | |
69 | | /* This is now the default number of entries we initially |
70 | | * allocate for the search path array objects. The arrays |
71 | | * will now enlarge as required - |
72 | | * see imain.c: file_path_add()/extend_path_list_container() |
73 | | */ |
74 | | |
75 | | #ifndef GS_MAX_LIB_DIRS |
76 | 162k | # define GS_MAX_LIB_DIRS 25 |
77 | | #endif |
78 | | |
79 | 0 | #define MAX_BUFFERED_SIZE 1024 |
80 | | |
81 | | /* Note: sscanf incorrectly defines its first argument as char * */ |
82 | | /* rather than const char *. This accounts for the ugly casts below. */ |
83 | | |
84 | | /* Redefine puts to use outprintf, */ |
85 | | /* so it will work even without stdio. */ |
86 | | #undef puts |
87 | 0 | #define puts(mem, str) outprintf(mem, "%s\n", str) |
88 | | |
89 | | /* Forward references */ |
90 | 0 | #define runInit 1 |
91 | 324k | #define runFlush 2 |
92 | | #define runBuffer 4 |
93 | | static int swproc(gs_main_instance *, const char *, arg_list *); |
94 | | static int argproc(gs_main_instance *, const char *); |
95 | | static int run_buffered(gs_main_instance *, const char *); |
96 | | static int esc_strlen(const char *); |
97 | | static void esc_strcat(char *, const char *); |
98 | | static int runarg(gs_main_instance *, const char *, const char *, const char *, int, int, int *, ref *); |
99 | | static int run_string(gs_main_instance *, const char *, int, int, int *, ref *); |
100 | | static int run_finish(gs_main_instance *, int, int, ref *); |
101 | | static int try_stdout_redirect(gs_main_instance * minst, |
102 | | const char *command, const char *filename); |
103 | | |
104 | | /* Forward references for help printout */ |
105 | | static void print_help(gs_main_instance *); |
106 | | static void print_revision(const gs_main_instance *); |
107 | | static void print_version(const gs_main_instance *); |
108 | | static void print_usage(const gs_main_instance *); |
109 | | static void print_devices(const gs_main_instance *); |
110 | | static void print_emulators(const gs_main_instance *); |
111 | | static void print_paths(gs_main_instance *); |
112 | | static void print_help_trailer(const gs_main_instance *); |
113 | | |
114 | | /* ------ Main program ------ */ |
115 | | |
116 | | /* Process the command line with a given instance. */ |
117 | | static stream * |
118 | | gs_main_arg_sopen(const char *fname, void *vminst) |
119 | 0 | { |
120 | 0 | gs_main_set_lib_paths((gs_main_instance *) vminst); |
121 | 0 | return lib_sopen(&((gs_main_instance *)vminst)->lib_path, |
122 | 0 | ((gs_main_instance *)vminst)->heap, fname); |
123 | 0 | } |
124 | | static void |
125 | | set_debug_flags(const char *arg, char *flags) |
126 | 0 | { |
127 | 0 | byte value = (*arg == '-' ? (++arg, 0) : 0xff); |
128 | |
|
129 | 0 | while (*arg) |
130 | 0 | flags[*arg++ & 127] = value; |
131 | 0 | } |
132 | | |
133 | | int |
134 | | gs_main_init_with_args01(gs_main_instance * minst, int argc, char *argv[]) |
135 | 162k | { |
136 | 162k | const char *arg; |
137 | 162k | arg_list args; |
138 | 162k | int code; |
139 | 162k | int have_dumped_args = 0; |
140 | | |
141 | | /* Now we actually process them */ |
142 | 162k | code = arg_init(&args, (const char **)argv, argc, |
143 | 162k | gs_main_arg_sopen, (void *)minst, |
144 | 162k | minst->get_codepoint, |
145 | 162k | minst->heap); |
146 | 162k | if (code < 0) |
147 | 0 | return code; |
148 | 162k | code = gs_main_init0(minst, 0, 0, 0, GS_MAX_LIB_DIRS); |
149 | 162k | if (code < 0) |
150 | 0 | return code; |
151 | | /* This first check is not needed on VMS since GS_LIB evaluates to the same |
152 | | value as that returned by gs_lib_default_path. Also, since GS_LIB is |
153 | | defined as a searchlist logical and getenv only returns the first entry |
154 | | in the searchlist, it really doesn't make sense to search that particular |
155 | | directory twice. |
156 | | */ |
157 | 162k | #ifndef __VMS |
158 | 162k | { |
159 | 162k | int len = 0; |
160 | 162k | int code = gp_getenv(GS_LIB, (char *)0, &len); |
161 | | |
162 | 162k | if (code < 0) { /* key present, value doesn't fit */ |
163 | 0 | char *path = (char *)gs_alloc_bytes(minst->heap, len, "GS_LIB"); |
164 | |
|
165 | 0 | if (path == NULL) |
166 | 0 | return_error(gs_error_VMerror); |
167 | | |
168 | 0 | gp_getenv(GS_LIB, path, &len); /* can't fail */ |
169 | 0 | minst->lib_path.env = path; |
170 | 0 | } |
171 | 162k | } |
172 | 162k | #endif /* __VMS */ |
173 | 162k | minst->lib_path.final = gs_lib_default_path; |
174 | 162k | code = gs_main_set_lib_paths(minst); |
175 | 162k | if (code < 0) |
176 | 0 | goto error; |
177 | | /* Prescan the command line for --help and --version. */ |
178 | 162k | { |
179 | 162k | int i; |
180 | 162k | bool helping = false; |
181 | | |
182 | 2.92M | for (i = 1; i < argc; ++i) { |
183 | 2.76M | if (!arg_strcmp(&args, argv[i], "--")) { |
184 | | /* A PostScript program will be interpreting all the */ |
185 | | /* remaining switches, so stop scanning. */ |
186 | 0 | helping = false; |
187 | 0 | break; |
188 | 2.76M | } else if (!arg_strcmp(&args, argv[i], "--help")) { |
189 | 0 | print_help(minst); |
190 | 0 | helping = true; |
191 | 2.76M | } else if (!arg_strcmp(&args, argv[i], "--debug")) { |
192 | 0 | gs_debug_flags_list(minst->heap); |
193 | 0 | helping = true; |
194 | 2.76M | } else if (!arg_strcmp(&args, argv[i], "--version")) { |
195 | 0 | print_version(minst); |
196 | 0 | puts(minst->heap, ""); /* \n */ |
197 | 0 | helping = true; |
198 | 0 | } |
199 | 2.76M | } |
200 | 162k | if (helping) { |
201 | 0 | code = gs_note_error(gs_error_Info); |
202 | 0 | goto error; |
203 | 0 | } |
204 | 162k | } |
205 | | /* Execute files named in the command line, */ |
206 | | /* processing options along the way. */ |
207 | | /* Wait until the first file name (or the end */ |
208 | | /* of the line) to finish initialization. */ |
209 | 162k | minst->run_start = true; |
210 | | |
211 | 162k | { |
212 | 162k | int len = 0; |
213 | 162k | int code = gp_getenv(GS_OPTIONS, (char *)0, &len); |
214 | | |
215 | 162k | if (code < 0) { /* key present, value doesn't fit */ |
216 | 0 | char *opts = |
217 | 0 | (char *)gs_alloc_bytes(minst->heap, len, "GS_OPTIONS"); |
218 | |
|
219 | 0 | if (opts == NULL) { |
220 | 0 | code = gs_note_error(gs_error_VMerror); |
221 | 0 | goto error; |
222 | 0 | } |
223 | 0 | gp_getenv(GS_OPTIONS, opts, &len); /* can't fail */ |
224 | 0 | if (arg_push_decoded_memory_string(&args, opts, false, true, minst->heap)) { |
225 | 0 | if (opts != NULL) |
226 | 0 | gs_free_object(minst->heap, opts, "error in gs_main_init_with_args"); |
227 | 0 | code = gs_note_error(gs_error_Fatal); |
228 | 0 | goto error; |
229 | 0 | } |
230 | 0 | } |
231 | 162k | } |
232 | 2.88M | while ((code = arg_next(&args, (const char **)&arg, minst->heap)) > 0) { |
233 | 2.76M | code = gs_lib_ctx_stash_sanitized_arg(minst->heap->gs_lib_ctx, arg); |
234 | 2.76M | if (code < 0) |
235 | 0 | goto error; |
236 | 2.76M | switch (*arg) { |
237 | 2.76M | case '-': |
238 | 2.76M | code = swproc(minst, arg, &args); |
239 | 2.76M | if (code < 0) |
240 | 44.1k | goto error; |
241 | 2.71M | if (code > 0) |
242 | 0 | outprintf(minst->heap, "Unknown switch %s - ignoring\n", arg); |
243 | 2.71M | if (gs_debug[':'] && !have_dumped_args) { |
244 | 0 | int i; |
245 | |
|
246 | 0 | if (gs_debug_c(gs_debug_flag_init_details)) |
247 | 0 | dmprintf1(minst->heap, "%% Args passed to instance "PRI_INTPTR": ", |
248 | 0 | (intptr_t)minst); |
249 | 0 | for (i=1; i<argc; i++) |
250 | 0 | dmprintf1(minst->heap, "%s ", argv[i]); |
251 | 0 | dmprintf(minst->heap, "\n"); |
252 | 0 | have_dumped_args = 1; |
253 | 0 | } |
254 | 2.71M | break; |
255 | 0 | default: |
256 | | /* default is to treat this as a file name to be run */ |
257 | 0 | code = argproc(minst, arg); |
258 | 0 | if (code < 0) |
259 | 0 | goto error; |
260 | 0 | if (minst->saved_pages_test_mode) { |
261 | 0 | gx_device *pdev; |
262 | 0 | int ret; |
263 | 0 | gxdso_device_child_request child_dev_data; |
264 | | |
265 | | /* get the real target (printer) device */ |
266 | 0 | pdev = gs_currentdevice(minst->i_ctx_p->pgs); |
267 | 0 | do { |
268 | 0 | child_dev_data.target = pdev; |
269 | 0 | ret = dev_proc(pdev, dev_spec_op)(pdev, gxdso_device_child, &child_dev_data, |
270 | 0 | sizeof(child_dev_data)); |
271 | 0 | if (ret > 0) |
272 | 0 | pdev = child_dev_data.target; |
273 | 0 | } while ((ret > 0) && (child_dev_data.n != 0)); |
274 | 0 | if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev, |
275 | 0 | (byte *)"print normal flush", 18)) < 0) |
276 | 0 | goto error; |
277 | 0 | if (code > 0) |
278 | 0 | if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0) |
279 | 0 | goto error; |
280 | 0 | } |
281 | 2.76M | } |
282 | 2.76M | } |
283 | | |
284 | 118k | return code; |
285 | | |
286 | 44.1k | error: |
287 | 44.1k | if (minst->lib_path.env != NULL) { |
288 | 0 | gs_free_object(minst->heap, (char *)minst->lib_path.env, "error in gs_main_init_with_args"); |
289 | 0 | minst->lib_path.env = NULL; |
290 | 0 | } |
291 | 44.1k | return code; |
292 | 162k | } |
293 | | |
294 | | int |
295 | | gs_main_init_with_args2(gs_main_instance * minst) |
296 | 118k | { |
297 | 118k | int code; |
298 | | |
299 | 118k | code = gs_main_init2(minst); |
300 | 118k | if (code < 0) |
301 | 0 | return code; |
302 | | |
303 | 118k | if (!minst->run_start) |
304 | 118k | return gs_error_Quit; |
305 | 0 | return code; |
306 | 118k | } |
307 | | |
308 | | int |
309 | | gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[]) |
310 | 162k | { |
311 | 162k | int code = gs_main_init_with_args01(minst, argc, argv); |
312 | | |
313 | 162k | if (code < 0) |
314 | 44.1k | return code; |
315 | 118k | return gs_main_init_with_args2(minst); |
316 | 162k | } |
317 | | |
318 | | /* |
319 | | * Specify a decoding function |
320 | | */ |
321 | | void gs_main_inst_arg_decode(gs_main_instance * minst, |
322 | | gs_arg_get_codepoint *get_codepoint) |
323 | 324k | { |
324 | 324k | minst->get_codepoint = get_codepoint; |
325 | 324k | } |
326 | | |
327 | | gs_arg_get_codepoint *gs_main_inst_get_arg_decode(gs_main_instance * minst) |
328 | 0 | { |
329 | 0 | return minst->get_codepoint; |
330 | 0 | } |
331 | | |
332 | | /* |
333 | | * Run the 'start' procedure (after processing the command line). |
334 | | */ |
335 | | int |
336 | | gs_main_run_start(gs_main_instance * minst) |
337 | 0 | { |
338 | 0 | return run_string(minst, "systemdict /start get exec", runFlush, minst->user_errors, NULL, NULL); |
339 | 0 | } |
340 | | |
341 | | static int |
342 | | do_arg_match(const char **arg, const char *match, size_t match_len) |
343 | 0 | { |
344 | 0 | const char *s = *arg; |
345 | 0 | if (strncmp(s, match, match_len) != 0) |
346 | 0 | return 0; |
347 | 0 | s += match_len; |
348 | 0 | if (*s == '=') |
349 | 0 | *arg = ++s; |
350 | 0 | else if (*s != 0) |
351 | 0 | return 0; |
352 | 0 | else |
353 | 0 | *arg = NULL; |
354 | 0 | return 1; |
355 | 0 | } |
356 | | |
357 | 0 | #define arg_match(A, B) do_arg_match(A, B, sizeof(B)-1) |
358 | | |
359 | | /* Process switches. Return 0 if processed, 1 for unknown switch, */ |
360 | | /* <0 if error. */ |
361 | | static int |
362 | | swproc(gs_main_instance * minst, const char *arg, arg_list * pal) |
363 | 2.92M | { |
364 | 2.92M | char sw = arg[1]; |
365 | 2.92M | ref vtrue; |
366 | 2.92M | int code = 0; |
367 | | |
368 | 2.92M | make_true(&vtrue); |
369 | 2.92M | arg += 2; /* skip - and letter */ |
370 | 2.92M | switch (sw) { |
371 | 0 | default: |
372 | 0 | return 1; |
373 | 0 | case 0: /* read stdin as a file char-by-char */ |
374 | | /* This is a ******HACK****** for Ghostview. */ |
375 | 0 | minst->heap->gs_lib_ctx->core->stdin_is_interactive = true; |
376 | 0 | goto run_stdin; |
377 | 162k | case '_': /* read stdin with normal buffering */ |
378 | 162k | minst->heap->gs_lib_ctx->core->stdin_is_interactive = false; |
379 | 162k | run_stdin: |
380 | 162k | minst->run_start = false; /* don't run 'start' */ |
381 | | /* Set NOPAUSE so showpage won't try to read from stdin. */ |
382 | 162k | code = swproc(minst, "-dNOPAUSE", pal); |
383 | 162k | if (code) |
384 | 0 | return code; |
385 | 162k | code = gs_main_init2(minst); /* Finish initialization */ |
386 | 162k | if (code < 0) |
387 | 20 | return code; |
388 | | |
389 | 162k | code = run_string(minst, ".runstdin", runFlush, minst->user_errors, NULL, NULL); |
390 | 162k | if (code < 0) |
391 | 44.1k | return code; |
392 | | /* If in saved_pages_test_mode, print and flush previous job before the next file */ |
393 | 118k | if (minst->saved_pages_test_mode) { |
394 | 0 | gx_device *pdev; |
395 | 0 | int ret; |
396 | 0 | gxdso_device_child_request child_dev_data; |
397 | | |
398 | | /* get the real target (printer) device */ |
399 | 0 | pdev = gs_currentdevice(minst->i_ctx_p->pgs); |
400 | 0 | do { |
401 | 0 | child_dev_data.target = pdev; |
402 | 0 | ret = dev_proc(pdev, dev_spec_op)(pdev, gxdso_device_child, &child_dev_data, |
403 | 0 | sizeof(child_dev_data)); |
404 | 0 | if (ret > 0) |
405 | 0 | pdev = child_dev_data.target; |
406 | 0 | } while ((ret > 0) && (child_dev_data.n != 0)); |
407 | 0 | if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev, |
408 | 0 | (byte *)"print normal flush", 18)) < 0) |
409 | 0 | return code; |
410 | 0 | if (code > 0) |
411 | 0 | if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0) |
412 | 0 | return code; |
413 | 0 | } |
414 | 118k | break; |
415 | 118k | case '-': /* run with command line args */ |
416 | 0 | if (strncmp(arg, "debug=", 6) == 0) { |
417 | 0 | code = gs_debug_flags_parse(minst->heap, arg+6); |
418 | 0 | if (code < 0) |
419 | 0 | return code; |
420 | 0 | break; |
421 | 0 | } else if (strncmp(arg, "saved-pages=", 12) == 0) { |
422 | 0 | gx_device *pdev; |
423 | | |
424 | | /* If init2 not yet done, just save the argument for processing then */ |
425 | 0 | if (minst->init_done < 2) { |
426 | 0 | if (minst->saved_pages_initial_arg == NULL) { |
427 | | /* Tuck the parameters away for later when init2 is done (usually "begin") */ |
428 | 0 | minst->saved_pages_initial_arg = (char *)gs_alloc_bytes(minst->heap, |
429 | 0 | 1+strlen((char *)arg+12), |
430 | 0 | "GS_OPTIONS"); |
431 | 0 | if (minst->saved_pages_initial_arg != NULL) { |
432 | 0 | strcpy(minst->saved_pages_initial_arg,(char *)arg+12); |
433 | 0 | } else { |
434 | 0 | outprintf(minst->heap, |
435 | 0 | " saved_pages_initial_arg larger than expected\n"); |
436 | 0 | arg_finit(pal); |
437 | 0 | return gs_error_Fatal; |
438 | 0 | } |
439 | 0 | } else { |
440 | 0 | outprintf(minst->heap, |
441 | 0 | " Only one --saved-pages=... command allowed before processing input\n"); |
442 | 0 | arg_finit(pal); |
443 | 0 | return gs_error_Fatal; |
444 | 0 | } |
445 | 0 | } else { |
446 | 0 | int ret; |
447 | 0 | gxdso_device_child_request child_dev_data; |
448 | | |
449 | | /* get the current device */ |
450 | 0 | pdev = gs_currentdevice(minst->i_ctx_p->pgs); |
451 | 0 | if (dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_saved_pages, NULL, 0) <= 0) { |
452 | 0 | outprintf(minst->heap, |
453 | 0 | " --saved-pages not supported by the '%s' device.\n", |
454 | 0 | pdev->dname); |
455 | 0 | arg_finit(pal); |
456 | 0 | return gs_error_Fatal; |
457 | 0 | } |
458 | | /* get the real target (printer) device */ |
459 | 0 | do { |
460 | 0 | child_dev_data.target = pdev; |
461 | 0 | ret = dev_proc(pdev, dev_spec_op)(pdev, gxdso_device_child, &child_dev_data, |
462 | 0 | sizeof(child_dev_data)); |
463 | 0 | if (ret > 0) |
464 | 0 | pdev = child_dev_data.target; |
465 | 0 | } while ((ret > 0) && (child_dev_data.n != 0)); |
466 | 0 | if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev, |
467 | 0 | (byte *)arg+12, strlen(arg+12))) < 0) |
468 | 0 | return code; |
469 | 0 | if (code > 0) |
470 | 0 | if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0) |
471 | 0 | return code; |
472 | 0 | } |
473 | 0 | break; |
474 | | /* The following code is only to allow regression testing of saved-pages */ |
475 | 0 | } else if (strncmp(arg, "saved-pages-test", 16) == 0) { |
476 | 0 | minst->saved_pages_test_mode = true; |
477 | 0 | break; |
478 | | /* Now handle the explicitly added paths to the file control lists */ |
479 | 0 | } else if (arg_match(&arg, "permit-file-read")) { |
480 | 0 | code = gs_add_explicit_control_path(minst->heap, arg, gs_permit_file_reading); |
481 | 0 | if (code < 0) return code; |
482 | 0 | break; |
483 | 0 | } else if (arg_match(&arg, "permit-file-write")) { |
484 | 0 | code = gs_add_explicit_control_path(minst->heap, arg, gs_permit_file_writing); |
485 | 0 | if (code < 0) return code; |
486 | 0 | break; |
487 | 0 | } else if (arg_match(&arg, "permit-file-control")) { |
488 | 0 | code = gs_add_explicit_control_path(minst->heap, arg, gs_permit_file_control); |
489 | 0 | if (code < 0) return code; |
490 | 0 | break; |
491 | 0 | } else if (arg_match(&arg, "permit-file-all")) { |
492 | 0 | code = gs_add_explicit_control_path(minst->heap, arg, gs_permit_file_reading); |
493 | 0 | if (code < 0) return code; |
494 | 0 | code = gs_add_explicit_control_path(minst->heap, arg, gs_permit_file_writing); |
495 | 0 | if (code < 0) return code; |
496 | 0 | code = gs_add_explicit_control_path(minst->heap, arg, gs_permit_file_control); |
497 | 0 | if (code < 0) return code; |
498 | 0 | break; |
499 | 0 | } else if (arg_match(&arg, "permit-devices")) { |
500 | 0 | code = gs_add_explicit_permitted_device(minst->heap, arg); |
501 | 0 | if (code < 0) return code; |
502 | 0 | break; |
503 | 0 | } |
504 | 0 | if (*arg != 0) { |
505 | | /* Unmatched switch. */ |
506 | 0 | outprintf(minst->heap, |
507 | 0 | " Unknown switch '--%s'.\n", |
508 | 0 | arg); |
509 | 0 | arg_finit(pal); |
510 | 0 | return gs_error_Fatal; |
511 | 0 | } |
512 | | /* FALLTHROUGH */ |
513 | 0 | case '+': |
514 | 0 | pal->expand_ats = false; |
515 | | /* FALLTHROUGH */ |
516 | 0 | case '@': /* ditto with @-expansion */ |
517 | 0 | { |
518 | 0 | char *psarg; |
519 | |
|
520 | 0 | code = arg_next(pal, (const char **)&psarg, minst->heap); |
521 | | /* Don't stash the @ file name */ |
522 | |
|
523 | 0 | if (code < 0) |
524 | 0 | return gs_error_Fatal; |
525 | 0 | if (psarg == NULL) { |
526 | 0 | outprintf(minst->heap, "Usage: gs ... -%c file.ps arg1 ... argn\n", sw); |
527 | 0 | arg_finit(pal); |
528 | 0 | return gs_error_Fatal; |
529 | 0 | } |
530 | 0 | psarg = arg_copy(psarg, minst->heap); |
531 | 0 | if (psarg == NULL) |
532 | 0 | code = gs_error_Fatal; |
533 | 0 | else |
534 | 0 | code = gs_main_init2(minst); |
535 | 0 | if (code >= 0) |
536 | 0 | code = run_string(minst, "userdict/ARGUMENTS[", 0, minst->user_errors, NULL, NULL); |
537 | 0 | if (code >= 0) |
538 | 0 | while ((code = arg_next(pal, (const char **)&arg, minst->heap)) > 0) { |
539 | 0 | code = gs_lib_ctx_stash_sanitized_arg(minst->heap->gs_lib_ctx, arg); |
540 | 0 | if (code < 0) |
541 | 0 | break; |
542 | 0 | code = runarg(minst, "", arg, "", runInit, minst->user_errors, NULL, NULL); |
543 | 0 | if (code < 0) |
544 | 0 | break; |
545 | 0 | } |
546 | 0 | if (code >= 0) |
547 | 0 | code = run_string(minst, "]put", 0, minst->user_errors, NULL, NULL); |
548 | 0 | if (code >= 0) |
549 | 0 | code = argproc(minst, psarg); |
550 | 0 | arg_free((char *)psarg, minst->heap); |
551 | 0 | if (code >= 0) |
552 | 0 | code = gs_error_Quit; |
553 | |
|
554 | 0 | return code; |
555 | 0 | } |
556 | 0 | case 'B': /* set run_string buffer size */ |
557 | 0 | if (*arg == '-') |
558 | 0 | minst->run_buffer_size = 0; |
559 | 0 | else { |
560 | 0 | uint bsize; |
561 | |
|
562 | 0 | if (sscanf((const char *)arg, "%u", &bsize) != 1 || |
563 | 0 | bsize <= 0 || bsize > MAX_BUFFERED_SIZE |
564 | 0 | ) { |
565 | 0 | outprintf(minst->heap, |
566 | 0 | "-B must be followed by - or size between 1 and %u\n", |
567 | 0 | MAX_BUFFERED_SIZE); |
568 | 0 | return gs_error_Fatal; |
569 | 0 | } |
570 | 0 | minst->run_buffer_size = bsize; |
571 | 0 | } |
572 | 0 | break; |
573 | 0 | case 'c': /* code follows */ |
574 | 0 | { |
575 | 0 | bool ats = pal->expand_ats; |
576 | |
|
577 | 0 | code = gs_main_init2(minst); |
578 | 0 | if (code < 0) |
579 | 0 | return code; |
580 | 0 | pal->expand_ats = false; |
581 | 0 | while ((code = arg_next(pal, (const char **)&arg, minst->heap)) > 0) { |
582 | 0 | if (arg[0] == '@' || |
583 | 0 | (arg[0] == '-' && !isdigit((unsigned char)arg[1])) |
584 | 0 | ) |
585 | 0 | break; |
586 | 0 | code = gs_lib_ctx_stash_sanitized_arg(minst->heap->gs_lib_ctx, "?"); |
587 | 0 | if (code < 0) |
588 | 0 | return code; |
589 | 0 | code = runarg(minst, "", arg, ".runstring", 0, minst->user_errors, NULL, NULL); |
590 | 0 | if (code < 0) |
591 | 0 | return code; |
592 | 0 | } |
593 | 0 | if (code < 0) |
594 | 0 | return gs_error_Fatal; |
595 | 0 | if (arg != NULL) { |
596 | 0 | char *p = arg_copy(arg, minst->heap); |
597 | 0 | if (p == NULL) |
598 | 0 | return gs_error_Fatal; |
599 | 0 | arg_push_string(pal, p, true); |
600 | 0 | } |
601 | 0 | pal->expand_ats = ats; |
602 | 0 | break; |
603 | 0 | } |
604 | 0 | case 'f': /* run file of arbitrary name */ |
605 | 0 | if (*arg != 0) { |
606 | 0 | code = argproc(minst, arg); |
607 | 0 | if (code < 0) |
608 | 0 | return code; |
609 | | /* If in saved_pages_test_mode, print and flush previous job before the next file */ |
610 | 0 | if (minst->saved_pages_test_mode) { |
611 | 0 | gx_device *pdev; |
612 | 0 | int ret; |
613 | 0 | gxdso_device_child_request child_dev_data; |
614 | | |
615 | | /* get the real target (printer) device */ |
616 | 0 | pdev = gs_currentdevice(minst->i_ctx_p->pgs); |
617 | 0 | do { |
618 | 0 | child_dev_data.target = pdev; |
619 | 0 | ret = dev_proc(pdev, dev_spec_op)(pdev, gxdso_device_child, &child_dev_data, |
620 | 0 | sizeof(child_dev_data)); |
621 | 0 | if (ret > 0) |
622 | 0 | pdev = child_dev_data.target; |
623 | 0 | } while ((ret > 0) && (child_dev_data.n != 0)); |
624 | 0 | if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev, |
625 | 0 | (byte *)"print normal flush", 18)) < 0) |
626 | 0 | return code; |
627 | 0 | if (code > 0) |
628 | 0 | if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0) |
629 | 0 | return code; |
630 | 0 | } |
631 | 0 | } |
632 | 0 | break; |
633 | 0 | case 'F': /* run file with buffer_size = 1 */ |
634 | 0 | if (!*arg) { |
635 | 0 | puts(minst->heap, "-F requires a file name"); |
636 | 0 | return gs_error_Fatal; |
637 | 0 | } { |
638 | 0 | uint bsize = minst->run_buffer_size; |
639 | |
|
640 | 0 | minst->run_buffer_size = 1; |
641 | 0 | code = argproc(minst, arg); |
642 | 0 | minst->run_buffer_size = bsize; |
643 | 0 | if (code < 0) |
644 | 0 | return code; |
645 | | /* If in saved_pages_test_mode, print and flush previous job before the next file */ |
646 | 0 | if (minst->saved_pages_test_mode) { |
647 | 0 | gx_device *pdev; |
648 | 0 | int ret; |
649 | 0 | gxdso_device_child_request child_dev_data; |
650 | | |
651 | | /* get the real target (printer) device */ |
652 | 0 | pdev = gs_currentdevice(minst->i_ctx_p->pgs); |
653 | 0 | do { |
654 | 0 | child_dev_data.target = pdev; |
655 | 0 | ret = dev_proc(pdev, dev_spec_op)(pdev, gxdso_device_child, &child_dev_data, |
656 | 0 | sizeof(child_dev_data)); |
657 | 0 | if (ret > 0) |
658 | 0 | pdev = child_dev_data.target; |
659 | 0 | } while ((ret > 0) && (child_dev_data.n != 0)); |
660 | 0 | if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev, |
661 | 0 | (byte *)"print normal flush", 18)) < 0) |
662 | 0 | return code; |
663 | 0 | if (code > 0) |
664 | 0 | if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0) |
665 | 0 | return code; |
666 | 0 | } |
667 | 0 | } |
668 | 0 | break; |
669 | 0 | case 'g': /* define device geometry */ |
670 | 0 | { |
671 | 0 | long dimensions[2]; |
672 | |
|
673 | 0 | if ((code = gs_main_init1(minst)) < 0) |
674 | 0 | return code; |
675 | 0 | if (sscanf((const char *)arg, "%ldx%ld", &dimensions[0], &dimensions[1]) != 2) { |
676 | 0 | puts(minst->heap, "-g must be followed by <width>x<height>"); |
677 | 0 | return gs_error_Fatal; |
678 | 0 | } |
679 | 0 | gs_main_force_dimensions(minst, dimensions); |
680 | 0 | break; |
681 | 0 | } |
682 | 0 | case 'h': /* print help */ |
683 | 0 | case '?': /* ditto */ |
684 | 0 | print_help(minst); |
685 | 0 | return gs_error_Info; /* show usage info on exit */ |
686 | 0 | case 'I': /* specify search path */ |
687 | 0 | { |
688 | 0 | const char *path; |
689 | |
|
690 | 0 | if (arg[0] == 0) { |
691 | 0 | code = arg_next(pal, (const char **)&path, minst->heap); |
692 | 0 | if (code < 0) |
693 | 0 | return code; |
694 | 0 | code = gs_lib_ctx_stash_sanitized_arg(minst->heap->gs_lib_ctx, "?"); |
695 | 0 | if (code < 0) |
696 | 0 | return code; |
697 | 0 | } else |
698 | 0 | path = arg; |
699 | 0 | if (path == NULL) |
700 | 0 | return gs_error_Fatal; |
701 | 0 | gs_main_add_lib_path(minst, path); |
702 | 0 | } |
703 | 0 | break; |
704 | 162k | case 'K': /* set malloc limit */ |
705 | 162k | { |
706 | 162k | long msize = 0; |
707 | 162k | gs_malloc_memory_t *rawheap = gs_malloc_wrapped_contents(minst->heap); |
708 | | |
709 | 162k | (void)sscanf((const char *)arg, "%ld", &msize); |
710 | 162k | if (msize <= 0 || msize > max_long >> 10) { |
711 | 0 | outprintf(minst->heap, "-K<numK> must have 1 <= numK <= %ld\n", |
712 | 0 | max_long >> 10); |
713 | 0 | return gs_error_Fatal; |
714 | 0 | } |
715 | 162k | rawheap->limit = msize << 10; |
716 | 162k | } |
717 | 0 | break; |
718 | 0 | case 'M': /* set memory allocation increment */ |
719 | 0 | { |
720 | 0 | unsigned msize = 0; |
721 | |
|
722 | 0 | (void)sscanf((const char *)arg, "%u", &msize); |
723 | 0 | if (msize <= 0 || msize >= (max_uint >> 10)) { |
724 | 0 | outprintf(minst->heap, "-M must be between 1 and %d\n", (int)((max_uint >> 10) - 1)); |
725 | 0 | return gs_error_Fatal; |
726 | 0 | } |
727 | 0 | minst->memory_clump_size = msize << 10; |
728 | 0 | } |
729 | 0 | break; |
730 | 0 | case 'N': /* set size of name table */ |
731 | 0 | { |
732 | 0 | unsigned int nsize = 0; |
733 | |
|
734 | 0 | (void)sscanf((const char *)arg, "%ud", &nsize); |
735 | 0 | if (nsize < 2 || nsize > (max_uint >> 10)) { |
736 | 0 | outprintf(minst->heap, "-N must be between 2 and %d\n", (int)(max_uint >> 10)); |
737 | 0 | return gs_error_Fatal; |
738 | 0 | } |
739 | 0 | minst->name_table_size = (ulong) nsize << 10; |
740 | 0 | } |
741 | 0 | break; |
742 | 0 | case 'o': /* set output file name and batch mode */ |
743 | 0 | { |
744 | 0 | i_ctx_t *i_ctx_p; |
745 | 0 | uint space; |
746 | 0 | const char *adef; |
747 | 0 | byte *str; |
748 | 0 | ref value; |
749 | 0 | int len; |
750 | |
|
751 | 0 | if ((code = gs_main_init1(minst)) < 0) |
752 | 0 | return code; |
753 | | |
754 | 0 | i_ctx_p = minst->i_ctx_p; |
755 | 0 | space = icurrent_space; |
756 | 0 | if (arg[0] == 0) { |
757 | 0 | code = arg_next(pal, (const char **)&adef, minst->heap); |
758 | 0 | if (code < 0) |
759 | 0 | return code; |
760 | 0 | if (code == 0) |
761 | 0 | return gs_error_undefinedfilename; |
762 | 0 | code = gs_lib_ctx_stash_sanitized_arg(minst->heap->gs_lib_ctx, "?"); |
763 | 0 | if (code < 0) |
764 | 0 | return code; |
765 | 0 | } else |
766 | 0 | adef = arg; |
767 | 0 | if ((code = gs_main_init1(minst)) < 0) |
768 | 0 | return code; |
769 | | |
770 | 0 | if (strlen(adef) > 0) { |
771 | 0 | code = gs_add_outputfile_control_path(minst->heap, adef); |
772 | 0 | if (code < 0) return code; |
773 | 0 | } |
774 | | |
775 | 0 | ialloc_set_space(idmemory, avm_system); |
776 | 0 | len = strlen(adef); |
777 | 0 | str = ialloc_string(len, "-o"); |
778 | 0 | if (str == NULL) |
779 | 0 | return gs_error_VMerror; |
780 | 0 | memcpy(str, adef, len); |
781 | 0 | make_const_string(&value, a_readonly | avm_system, len, str); |
782 | 0 | ialloc_set_space(idmemory, space); |
783 | 0 | i_initial_enter_name(minst->i_ctx_p, "OutputFile", &value); |
784 | 0 | i_initial_enter_name(minst->i_ctx_p, "NOPAUSE", &vtrue); |
785 | 0 | i_initial_enter_name(minst->i_ctx_p, "BATCH", &vtrue); |
786 | 0 | } |
787 | 0 | break; |
788 | 0 | case 'P': /* choose whether search '.' first */ |
789 | 0 | if (!strcmp(arg, "")) |
790 | 0 | minst->search_here_first = true; |
791 | 0 | else if (!strcmp(arg, "-")) |
792 | 0 | minst->search_here_first = false; |
793 | 0 | else { |
794 | 0 | puts(minst->heap, "Only -P or -P- is allowed."); |
795 | 0 | return gs_error_Fatal; |
796 | 0 | } |
797 | 0 | break; |
798 | 0 | case 'q': /* quiet startup */ |
799 | 0 | if ((code = gs_main_init1(minst)) < 0) |
800 | 0 | return code; |
801 | 0 | i_initial_enter_name(minst->i_ctx_p, "QUIET", &vtrue); |
802 | 0 | break; |
803 | 162k | case 'r': /* define device resolution */ |
804 | 162k | { |
805 | 162k | float res[2]; |
806 | | |
807 | 162k | if ((code = gs_main_init1(minst)) < 0) |
808 | 0 | return code; |
809 | 162k | switch (sscanf((const char *)arg, "%fx%f", &res[0], &res[1])) { |
810 | 0 | default: |
811 | 0 | puts(minst->heap, "-r must be followed by <res> or <xres>x<yres>"); |
812 | 0 | return gs_error_Fatal; |
813 | 0 | case 1: /* -r<res> */ |
814 | 0 | res[1] = res[0]; |
815 | | /* fall through */ |
816 | 162k | case 2: /* -r<xres>x<yres> */ |
817 | 162k | gs_main_force_resolutions(minst, res); |
818 | 162k | } |
819 | 162k | break; |
820 | 162k | } |
821 | 162k | case 'D': /* define name */ |
822 | 1.78M | case 'd': |
823 | 1.78M | case 'S': /* define name as string */ |
824 | 2.43M | case 's': |
825 | 2.43M | { |
826 | 2.43M | char *adef = arg_copy(arg, minst->heap); |
827 | 2.43M | char *eqp; |
828 | 2.43M | bool isd = (sw == 'D' || sw == 'd'); |
829 | 2.43M | ref value; |
830 | | |
831 | 2.43M | if (adef == NULL) |
832 | 0 | return gs_error_Fatal; |
833 | 2.43M | eqp = strchr(adef, '='); |
834 | | |
835 | 2.43M | if (eqp == NULL) |
836 | 1.13M | eqp = strchr(adef, '#'); |
837 | | /* Initialize the object memory, scanner, and */ |
838 | | /* name table now if needed. */ |
839 | 2.43M | if ((code = gs_main_init1(minst)) < 0) { |
840 | 0 | arg_free((char *)adef, minst->heap); |
841 | 0 | return code; |
842 | 0 | } |
843 | 2.43M | if (eqp == adef) { |
844 | 0 | puts(minst->heap, "Usage: -dNAME, -dNAME=TOKEN, -sNAME=STRING"); |
845 | 0 | arg_free((char *)adef, minst->heap); |
846 | 0 | return gs_error_Fatal; |
847 | 0 | } |
848 | 2.43M | if (eqp == NULL) { |
849 | 1.13M | if (isd) |
850 | 1.13M | make_true(&value); |
851 | 0 | else |
852 | 1.13M | make_empty_string(&value, a_readonly); |
853 | 1.29M | } else { |
854 | 1.29M | int code; |
855 | 1.29M | i_ctx_t *i_ctx_p = minst->i_ctx_p; |
856 | 1.29M | uint space = icurrent_space; |
857 | | |
858 | 1.29M | *eqp++ = 0; |
859 | | |
860 | 1.29M | if (strlen(adef) == 10 && strncmp(adef, "OutputFile", 10) == 0 && strlen(eqp) > 0) { |
861 | 162k | code = gs_add_outputfile_control_path(minst->heap, eqp); |
862 | 162k | if (code < 0) { |
863 | 0 | arg_free((char *)adef, minst->heap); |
864 | 0 | return code; |
865 | 0 | } |
866 | 162k | } |
867 | | |
868 | 1.29M | ialloc_set_space(idmemory, avm_system); |
869 | 1.29M | if (isd) { |
870 | 649k | int i; |
871 | 649k | int64_t num; |
872 | | |
873 | | /* Check for numbers so we can provide for suffix scalers */ |
874 | | /* Note the check for '#' is for PS "radix" numbers such as 16#ff */ |
875 | | /* and check for '.' and 'e' or 'E' which are 'real' numbers */ |
876 | 649k | if ((strchr(eqp, '#') == NULL) && (strchr(eqp, '.') == NULL) && |
877 | 649k | (strchr(eqp, 'e') == NULL) && (strchr(eqp, 'E') == NULL) && |
878 | 649k | ((i = sscanf((const char *)eqp, "%"PRIi64, &num)) == 1)) { |
879 | 649k | char suffix = eqp[strlen(eqp) - 1]; |
880 | | |
881 | 649k | switch (suffix) { |
882 | 162k | case 'k': |
883 | 162k | case 'K': |
884 | 162k | num *= 1024; |
885 | 162k | break; |
886 | 0 | case 'm': |
887 | 0 | case 'M': |
888 | 0 | num *= 1024 * 1024; |
889 | 0 | break; |
890 | 0 | case 'g': |
891 | 0 | case 'G': |
892 | | /* caveat emptor: more than 2g will overflow */ |
893 | | /* and really should produce a 'real', so don't do this */ |
894 | 0 | num *= 1024 * 1024 * 1024; |
895 | 0 | break; |
896 | 487k | default: |
897 | 487k | break; /* not a valid suffix or last char was digit */ |
898 | 649k | } |
899 | 649k | make_int(&value, (ps_int)num); |
900 | 649k | } else { |
901 | | /* use the PS scanner to capture other valid token types */ |
902 | 0 | stream astream; |
903 | 0 | scanner_state state; |
904 | |
|
905 | 0 | s_init(&astream, NULL); |
906 | 0 | sread_string(&astream, |
907 | 0 | (const byte *)eqp, strlen(eqp)); |
908 | 0 | gs_scanner_init_stream(&state, &astream); |
909 | 0 | code = gs_scan_token(minst->i_ctx_p, &value, &state); |
910 | 0 | if (code) { |
911 | 0 | outprintf(minst->heap, "Invalid value for option -d%s, -dNAME= must be followed by a valid token\n", arg); |
912 | 0 | arg_free((char *)adef, minst->heap); |
913 | 0 | return gs_error_Fatal; |
914 | 0 | } |
915 | 0 | if (r_has_type_attrs(&value, t_name, |
916 | 0 | a_executable)) { |
917 | 0 | ref nsref; |
918 | |
|
919 | 0 | name_string_ref(minst->heap, &value, &nsref); |
920 | 0 | #undef string_is |
921 | 0 | #define string_is(nsref, str, len)\ |
922 | 0 | (r_size(&(nsref)) == (len) &&\ |
923 | 0 | !strncmp((const char *)(nsref).value.const_bytes, str, (len))) |
924 | 0 | if (string_is(nsref, "null", 4)) |
925 | 0 | make_null(&value); |
926 | 0 | else if (string_is(nsref, "true", 4)) |
927 | 0 | make_true(&value); |
928 | 0 | else if (string_is(nsref, "false", 5)) |
929 | 0 | make_false(&value); |
930 | 0 | else { |
931 | 0 | outprintf(minst->heap, "Invalid value for option -d%s, use -sNAME= to define string constants\n", arg); |
932 | 0 | arg_free((char *)adef, minst->heap); |
933 | 0 | return gs_error_Fatal; |
934 | 0 | } |
935 | 0 | } |
936 | 0 | } |
937 | 649k | } else { |
938 | 649k | int len = strlen(eqp); |
939 | 649k | byte *body = ialloc_string(len, "-s"); |
940 | | |
941 | 649k | if (body == NULL) { |
942 | 0 | lprintf("Out of memory!\n"); |
943 | 0 | arg_free((char *)adef, minst->heap); |
944 | 0 | return gs_error_Fatal; |
945 | 0 | } |
946 | 649k | memcpy(body, eqp, len); |
947 | 649k | make_const_string(&value, a_readonly | avm_system, len, body); |
948 | 649k | if ((code = try_stdout_redirect(minst, adef, eqp)) < 0) { |
949 | 0 | arg_free((char *)adef, minst->heap); |
950 | 0 | return code; |
951 | 0 | } |
952 | 649k | } |
953 | 1.29M | ialloc_set_space(idmemory, space); |
954 | 1.29M | } |
955 | | /* Enter the name in systemdict. */ |
956 | 2.43M | i_initial_enter_name_copy(minst->i_ctx_p, adef, &value); |
957 | 2.43M | arg_free((char *)adef, minst->heap); |
958 | 2.43M | break; |
959 | 2.43M | } |
960 | 0 | case 'p': |
961 | 0 | { |
962 | 0 | char *adef = arg_copy(arg, minst->heap); |
963 | 0 | char *eqp; |
964 | |
|
965 | 0 | if (adef == NULL) |
966 | 0 | return gs_error_Fatal; |
967 | 0 | eqp = strchr(adef, '='); |
968 | |
|
969 | 0 | if (eqp == NULL) |
970 | 0 | eqp = strchr(adef, '#'); |
971 | 0 | if (eqp == NULL) { |
972 | 0 | outprintf(minst->heap, "Usage: -pNAME=STRING\n"); |
973 | 0 | arg_free((char *)adef, minst->heap); |
974 | 0 | return gs_error_Fatal; |
975 | 0 | } |
976 | 0 | *eqp++ = 0; |
977 | | /* Slightly uncomfortable calling back up to a higher |
978 | | * level, but we'll live with it. */ |
979 | 0 | code = gsapi_set_param(gs_lib_ctx_get_interp_instance(minst->heap), |
980 | 0 | adef, eqp, gs_spt_parsed); |
981 | 0 | if (code < 0) { |
982 | 0 | arg_free((char *)adef, minst->heap); |
983 | 0 | return code; |
984 | 0 | } |
985 | 0 | break; |
986 | 0 | } |
987 | 0 | case 'u': /* undefine name */ |
988 | 0 | if (!*arg) { |
989 | 0 | puts(minst->heap, "-u requires a name to undefine."); |
990 | 0 | return gs_error_Fatal; |
991 | 0 | } |
992 | 0 | if ((code = gs_main_init1(minst)) < 0) |
993 | 0 | return code; |
994 | 0 | i_initial_remove_name(minst->i_ctx_p, arg); |
995 | 0 | break; |
996 | 0 | case 'v': /* print revision */ |
997 | 0 | print_revision(minst); |
998 | 0 | return gs_error_Info; |
999 | | /*#ifdef DEBUG */ |
1000 | | /* |
1001 | | * Here we provide a place for inserting debugging code that can be |
1002 | | * run in place of the normal interpreter code. |
1003 | | */ |
1004 | 0 | case 'X': |
1005 | 0 | code = gs_main_init2(minst); |
1006 | 0 | if (code < 0) |
1007 | 0 | return code; |
1008 | 0 | { |
1009 | 0 | int xec; /* exit_code */ |
1010 | 0 | ref xeo; /* error_object */ |
1011 | |
|
1012 | 0 | #define start_x()\ |
1013 | 0 | gs_main_run_string_begin(minst, 1, &xec, &xeo) |
1014 | 0 | #define run_x(str)\ |
1015 | 0 | gs_main_run_string_continue(minst, str, strlen(str), 1, &xec, &xeo) |
1016 | 0 | #define stop_x()\ |
1017 | 0 | gs_main_run_string_end(minst, 1, &xec, &xeo) |
1018 | 0 | start_x(); |
1019 | 0 | run_x("\216\003abc"); |
1020 | 0 | run_x("== flush\n"); |
1021 | 0 | stop_x(); |
1022 | 0 | } |
1023 | 0 | return gs_error_Quit; |
1024 | | /*#endif */ |
1025 | 0 | case 'Z': |
1026 | 0 | set_debug_flags(arg, gs_debug); |
1027 | 0 | break; |
1028 | 2.92M | } |
1029 | 2.88M | return 0; |
1030 | 2.92M | } |
1031 | | |
1032 | | /* Define versions of strlen and strcat that encode strings in hex. */ |
1033 | | /* This is so we can enter escaped characters regardless of whether */ |
1034 | | /* the Level 1 convention of ignoring \s in strings-within-strings */ |
1035 | | /* is being observed (sigh). */ |
1036 | | static int |
1037 | | esc_strlen(const char *str) |
1038 | 0 | { |
1039 | 0 | return strlen(str) * 2 + 2; |
1040 | 0 | } |
1041 | | static void |
1042 | | esc_strcat(char *dest, const char *src) |
1043 | 0 | { |
1044 | 0 | char *d = dest + strlen(dest); |
1045 | 0 | const char *p; |
1046 | 0 | static const char *const hex = "0123456789abcdef"; |
1047 | |
|
1048 | 0 | *d++ = '<'; |
1049 | 0 | for (p = src; *p; p++) { |
1050 | 0 | byte c = (byte) * p; |
1051 | |
|
1052 | 0 | *d++ = hex[c >> 4]; |
1053 | 0 | *d++ = hex[c & 0xf]; |
1054 | 0 | } |
1055 | 0 | *d++ = '>'; |
1056 | 0 | *d = 0; |
1057 | 0 | } |
1058 | | |
1059 | | /* Process file names */ |
1060 | | static int |
1061 | | argproc(gs_main_instance * minst, const char *arg) |
1062 | 0 | { |
1063 | 0 | int code1, code = gs_main_init1(minst); /* need i_ctx_p to proceed */ |
1064 | |
|
1065 | 0 | if (code < 0) |
1066 | 0 | return code; |
1067 | | |
1068 | 0 | code = gs_add_control_path(minst->heap, gs_permit_file_reading, arg); |
1069 | 0 | if (code < 0) return code; |
1070 | | |
1071 | 0 | if (minst->run_buffer_size) { |
1072 | | /* Run file with run_string. */ |
1073 | 0 | code = run_buffered(minst, arg); |
1074 | 0 | } else { |
1075 | | /* Run file directly in the normal way. */ |
1076 | 0 | code = runarg(minst, "", arg, ".runfile", runInit | runFlush, minst->user_errors, NULL, NULL); |
1077 | 0 | } |
1078 | |
|
1079 | 0 | code1 = gs_remove_control_path(minst->heap, gs_permit_file_reading, arg); |
1080 | 0 | if (code >= 0 && code1 < 0) code = code1; |
1081 | |
|
1082 | 0 | return code; |
1083 | 0 | } |
1084 | | static int |
1085 | | run_buffered(gs_main_instance * minst, const char *arg) |
1086 | 0 | { |
1087 | 0 | gp_file *in = gp_fopen(minst->heap, arg, gp_fmode_rb); |
1088 | 0 | int exit_code; |
1089 | 0 | ref error_object; |
1090 | 0 | int code; |
1091 | |
|
1092 | 0 | if (in == 0) { |
1093 | 0 | outprintf(minst->heap, "Unable to open %s for reading", arg); |
1094 | 0 | return_error(gs_error_invalidfileaccess); |
1095 | 0 | } |
1096 | 0 | code = gs_main_init2(minst); |
1097 | 0 | if (code < 0) { |
1098 | 0 | gp_fclose(in); |
1099 | 0 | return code; |
1100 | 0 | } |
1101 | 0 | code = gs_main_run_string_begin(minst, minst->user_errors, |
1102 | 0 | &exit_code, &error_object); |
1103 | 0 | if (!code) { |
1104 | 0 | char buf[MAX_BUFFERED_SIZE]; |
1105 | 0 | int count; |
1106 | |
|
1107 | 0 | code = gs_error_NeedInput; |
1108 | 0 | while ((count = gp_fread(buf, 1, minst->run_buffer_size, in)) > 0) { |
1109 | 0 | code = gs_main_run_string_continue(minst, buf, count, |
1110 | 0 | minst->user_errors, |
1111 | 0 | &exit_code, &error_object); |
1112 | 0 | if (code != gs_error_NeedInput) |
1113 | 0 | break; |
1114 | 0 | } |
1115 | 0 | if (code == gs_error_NeedInput) { |
1116 | 0 | code = gs_main_run_string_end(minst, minst->user_errors, |
1117 | 0 | &exit_code, &error_object); |
1118 | 0 | } |
1119 | 0 | } |
1120 | 0 | gp_fclose(in); |
1121 | 0 | zflush(minst->i_ctx_p); |
1122 | 0 | zflushpage(minst->i_ctx_p); |
1123 | 0 | return run_finish(minst, code, exit_code, &error_object); |
1124 | 0 | } |
1125 | | static int |
1126 | | runarg(gs_main_instance *minst, |
1127 | | const char *pre, |
1128 | | const char *arg, |
1129 | | const char *post, |
1130 | | int options, |
1131 | | int user_errors, |
1132 | | int *pexit_code, |
1133 | | ref *perror_object) |
1134 | 0 | { |
1135 | 0 | int len = strlen(pre) + esc_strlen(arg) + strlen(post) + 1; |
1136 | 0 | int code; |
1137 | 0 | char *line; |
1138 | |
|
1139 | 0 | if (options & runInit) { |
1140 | 0 | code = gs_main_init2(minst); /* Finish initialization */ |
1141 | |
|
1142 | 0 | if (code < 0) |
1143 | 0 | return code; |
1144 | 0 | } |
1145 | 0 | line = (char *)gs_alloc_bytes(minst->heap, len, "runarg"); |
1146 | 0 | if (line == 0) { |
1147 | 0 | lprintf("Out of memory!\n"); |
1148 | 0 | return_error(gs_error_VMerror); |
1149 | 0 | } |
1150 | 0 | strcpy(line, pre); |
1151 | 0 | esc_strcat(line, arg); |
1152 | 0 | strcat(line, post); |
1153 | 0 | minst->i_ctx_p->starting_arg_file = true; |
1154 | 0 | code = run_string(minst, line, options, user_errors, pexit_code, perror_object); |
1155 | 0 | minst->i_ctx_p->starting_arg_file = false; |
1156 | 0 | gs_free_object(minst->heap, line, "runarg"); |
1157 | 0 | return code; |
1158 | 0 | } |
1159 | | int |
1160 | | gs_main_run_file2(gs_main_instance *minst, |
1161 | | const char *filename, |
1162 | | int user_errors, |
1163 | | int *pexit_code, |
1164 | | ref *perror_object) |
1165 | 0 | { |
1166 | 0 | int code, code1; |
1167 | |
|
1168 | 0 | code = gs_add_control_path(minst->heap, gs_permit_file_reading, filename); |
1169 | 0 | if (code < 0) return code; |
1170 | | |
1171 | 0 | code = runarg(minst, "", filename, ".runfile", runFlush, user_errors, pexit_code, perror_object); |
1172 | |
|
1173 | 0 | code1 = gs_remove_control_path(minst->heap, gs_permit_file_reading, filename); |
1174 | 0 | if (code >= 0 && code1 < 0) code = code1; |
1175 | |
|
1176 | 0 | return code; |
1177 | 0 | } |
1178 | | static int |
1179 | | run_string(gs_main_instance *minst, |
1180 | | const char *str, |
1181 | | int options, |
1182 | | int user_errors, |
1183 | | int *pexit_code, |
1184 | | ref *perror_object) |
1185 | 162k | { |
1186 | 162k | int exit_code; |
1187 | 162k | ref error_object; |
1188 | 162k | int code; |
1189 | | |
1190 | 162k | if (pexit_code == NULL) |
1191 | 162k | pexit_code = &exit_code; |
1192 | 162k | if (perror_object == NULL) |
1193 | 162k | perror_object = &error_object; |
1194 | | |
1195 | 162k | code = gs_main_run_string(minst, str, user_errors, |
1196 | 162k | pexit_code, perror_object); |
1197 | | |
1198 | 162k | if ((options & runFlush) || code != 0) { |
1199 | 162k | zflush(minst->i_ctx_p); /* flush stdout */ |
1200 | 162k | zflushpage(minst->i_ctx_p); /* force display update */ |
1201 | 162k | } |
1202 | 162k | return run_finish(minst, code, *pexit_code, perror_object); |
1203 | 162k | } |
1204 | | static int |
1205 | | run_finish(gs_main_instance *minst, int code, int exit_code, |
1206 | | ref * perror_object) |
1207 | 162k | { |
1208 | 162k | switch (code) { |
1209 | 16 | case gs_error_Quit: |
1210 | 118k | case 0: |
1211 | 118k | break; |
1212 | 44.0k | case gs_error_Fatal: |
1213 | 44.0k | if (exit_code == gs_error_InterpreterExit) |
1214 | 0 | code = exit_code; |
1215 | 44.0k | else |
1216 | 44.0k | emprintf1(minst->heap, |
1217 | 44.0k | "Unrecoverable error, exit code %d\n", |
1218 | 44.0k | exit_code); |
1219 | 44.0k | break; |
1220 | 0 | default: |
1221 | 0 | gs_main_dump_stack(minst, code, perror_object); |
1222 | 162k | } |
1223 | 162k | return code; |
1224 | 162k | } |
1225 | | |
1226 | | /* Redirect stdout to a file: |
1227 | | * -sstdout=filename |
1228 | | * -sstdout=- |
1229 | | * -sstdout=%stdout |
1230 | | * -sstdout=%stderr |
1231 | | * -sOutputFile=- is not affected. |
1232 | | * File is closed at program exit (if not stdout/err) |
1233 | | * or when -sstdout is used again. |
1234 | | */ |
1235 | | static int |
1236 | | try_stdout_redirect(gs_main_instance * minst, |
1237 | | const char *command, const char *filename) |
1238 | 649k | { |
1239 | 649k | gs_lib_ctx_core_t *core = minst->heap->gs_lib_ctx->core; |
1240 | 649k | if (strcmp(command, "stdout") == 0) { |
1241 | 162k | core->stdout_to_stderr = 0; |
1242 | 162k | core->stdout_is_redirected = 0; |
1243 | | /* If stdout already being redirected and it is not stdout |
1244 | | * or stderr, close it |
1245 | | */ |
1246 | 162k | if (core->fstdout2 |
1247 | 162k | && (gp_get_file(core->fstdout2) != core->fstdout) |
1248 | 162k | && (gp_get_file(core->fstdout2) != core->fstderr)) { |
1249 | 0 | gp_fclose(core->fstdout2); |
1250 | 0 | core->fstdout2 = NULL; |
1251 | 0 | } |
1252 | | /* If stdout is being redirected, set minst->fstdout2 */ |
1253 | 162k | if ( (filename != 0) && strlen(filename) && |
1254 | 162k | strcmp(filename, "-") && strcmp(filename, "%stdout") ) { |
1255 | 162k | if (strcmp(filename, "%stderr") == 0) { |
1256 | 0 | core->stdout_to_stderr = 1; |
1257 | 162k | } else { |
1258 | 162k | core->fstdout2 = gp_fopen(minst->heap, filename, "w"); |
1259 | 162k | if (core->fstdout2 == NULL) |
1260 | 0 | return_error(gs_error_invalidfileaccess); |
1261 | 162k | } |
1262 | 162k | core->stdout_is_redirected = 1; |
1263 | 162k | } |
1264 | 162k | return 0; |
1265 | 162k | } |
1266 | 487k | return 1; |
1267 | 649k | } |
1268 | | |
1269 | | /* ---------------- Print information ---------------- */ |
1270 | | |
1271 | | /* |
1272 | | * Help strings. We have to break them up into parts, because |
1273 | | * the Watcom compiler has a limit of 510 characters for a single token. |
1274 | | * For PC displays, we want to limit the strings to 24 lines. |
1275 | | */ |
1276 | | static const char help_usage1[] = "\ |
1277 | | Usage: gs [switches] [file1.ps file2.ps ...]\n\ |
1278 | | Most frequently used switches: (you can use # in place of =)\n\ |
1279 | | -dNOPAUSE no pause after page | -q `quiet', fewer messages\n\ |
1280 | | -g<width>x<height> page size in pixels | -r<res> pixels/inch resolution\n"; |
1281 | | static const char help_usage2[] = "\ |
1282 | | -sDEVICE=<devname> select device | -dBATCH exit after last file\n\ |
1283 | | -sOutputFile=<file> select output file: - for stdout, |command for pipe,\n\ |
1284 | | embed %d or %ld for page #\n"; |
1285 | | #ifdef DEBUG |
1286 | | static const char help_debug[] = "\ |
1287 | | --debug=<option>[,<option>]* select debugging options\n\ |
1288 | | --debug list debugging options\n"; |
1289 | | #endif |
1290 | | static const char help_trailer[] = "\ |
1291 | | For more information, see %s\n\ |
1292 | | Please report bugs to bugs.ghostscript.com.\n"; |
1293 | | static const char help_devices[] = "Available devices:"; |
1294 | | static const char help_default_device[] = "Default output device:"; |
1295 | | static const char help_emulators[] = "Input formats:"; |
1296 | | static const char help_paths[] = "Search path:"; |
1297 | | #ifdef HAVE_FONTCONFIG |
1298 | | static const char help_fontconfig[] = "Ghostscript is also using fontconfig to search for font files\n"; |
1299 | | #else |
1300 | | static const char help_fontconfig[] = ""; |
1301 | | #endif |
1302 | | |
1303 | | extern_gx_io_device_table(); |
1304 | | |
1305 | | /* Print the standard help message. */ |
1306 | | static void |
1307 | | print_help(gs_main_instance * minst) |
1308 | 0 | { |
1309 | 0 | int i, have_rom_device = 0; |
1310 | |
|
1311 | 0 | print_revision(minst); |
1312 | 0 | print_usage(minst); |
1313 | 0 | print_emulators(minst); |
1314 | 0 | print_devices(minst); |
1315 | 0 | print_paths(minst); |
1316 | | /* Check if we have the %rom device */ |
1317 | 0 | for (i = 0; i < gx_io_device_table_count; i++) { |
1318 | 0 | const gx_io_device *iodev = gx_io_device_table[i]; |
1319 | 0 | const char *dname = iodev->dname; |
1320 | |
|
1321 | 0 | if (dname && strlen(dname) == 5 && !memcmp("%rom%", dname, 5)) { |
1322 | 0 | struct stat pstat; |
1323 | | /* gs_error_unregistered means no usable romfs is available */ |
1324 | 0 | int code = iodev->procs.file_status((gx_io_device *)iodev, dname, &pstat); |
1325 | 0 | if (code != gs_error_unregistered){ |
1326 | 0 | have_rom_device = 1; |
1327 | 0 | } |
1328 | 0 | break; |
1329 | 0 | } |
1330 | 0 | } |
1331 | 0 | if (have_rom_device) { |
1332 | 0 | outprintf(minst->heap, "Initialization files are compiled into the executable.\n"); |
1333 | 0 | } |
1334 | 0 | print_help_trailer(minst); |
1335 | 0 | } |
1336 | | |
1337 | | /* Print the revision, revision date, and copyright. */ |
1338 | | static void |
1339 | | print_revision(const gs_main_instance *minst) |
1340 | 0 | { |
1341 | 0 | printf_program_ident(minst->heap, gs_product, gs_revision); |
1342 | 0 | outprintf(minst->heap, " (%d-%02d-%02d)\n%s\n", |
1343 | 0 | (int)(gs_revisiondate / 10000), |
1344 | 0 | (int)(gs_revisiondate / 100 % 100), |
1345 | 0 | (int)(gs_revisiondate % 100), |
1346 | 0 | gs_copyright); |
1347 | 0 | } |
1348 | | |
1349 | | /* Print the version number. */ |
1350 | | static void |
1351 | | print_version(const gs_main_instance *minst) |
1352 | 0 | { |
1353 | 0 | printf_program_ident(minst->heap, NULL, gs_revision); |
1354 | 0 | } |
1355 | | |
1356 | | /* Print usage information. */ |
1357 | | static void |
1358 | | print_usage(const gs_main_instance *minst) |
1359 | 0 | { |
1360 | 0 | outprintf(minst->heap, "%s", help_usage1); |
1361 | 0 | outprintf(minst->heap, "%s", help_usage2); |
1362 | | #ifdef DEBUG |
1363 | | outprintf(minst->heap, "%s", help_debug); |
1364 | | #endif |
1365 | 0 | } |
1366 | | |
1367 | | /* compare function for qsort */ |
1368 | | static int |
1369 | | cmpstr(const void *v1, const void *v2) |
1370 | 0 | { |
1371 | 0 | return strcmp( *(char * const *)v1, *(char * const *)v2 ); |
1372 | 0 | } |
1373 | | |
1374 | | /* Print the list of available devices. */ |
1375 | | static void |
1376 | | print_devices(const gs_main_instance *minst) |
1377 | 0 | { |
1378 | 0 | outprintf(minst->heap, "%s", help_default_device); |
1379 | 0 | outprintf(minst->heap, " %s\n", gs_devicename(gs_getdefaultdevice())); |
1380 | 0 | outprintf(minst->heap, "%s", help_devices); |
1381 | 0 | { |
1382 | 0 | int i; |
1383 | 0 | int pos = 100; |
1384 | 0 | const gx_device *pdev; |
1385 | 0 | const char **names; |
1386 | 0 | size_t ndev = 0; |
1387 | |
|
1388 | 0 | for (i = 0; gs_getdevice(i) != 0; i++) |
1389 | 0 | ; |
1390 | 0 | ndev = (size_t)i; |
1391 | 0 | names = (const char **)gs_alloc_bytes(minst->heap, ndev * sizeof(const char*), "print_devices"); |
1392 | 0 | if (names == (const char **)NULL) { /* old-style unsorted device list */ |
1393 | 0 | for (i = 0; (pdev = gs_getdevice(i)) != 0; i++) { |
1394 | 0 | const char *dname = gs_devicename(pdev); |
1395 | 0 | int len = strlen(dname); |
1396 | |
|
1397 | 0 | if (pos + 1 + len > 76) |
1398 | 0 | outprintf(minst->heap, "\n "), pos = 2; |
1399 | 0 | outprintf(minst->heap, " %s", dname); |
1400 | 0 | pos += 1 + len; |
1401 | 0 | } |
1402 | 0 | } |
1403 | 0 | else { /* new-style sorted device list */ |
1404 | 0 | for (i = 0; (pdev = gs_getdevice(i)) != 0; i++) |
1405 | 0 | names[i] = gs_devicename(pdev); |
1406 | 0 | qsort((void*)names, ndev, sizeof(const char*), cmpstr); |
1407 | 0 | for (i = 0; i < ndev; i++) { |
1408 | 0 | int len = strlen(names[i]); |
1409 | |
|
1410 | 0 | if (pos + 1 + len > 76) |
1411 | 0 | outprintf(minst->heap, "\n "), pos = 2; |
1412 | 0 | outprintf(minst->heap, " %s", names[i]); |
1413 | 0 | pos += 1 + len; |
1414 | 0 | } |
1415 | 0 | gs_free(minst->heap, (char *)names, ndev * sizeof(const char*), 1, "print_devices"); |
1416 | 0 | } |
1417 | 0 | } |
1418 | 0 | outprintf(minst->heap, "\n"); |
1419 | 0 | } |
1420 | | |
1421 | | /* Print the list of language emulators. */ |
1422 | | static void |
1423 | | print_emulators(const gs_main_instance *minst) |
1424 | 0 | { |
1425 | 0 | outprintf(minst->heap, "%s", help_emulators); |
1426 | 0 | { |
1427 | 0 | const byte *s; |
1428 | |
|
1429 | 0 | for (s = gs_emulators; s[0] != 0; s += strlen((const char *)s) + 1) |
1430 | 0 | outprintf(minst->heap, " %s", s); |
1431 | 0 | } |
1432 | 0 | outprintf(minst->heap, "\n"); |
1433 | 0 | } |
1434 | | |
1435 | | /* Print the search paths. */ |
1436 | | static void |
1437 | | print_paths(gs_main_instance * minst) |
1438 | 0 | { |
1439 | 0 | outprintf(minst->heap, "%s", help_paths); |
1440 | 0 | gs_main_set_lib_paths(minst); |
1441 | 0 | { |
1442 | 0 | uint count = r_size(&minst->lib_path.list); |
1443 | 0 | uint i; |
1444 | 0 | int pos = 100; |
1445 | 0 | char fsepr[3]; |
1446 | |
|
1447 | 0 | fsepr[0] = ' ', fsepr[1] = gp_file_name_list_separator, |
1448 | 0 | fsepr[2] = 0; |
1449 | 0 | for (i = 0; i < count; ++i) { |
1450 | 0 | const ref *prdir = |
1451 | 0 | minst->lib_path.list.value.refs + i; |
1452 | 0 | uint len = r_size(prdir); |
1453 | 0 | const char *sepr = (i == count - 1 ? "" : fsepr); |
1454 | |
|
1455 | 0 | if (1 + pos + strlen(sepr) + len > 76) |
1456 | 0 | outprintf(minst->heap, "\n "), pos = 2; |
1457 | 0 | outprintf(minst->heap, " "); |
1458 | | /* |
1459 | | * This is really ugly, but it's necessary because some |
1460 | | * platforms rely on all console output being funneled through |
1461 | | * outprintf. We wish we could just do: |
1462 | | fwrite(prdir->value.bytes, 1, len, minst->fstdout); |
1463 | | */ |
1464 | 0 | { |
1465 | 0 | const char *p = (const char *)prdir->value.bytes; |
1466 | 0 | uint j; |
1467 | |
|
1468 | 0 | for (j = len; j; j--) |
1469 | 0 | outprintf(minst->heap, "%c", *p++); |
1470 | 0 | } |
1471 | 0 | outprintf(minst->heap, "%s", sepr); |
1472 | 0 | pos += 1 + len + strlen(sepr); |
1473 | 0 | } |
1474 | 0 | } |
1475 | 0 | outprintf(minst->heap, "\n"); |
1476 | 0 | outprintf(minst->heap, "%s", help_fontconfig); |
1477 | 0 | } |
1478 | | |
1479 | | /* Print the help trailer. */ |
1480 | | static void |
1481 | | print_help_trailer(const gs_main_instance *minst) |
1482 | 0 | { |
1483 | 0 | char buffer[gp_file_name_sizeof]; |
1484 | 0 | const char *use_htm = "Use.html", *p = buffer; |
1485 | 0 | const char *rtd_url = "https://ghostscript.readthedocs.io/en"; |
1486 | 0 | const char *latest_ver="latest"; |
1487 | 0 | const char *vers = latest_ver; |
1488 | 0 | const char *gs_str = "gs"; |
1489 | 0 | const char *gs = ""; |
1490 | 0 | uint blen = sizeof(buffer); |
1491 | |
|
1492 | 0 | if (strlen(GS_PRODUCT) == strlen(GS_PRODUCTFAMILY)) { |
1493 | 0 | vers = GS_STRINGIZE(GS_DOT_VERSION); |
1494 | 0 | gs = gs_str; |
1495 | 0 | } |
1496 | |
|
1497 | 0 | snprintf(buffer, blen, "%s/%s%s/%s", rtd_url, gs, vers, use_htm); |
1498 | 0 | outprintf(minst->heap, help_trailer, p); |
1499 | 0 | } |