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