1 /* Copyright (C) 1996-2003 artofcode LLC. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: imainarg.c,v 1.34 2004/11/30 20:31:54 ghostgum Exp $ */
18 /* Command line parsing and dispatching */
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 "gxdevice.h"
31 #include "gxdevmem.h"
32 #include "gsdevice.h"
33 #include "stream.h"
34 #include "ierrors.h"
35 #include "estack.h"
36 #include "ialloc.h"
37 #include "strimpl.h" /* for sfilter.h */
38 #include "sfilter.h" /* for iscan.h */
39 #include "ostack.h" /* must precede iscan.h */
40 #include "iscan.h"
41 #include "iconf.h"
42 #include "imain.h"
43 #include "imainarg.h"
44 #include "iapi.h"
45 #include "iminst.h"
46 #include "iname.h"
47 #include "store.h"
48 #include "files.h" /* requires stream.h */
49 #include "interp.h"
50 #include "iutil.h"
51 #include "ivmspace.h"
52 #include "vdtrace.h"
53
54 /* Import operator procedures */
55 extern int zflush(i_ctx_t *);
56 extern int zflushpage(i_ctx_t *);
57
58 #ifndef GS_LIB
59 # define GS_LIB "GS_LIB"
60 #endif
61
62 #ifndef GS_OPTIONS
63 # define GS_OPTIONS "GS_OPTIONS"
64 #endif
65
66 #ifndef GS_MAX_LIB_DIRS
67 # define GS_MAX_LIB_DIRS 25
68 #endif
69
70 #ifndef GS_BUG_MAILBOX
71 # define GS_BUG_MAILBOX "bug-gs@ghostscript.com"
72 #endif
73
74 #define MAX_BUFFERED_SIZE 1024
75
76 /* Note: sscanf incorrectly defines its first argument as char * */
77 /* rather than const char *. This accounts for the ugly casts below. */
78
79 /* Redefine puts to use outprintf, */
80 /* so it will work even without stdio. */
81 #undef puts
82 #define puts(mem, str) outprintf(mem, "%s\n", str)
83
84 /* Forward references */
85 #define runInit 1
86 #define runFlush 2
87 #define runBuffer 4
88 private int swproc(gs_main_instance *, const char *, arg_list *);
89 private int argproc(gs_main_instance *, const char *);
90 private int run_buffered(gs_main_instance *, const char *);
91 private int esc_strlen(const char *);
92 private void esc_strcat(char *, const char *);
93 private int runarg(gs_main_instance *, const char *, const char *, const char *, int);
94 private int run_string(gs_main_instance *, const char *, int);
95 private int run_finish(gs_main_instance *, int, int, ref *);
96 private int try_stdout_redirect(gs_main_instance * minst,
97 const char *command, const char *filename);
98
99 /* Forward references for help printout */
100 private void print_help(gs_main_instance *);
101 private void print_revision(const gs_main_instance *);
102 private void print_version(const gs_main_instance *);
103 private void print_usage(const gs_main_instance *);
104 private void print_devices(const gs_main_instance *);
105 private void print_emulators(const gs_main_instance *);
106 private void print_paths(gs_main_instance *);
107 private void print_help_trailer(const gs_main_instance *);
108
109 /* ------ Main program ------ */
110
111 /* Process the command line with a given instance. */
112 private FILE *
gs_main_arg_fopen(const char * fname,void * vminst)113 gs_main_arg_fopen(const char *fname, void *vminst)
114 {
115 gs_main_set_lib_paths((gs_main_instance *) vminst);
116 return lib_fopen(&((gs_main_instance *)vminst)->lib_path,
117 ((gs_main_instance *)vminst)->heap, fname);
118 }
119 private void
set_debug_flags(const char * arg,char * flags)120 set_debug_flags(const char *arg, char *flags)
121 {
122 byte value = (*arg == '-' ? (++arg, 0) : 0xff);
123
124 while (*arg)
125 flags[*arg++ & 127] = value;
126 }
127
128 int
gs_main_init_with_args(gs_main_instance * minst,int argc,char * argv[])129 gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[])
130 {
131 const char *arg;
132 arg_list args;
133 int code;
134
135 arg_init(&args, (const char **)argv, argc,
136 gs_main_arg_fopen, (void *)minst);
137 code = gs_main_init0(minst, 0, 0, 0, GS_MAX_LIB_DIRS);
138 if (code < 0)
139 return code;
140 /* This first check is not needed on VMS since GS_LIB evaluates to the same
141 value as that returned by gs_lib_default_path. Also, since GS_LIB is
142 defined as a searchlist logical and getenv only returns the first entry
143 in the searchlist, it really doesn't make sense to search that particular
144 directory twice.
145 */
146 #ifndef __VMS
147 {
148 int len = 0;
149 int code = gp_getenv(GS_LIB, (char *)0, &len);
150
151 if (code < 0) { /* key present, value doesn't fit */
152 char *path = (char *)gs_alloc_bytes(minst->heap, len, "GS_LIB");
153
154 gp_getenv(GS_LIB, path, &len); /* can't fail */
155 minst->lib_path.env = path;
156 }
157 }
158 #endif /* __VMS */
159 minst->lib_path.final = gs_lib_default_path;
160 code = gs_main_set_lib_paths(minst);
161 if (code < 0)
162 return code;
163 /* Prescan the command line for --help and --version. */
164 {
165 int i;
166 bool helping = false;
167
168 for (i = 1; i < argc; ++i)
169 if (!strcmp(argv[i], "--")) {
170 /* A PostScript program will be interpreting all the */
171 /* remaining switches, so stop scanning. */
172 helping = false;
173 break;
174 } else if (!strcmp(argv[i], "--help")) {
175 print_help(minst);
176 helping = true;
177 } else if (!strcmp(argv[i], "--version")) {
178 print_version(minst);
179 puts(minst->heap, ""); /* \n */
180 helping = true;
181 }
182 if (helping)
183 return e_Info;
184 }
185 /* Execute files named in the command line, */
186 /* processing options along the way. */
187 /* Wait until the first file name (or the end */
188 /* of the line) to finish initialization. */
189 minst->run_start = true;
190
191 {
192 int len = 0;
193 int code = gp_getenv(GS_OPTIONS, (char *)0, &len);
194
195 if (code < 0) { /* key present, value doesn't fit */
196 char *opts =
197 (char *)gs_alloc_bytes(minst->heap, len, "GS_OPTIONS");
198
199 gp_getenv(GS_OPTIONS, opts, &len); /* can't fail */
200 if (arg_push_memory_string(&args, opts, minst->heap))
201 return e_Fatal;
202 }
203 }
204 while ((arg = arg_next(&args, &code)) != 0) {
205 switch (*arg) {
206 case '-':
207 code = swproc(minst, arg, &args);
208 if (code < 0)
209 return code;
210 if (code > 0)
211 outprintf(minst->heap, "Unknown switch %s - ignoring\n", arg);
212 break;
213 default:
214 code = argproc(minst, arg);
215 if (code < 0)
216 return code;
217 }
218 }
219 if (code < 0)
220 return code;
221
222
223 code = gs_main_init2(minst);
224 if (code < 0)
225 return code;
226
227 if (!minst->run_start)
228 return e_Quit;
229 return code ;
230 }
231
232 /*
233 * Run the 'start' procedure (after processing the command line).
234 */
235 int
gs_main_run_start(gs_main_instance * minst)236 gs_main_run_start(gs_main_instance * minst)
237 {
238 return run_string(minst, "systemdict /start get exec", runFlush);
239 }
240
241 /* Process switches. Return 0 if processed, 1 for unknown switch, */
242 /* <0 if error. */
243 private int
swproc(gs_main_instance * minst,const char * arg,arg_list * pal)244 swproc(gs_main_instance * minst, const char *arg, arg_list * pal)
245 {
246 char sw = arg[1];
247 ref vtrue;
248 int code = 0;
249 #undef initial_enter_name
250 #define initial_enter_name(nstr, pvalue)\
251 i_initial_enter_name(minst->i_ctx_p, nstr, pvalue)
252
253 make_true(&vtrue);
254 arg += 2; /* skip - and letter */
255 switch (sw) {
256 default:
257 return 1;
258 case 0: /* read stdin as a file char-by-char */
259 /* This is a ******HACK****** for Ghostview. */
260 minst->heap->gs_lib_ctx->stdin_is_interactive = true;
261 goto run_stdin;
262 case '_': /* read stdin with normal buffering */
263 minst->heap->gs_lib_ctx->stdin_is_interactive = false;
264 run_stdin:
265 minst->run_start = false; /* don't run 'start' */
266 /* Set NOPAUSE so showpage won't try to read from stdin. */
267 code = swproc(minst, "-dNOPAUSE", pal);
268 if (code)
269 return code;
270 code = gs_main_init2(minst); /* Finish initialization */
271 if (code < 0)
272 return code;
273
274 code = run_string(minst, ".runstdin", runFlush);
275 if (code < 0)
276 return code;
277 break;
278 case '-': /* run with command line args */
279 case '+':
280 pal->expand_ats = false;
281 case '@': /* ditto with @-expansion */
282 {
283 const char *psarg = arg_next(pal, &code);
284
285 if (code < 0)
286 return e_Fatal;
287 if (psarg == 0) {
288 outprintf(minst->heap, "Usage: gs ... -%c file.ps arg1 ... argn\n", sw);
289 arg_finit(pal);
290 return e_Fatal;
291 }
292 psarg = arg_copy(psarg, minst->heap);
293 if (psarg == NULL)
294 return e_Fatal;
295 code = gs_main_init2(minst);
296 if (code < 0)
297 return code;
298 code = run_string(minst, "userdict/ARGUMENTS[", 0);
299 if (code < 0)
300 return code;
301 while ((arg = arg_next(pal, &code)) != 0) {
302 char *fname = arg_copy(arg, minst->heap);
303 if (fname == NULL)
304 return e_Fatal;
305 code = runarg(minst, "", fname, "", runInit);
306 if (code < 0)
307 return code;
308 }
309 if (code < 0)
310 return e_Fatal;
311 runarg(minst, "]put", psarg, ".runfile", runInit | runFlush);
312 return e_Quit;
313 }
314 case 'A': /* trace allocator */
315 switch (*arg) {
316 case 0:
317 gs_alloc_debug = 1;
318 break;
319 case '-':
320 gs_alloc_debug = 0;
321 break;
322 default:
323 puts(minst->heap, "-A may only be followed by -");
324 return e_Fatal;
325 }
326 break;
327 case 'B': /* set run_string buffer size */
328 if (*arg == '-')
329 minst->run_buffer_size = 0;
330 else {
331 uint bsize;
332
333 if (sscanf((const char *)arg, "%u", &bsize) != 1 ||
334 bsize <= 0 || bsize > MAX_BUFFERED_SIZE
335 ) {
336 outprintf(minst->heap,
337 "-B must be followed by - or size between 1 and %u\n",
338 MAX_BUFFERED_SIZE);
339 return e_Fatal;
340 }
341 minst->run_buffer_size = bsize;
342 }
343 break;
344 case 'c': /* code follows */
345 {
346 bool ats = pal->expand_ats;
347
348 code = gs_main_init2(minst);
349 if (code < 0)
350 return code;
351 pal->expand_ats = false;
352 while ((arg = arg_next(pal, &code)) != 0) {
353 char *sarg;
354
355 if (arg[0] == '@' ||
356 (arg[0] == '-' && !isdigit(arg[1]))
357 )
358 break;
359 sarg = arg_copy(arg, minst->heap);
360 if (sarg == NULL)
361 return e_Fatal;
362 code = runarg(minst, "", sarg, ".runstring", 0);
363 if (code < 0)
364 return code;
365 }
366 if (code < 0)
367 return e_Fatal;
368 if (arg != 0) {
369 char *p = arg_copy(arg, minst->heap);
370 if (p == NULL)
371 return e_Fatal;
372 arg_push_string(pal, p);
373 }
374 pal->expand_ats = ats;
375 break;
376 }
377 case 'E': /* log errors */
378 switch (*arg) {
379 case 0:
380 gs_log_errors = 1;
381 break;
382 case '-':
383 gs_log_errors = 0;
384 break;
385 default:
386 puts(minst->heap, "-E may only be followed by -");
387 return e_Fatal;
388 }
389 break;
390 case 'f': /* run file of arbitrary name */
391 if (*arg != 0) {
392 code = argproc(minst, arg);
393 if (code < 0)
394 return code;
395 }
396 break;
397 case 'F': /* run file with buffer_size = 1 */
398 if (!*arg) {
399 puts(minst->heap, "-F requires a file name");
400 return e_Fatal;
401 } {
402 uint bsize = minst->run_buffer_size;
403
404 minst->run_buffer_size = 1;
405 code = argproc(minst, arg);
406 minst->run_buffer_size = bsize;
407 if (code < 0)
408 return code;
409 }
410 break;
411 case 'g': /* define device geometry */
412 {
413 long width, height;
414 ref value;
415
416 if ((code = gs_main_init1(minst)) < 0)
417 return code;
418 if (sscanf((const char *)arg, "%ldx%ld", &width, &height) != 2) {
419 puts(minst->heap, "-g must be followed by <width>x<height>");
420 return e_Fatal;
421 }
422 make_int(&value, width);
423 initial_enter_name("DEVICEWIDTH", &value);
424 make_int(&value, height);
425 initial_enter_name("DEVICEHEIGHT", &value);
426 initial_enter_name("FIXEDMEDIA", &vtrue);
427 break;
428 }
429 case 'h': /* print help */
430 case '?': /* ditto */
431 print_help(minst);
432 return e_Info; /* show usage info on exit */
433 case 'I': /* specify search path */
434 {
435 char *path = arg_copy(arg, minst->heap);
436 if (path == NULL)
437 return e_Fatal;
438 gs_main_add_lib_path(minst, path);
439 }
440 break;
441 case 'K': /* set malloc limit */
442 {
443 long msize = 0;
444 gs_malloc_memory_t *rawheap = gs_malloc_wrapped_contents(minst->heap);
445
446 sscanf((const char *)arg, "%ld", &msize);
447 if (msize <= 0 || msize > max_long >> 10) {
448 outprintf(minst->heap, "-K<numK> must have 1 <= numK <= %ld\n",
449 max_long >> 10);
450 return e_Fatal;
451 }
452 rawheap->limit = msize << 10;
453 }
454 break;
455 case 'M': /* set memory allocation increment */
456 {
457 unsigned msize = 0;
458
459 sscanf((const char *)arg, "%u", &msize);
460 #if arch_ints_are_short
461 if (msize <= 0 || msize >= 64) {
462 puts(minst->heap, "-M must be between 1 and 63");
463 return e_Fatal;
464 }
465 #endif
466 minst->memory_chunk_size = msize << 10;
467 }
468 break;
469 case 'N': /* set size of name table */
470 {
471 unsigned nsize = 0;
472
473 sscanf((const char *)arg, "%d", &nsize);
474 #if arch_ints_are_short
475 if (nsize < 2 || nsize > 64) {
476 puts(minst->heap, "-N must be between 2 and 64");
477 return e_Fatal;
478 }
479 #endif
480 minst->name_table_size = (ulong) nsize << 10;
481 }
482 break;
483 case 'P': /* choose whether search '.' first */
484 if (!strcmp(arg, ""))
485 minst->search_here_first = true;
486 else if (!strcmp(arg, "-"))
487 minst->search_here_first = false;
488 else {
489 puts(minst->heap, "Only -P or -P- is allowed.");
490 return e_Fatal;
491 }
492 break;
493 case 'q': /* quiet startup */
494 if ((code = gs_main_init1(minst)) < 0)
495 return code;
496 initial_enter_name("QUIET", &vtrue);
497 break;
498 case 'r': /* define device resolution */
499 {
500 float xres, yres;
501 ref value;
502
503 if ((code = gs_main_init1(minst)) < 0)
504 return code;
505 switch (sscanf((const char *)arg, "%fx%f", &xres, &yres)) {
506 default:
507 puts(minst->heap, "-r must be followed by <res> or <xres>x<yres>");
508 return e_Fatal;
509 case 1: /* -r<res> */
510 yres = xres;
511 case 2: /* -r<xres>x<yres> */
512 make_real(&value, xres);
513 initial_enter_name("DEVICEXRESOLUTION", &value);
514 make_real(&value, yres);
515 initial_enter_name("DEVICEYRESOLUTION", &value);
516 initial_enter_name("FIXEDRESOLUTION", &vtrue);
517 }
518 break;
519 }
520 case 'D': /* define name */
521 case 'd':
522 case 'S': /* define name as string */
523 case 's':
524 {
525 char *adef = arg_copy(arg, minst->heap);
526 char *eqp;
527 bool isd = (sw == 'D' || sw == 'd');
528 ref value;
529
530 if (adef == NULL)
531 return e_Fatal;
532 eqp = strchr(adef, '=');
533
534 if (eqp == NULL)
535 eqp = strchr(adef, '#');
536 /* Initialize the object memory, scanner, and */
537 /* name table now if needed. */
538 if ((code = gs_main_init1(minst)) < 0)
539 return code;
540 if (eqp == adef) {
541 puts(minst->heap, "Usage: -dname, -dname=token, -sname=string");
542 return e_Fatal;
543 }
544 if (eqp == NULL) {
545 if (isd)
546 make_true(&value);
547 else
548 make_empty_string(&value, a_readonly);
549 } else {
550 int code;
551 i_ctx_t *i_ctx_p = minst->i_ctx_p;
552 uint space = icurrent_space;
553
554 *eqp++ = 0;
555 ialloc_set_space(idmemory, avm_system);
556 if (isd) {
557 stream astream;
558 scanner_state state;
559
560 s_init(&astream, NULL);
561 sread_string(&astream,
562 (const byte *)eqp, strlen(eqp));
563 scanner_state_init(&state, false);
564 code = scan_token(minst->i_ctx_p, &astream, &value,
565 &state);
566 if (code) {
567 puts(minst->heap, "-dname= must be followed by a valid token");
568 return e_Fatal;
569 }
570 if (r_has_type_attrs(&value, t_name,
571 a_executable)) {
572 ref nsref;
573
574 name_string_ref(minst->heap, &value, &nsref);
575 #define string_is(nsref, str, len)\
576 (r_size(&(nsref)) == (len) &&\
577 !strncmp((const char *)(nsref).value.const_bytes, str, (len)))
578 if (string_is(nsref, "null", 4))
579 make_null(&value);
580 else if (string_is(nsref, "true", 4))
581 make_true(&value);
582 else if (string_is(nsref, "false", 5))
583 make_false(&value);
584 else {
585 puts(minst->heap,
586 "-dvar=name requires name=null, true, or false");
587 return e_Fatal;
588 }
589 #undef name_is_string
590 }
591 } else {
592 int len = strlen(eqp);
593 char *str =
594 (char *)gs_alloc_bytes(minst->heap,
595 (uint) len, "-s");
596
597 if (str == 0) {
598 lprintf("Out of memory!\n");
599 return e_Fatal;
600 }
601 memcpy(str, eqp, len);
602 make_const_string(&value,
603 a_readonly | avm_foreign,
604 len, (const byte *)str);
605 if ((code = try_stdout_redirect(minst, adef, eqp)) < 0)
606 return code;
607 }
608 ialloc_set_space(idmemory, space);
609 }
610 /* Enter the name in systemdict. */
611 initial_enter_name(adef, &value);
612 break;
613 }
614 case 'T':
615 set_debug_flags(arg, vd_flags);
616 break;
617 case 'u': /* undefine name */
618 if (!*arg) {
619 puts(minst->heap, "-u requires a name to undefine.");
620 return e_Fatal;
621 }
622 if ((code = gs_main_init1(minst)) < 0)
623 return code;
624 i_initial_remove_name(minst->i_ctx_p, arg);
625 break;
626 case 'v': /* print revision */
627 print_revision(minst);
628 return e_Info;
629 /*#ifdef DEBUG */
630 /*
631 * Here we provide a place for inserting debugging code that can be
632 * run in place of the normal interpreter code.
633 */
634 case 'X':
635 code = gs_main_init2(minst);
636 if (code < 0)
637 return code;
638 {
639 int xec; /* exit_code */
640 ref xeo; /* error_object */
641
642 #define start_x()\
643 gs_main_run_string_begin(minst, 1, &xec, &xeo)
644 #define run_x(str)\
645 gs_main_run_string_continue(minst, str, strlen(str), 1, &xec, &xeo)
646 #define stop_x()\
647 gs_main_run_string_end(minst, 1, &xec, &xeo)
648 start_x();
649 run_x("\216\003abc");
650 run_x("== flush\n");
651 stop_x();
652 }
653 return e_Quit;
654 /*#endif */
655 case 'Z':
656 set_debug_flags(arg, gs_debug);
657 break;
658 }
659 return 0;
660 }
661
662 /* Define versions of strlen and strcat that encode strings in hex. */
663 /* This is so we can enter escaped characters regardless of whether */
664 /* the Level 1 convention of ignoring \s in strings-within-strings */
665 /* is being observed (sigh). */
666 private int
esc_strlen(const char * str)667 esc_strlen(const char *str)
668 {
669 return strlen(str) * 2 + 2;
670 }
671 private void
esc_strcat(char * dest,const char * src)672 esc_strcat(char *dest, const char *src)
673 {
674 char *d = dest + strlen(dest);
675 const char *p;
676 static const char *const hex = "0123456789abcdef";
677
678 *d++ = '<';
679 for (p = src; *p; p++) {
680 byte c = (byte) * p;
681
682 *d++ = hex[c >> 4];
683 *d++ = hex[c & 0xf];
684 }
685 *d++ = '>';
686 *d = 0;
687 }
688
689 /* Process file names */
690 private int
argproc(gs_main_instance * minst,const char * arg)691 argproc(gs_main_instance * minst, const char *arg)
692 {
693 int code = gs_main_init1(minst); /* need i_ctx_p to proceed */
694 char *filearg;
695
696 if (code < 0)
697 return code;
698 filearg = arg_copy(arg, minst->heap);
699 if (filearg == NULL)
700 return e_Fatal;
701 if (minst->run_buffer_size) {
702 /* Run file with run_string. */
703 return run_buffered(minst, filearg);
704 } else {
705 /* Run file directly in the normal way. */
706 return runarg(minst, "", filearg, ".runfile", runInit | runFlush);
707 }
708 }
709 private int
run_buffered(gs_main_instance * minst,const char * arg)710 run_buffered(gs_main_instance * minst, const char *arg)
711 {
712 FILE *in = gp_fopen(arg, gp_fmode_rb);
713 int exit_code;
714 ref error_object;
715 int code;
716
717 if (in == 0) {
718 outprintf(minst->heap, "Unable to open %s for reading", arg);
719 return_error(e_invalidfileaccess);
720 }
721 code = gs_main_init2(minst);
722 if (code < 0)
723 return code;
724 code = gs_main_run_string_begin(minst, minst->user_errors,
725 &exit_code, &error_object);
726 if (!code) {
727 char buf[MAX_BUFFERED_SIZE];
728 int count;
729
730 code = e_NeedInput;
731 while ((count = fread(buf, 1, minst->run_buffer_size, in)) > 0) {
732 code = gs_main_run_string_continue(minst, buf, count,
733 minst->user_errors,
734 &exit_code, &error_object);
735 if (code != e_NeedInput)
736 break;
737 }
738 if (code == e_NeedInput) {
739 code = gs_main_run_string_end(minst, minst->user_errors,
740 &exit_code, &error_object);
741 }
742 }
743 fclose(in);
744 zflush(minst->i_ctx_p);
745 zflushpage(minst->i_ctx_p);
746 return run_finish(minst, code, exit_code, &error_object);
747 }
748 private int
runarg(gs_main_instance * minst,const char * pre,const char * arg,const char * post,int options)749 runarg(gs_main_instance * minst, const char *pre, const char *arg,
750 const char *post, int options)
751 {
752 int len = strlen(pre) + esc_strlen(arg) + strlen(post) + 1;
753 int code;
754 char *line;
755
756 if (options & runInit) {
757 code = gs_main_init2(minst); /* Finish initialization */
758
759 if (code < 0)
760 return code;
761 }
762 line = (char *)gs_alloc_bytes(minst->heap, len, "argproc");
763 if (line == 0) {
764 lprintf("Out of memory!\n");
765 return_error(e_VMerror);
766 }
767 strcpy(line, pre);
768 esc_strcat(line, arg);
769 strcat(line, post);
770 minst->i_ctx_p->starting_arg_file = true;
771 code = run_string(minst, line, options);
772 minst->i_ctx_p->starting_arg_file = false;
773 return code;
774 }
775 private int
run_string(gs_main_instance * minst,const char * str,int options)776 run_string(gs_main_instance * minst, const char *str, int options)
777 {
778 int exit_code;
779 ref error_object;
780 int code = gs_main_run_string(minst, str, minst->user_errors,
781 &exit_code, &error_object);
782
783 if ((options & runFlush) || code != 0) {
784 zflush(minst->i_ctx_p); /* flush stdout */
785 zflushpage(minst->i_ctx_p); /* force display update */
786 }
787 return run_finish(minst, code, exit_code, &error_object);
788 }
789 private int
run_finish(gs_main_instance * minst,int code,int exit_code,ref * perror_object)790 run_finish(gs_main_instance *minst, int code, int exit_code,
791 ref * perror_object)
792 {
793 switch (code) {
794 case e_Quit:
795 case 0:
796 break;
797 case e_Fatal:
798 eprintf1("Unrecoverable error, exit code %d\n", exit_code);
799 break;
800 default:
801 gs_main_dump_stack(minst, code, perror_object);
802 }
803 return code;
804 }
805
806 /* Redirect stdout to a file:
807 * -sstdout=filename
808 * -sstdout=-
809 * -sstdout=%stdout
810 * -sstdout=%stderr
811 * -sOutputFile=- is not affected.
812 * File is closed at program exit (if not stdout/err)
813 * or when -sstdout is used again.
814 */
815 private int
try_stdout_redirect(gs_main_instance * minst,const char * command,const char * filename)816 try_stdout_redirect(gs_main_instance * minst,
817 const char *command, const char *filename)
818 {
819 if (strcmp(command, "stdout") == 0) {
820 minst->heap->gs_lib_ctx->stdout_to_stderr = 0;
821 minst->heap->gs_lib_ctx->stdout_is_redirected = 0;
822 /* If stdout already being redirected and it is not stdout
823 * or stderr, close it
824 */
825 if (minst->heap->gs_lib_ctx->fstdout2
826 && (minst->heap->gs_lib_ctx->fstdout2 != minst->heap->gs_lib_ctx->fstdout)
827 && (minst->heap->gs_lib_ctx->fstdout2 != minst->heap->gs_lib_ctx->fstderr)) {
828 fclose(minst->heap->gs_lib_ctx->fstdout2);
829 minst->heap->gs_lib_ctx->fstdout2 = (FILE *)NULL;
830 }
831 /* If stdout is being redirected, set minst->fstdout2 */
832 if ( (filename != 0) && strlen(filename) &&
833 strcmp(filename, "-") && strcmp(filename, "%stdout") ) {
834 if (strcmp(filename, "%stderr") == 0) {
835 minst->heap->gs_lib_ctx->stdout_to_stderr = 1;
836 }
837 else if ((minst->heap->gs_lib_ctx->fstdout2 =
838 fopen(filename, "w")) == (FILE *)NULL)
839 return_error(e_invalidfileaccess);
840 minst->heap->gs_lib_ctx->stdout_is_redirected = 1;
841 }
842 return 0;
843 }
844 return 1;
845 }
846
847 /* ---------------- Print information ---------------- */
848
849 /*
850 * Help strings. We have to break them up into parts, because
851 * the Watcom compiler has a limit of 510 characters for a single token.
852 * For PC displays, we want to limit the strings to 24 lines.
853 */
854 private const char help_usage1[] = "\
855 Usage: gs [switches] [file1.ps file2.ps ...]\n\
856 Most frequently used switches: (you can use # in place of =)\n\
857 -dNOPAUSE no pause after page | -q `quiet', fewer messages\n\
858 -g<width>x<height> page size in pixels | -r<res> pixels/inch resolution\n";
859 private const char help_usage2[] = "\
860 -sDEVICE=<devname> select device | -dBATCH exit after last file\n\
861 -sOutputFile=<file> select output file: - for stdout, |command for pipe,\n\
862 embed %d or %ld for page #\n";
863 private const char help_trailer[] = "\
864 For more information, see %s.\n\
865 Report bugs to %s, using the form in Bug-form.htm.\n";
866 private const char help_devices[] = "Available devices:";
867 private const char help_default_device[] = "Default output device:";
868 private const char help_emulators[] = "Input formats:";
869 private const char help_paths[] = "Search path:";
870
871 /* Print the standard help message. */
872 private void
print_help(gs_main_instance * minst)873 print_help(gs_main_instance * minst)
874 {
875 print_revision(minst);
876 print_usage(minst);
877 print_emulators(minst);
878 print_devices(minst);
879 print_paths(minst);
880 if (gs_init_string_sizeof > 0) {
881 outprintf(minst->heap, "Initialization files are compiled into the executable.\n");
882 }
883 print_help_trailer(minst);
884 }
885
886 /* Print the revision, revision date, and copyright. */
887 private void
print_revision(const gs_main_instance * minst)888 print_revision(const gs_main_instance *minst)
889 {
890 printf_program_ident(minst->heap, gs_product, gs_revision);
891 outprintf(minst->heap, " (%d-%02d-%02d)\n%s\n",
892 (int)(gs_revisiondate / 10000),
893 (int)(gs_revisiondate / 100 % 100),
894 (int)(gs_revisiondate % 100),
895 gs_copyright);
896 }
897
898 /* Print the version number. */
899 private void
print_version(const gs_main_instance * minst)900 print_version(const gs_main_instance *minst)
901 {
902 printf_program_ident(minst->heap, NULL, gs_revision);
903 }
904
905 /* Print usage information. */
906 private void
print_usage(const gs_main_instance * minst)907 print_usage(const gs_main_instance *minst)
908 {
909 outprintf(minst->heap, "%s", help_usage1);
910 outprintf(minst->heap, "%s", help_usage2);
911 }
912
913 /* compare function for qsort */
914 private int
cmpstr(const void * v1,const void * v2)915 cmpstr(const void *v1, const void *v2)
916 {
917 return strcmp( *(char * const *)v1, *(char * const *)v2 );
918 }
919
920 /* Print the list of available devices. */
921 private void
print_devices(const gs_main_instance * minst)922 print_devices(const gs_main_instance *minst)
923 {
924 outprintf(minst->heap, "%s", help_default_device);
925 outprintf(minst->heap, " %s\n", gs_devicename(gs_getdevice(0)));
926 outprintf(minst->heap, "%s", help_devices);
927 {
928 int i;
929 int pos = 100;
930 const gx_device *pdev;
931 const char **names;
932 size_t ndev = 0;
933
934 for (i = 0; (pdev = gs_getdevice(i)) != 0; i++)
935 ;
936 ndev = (size_t)i;
937 names = (const char **)gs_alloc_bytes(minst->heap, ndev * sizeof(const char*), "print_devices");
938 if (names == (const char **)NULL) { /* old-style unsorted device list */
939 for (i = 0; (pdev = gs_getdevice(i)) != 0; i++) {
940 const char *dname = gs_devicename(pdev);
941 int len = strlen(dname);
942
943 if (pos + 1 + len > 76)
944 outprintf(minst->heap, "\n "), pos = 2;
945 outprintf(minst->heap, " %s", dname);
946 pos += 1 + len;
947 }
948 }
949 else { /* new-style sorted device list */
950 for (i = 0; (pdev = gs_getdevice(i)) != 0; i++)
951 names[i] = gs_devicename(pdev);
952 qsort((void*)names, ndev, sizeof(const char*), cmpstr);
953 for (i = 0; i < ndev; i++) {
954 int len = strlen(names[i]);
955
956 if (pos + 1 + len > 76)
957 outprintf(minst->heap, "\n "), pos = 2;
958 outprintf(minst->heap, " %s", names[i]);
959 pos += 1 + len;
960 }
961 gs_free(minst->heap, (char *)names, ndev * sizeof(const char*), 1, "print_devices");
962 }
963 }
964 outprintf(minst->heap, "\n");
965 }
966
967 /* Print the list of language emulators. */
968 private void
print_emulators(const gs_main_instance * minst)969 print_emulators(const gs_main_instance *minst)
970 {
971 outprintf(minst->heap, "%s", help_emulators);
972 {
973 const ref *pes;
974
975 for (pes = gs_emulator_name_array;
976 pes->value.const_bytes != 0; pes++
977 )
978 /*
979 * Even though gs_emulator_name_array is declared and used as
980 * an array of string refs, each string is actually a
981 * (null terminated) C string.
982 */
983 outprintf(minst->heap, " %s", (const char *)pes->value.const_bytes);
984 }
985 outprintf(minst->heap, "\n");
986 }
987
988 /* Print the search paths. */
989 private void
print_paths(gs_main_instance * minst)990 print_paths(gs_main_instance * minst)
991 {
992 outprintf(minst->heap, "%s", help_paths);
993 gs_main_set_lib_paths(minst);
994 {
995 uint count = r_size(&minst->lib_path.list);
996 uint i;
997 int pos = 100;
998 char fsepr[3];
999
1000 fsepr[0] = ' ', fsepr[1] = gp_file_name_list_separator,
1001 fsepr[2] = 0;
1002 for (i = 0; i < count; ++i) {
1003 const ref *prdir =
1004 minst->lib_path.list.value.refs + i;
1005 uint len = r_size(prdir);
1006 const char *sepr = (i == count - 1 ? "" : fsepr);
1007
1008 if (1 + pos + strlen(sepr) + len > 76)
1009 outprintf(minst->heap, "\n "), pos = 2;
1010 outprintf(minst->heap, " ");
1011 /*
1012 * This is really ugly, but it's necessary because some
1013 * platforms rely on all console output being funneled through
1014 * outprintf. We wish we could just do:
1015 fwrite(prdir->value.bytes, 1, len, minst->fstdout);
1016 */
1017 {
1018 const char *p = (const char *)prdir->value.bytes;
1019 uint j;
1020
1021 for (j = len; j; j--)
1022 outprintf(minst->heap, "%c", *p++);
1023 }
1024 outprintf(minst->heap, "%s", sepr);
1025 pos += 1 + len + strlen(sepr);
1026 }
1027 }
1028 outprintf(minst->heap, "\n");
1029 }
1030
1031 /* Print the help trailer. */
1032 private void
print_help_trailer(const gs_main_instance * minst)1033 print_help_trailer(const gs_main_instance *minst)
1034 {
1035 char buffer[gp_file_name_sizeof];
1036 const char *use_htm = "Use.htm", *p = buffer;
1037 uint blen = sizeof(buffer);
1038
1039 if (gp_file_name_combine(gs_doc_directory, strlen(gs_doc_directory),
1040 use_htm, strlen(use_htm), false, buffer, &blen) != gp_combine_success)
1041 p = use_htm;
1042 outprintf(minst->heap, help_trailer, p, GS_BUG_MAILBOX);
1043 }
1044