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