1 /*
2  * Argument handling and main.
3  * Copyright (c) 1995-2003 Markku Rossi.
4  *
5  * Author: Markku Rossi <mtr@iki.fi>
6  */
7 
8 /*
9  * This file is part of GNU Enscript.
10  *
11  * Enscript is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * Enscript is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Enscript.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #include "gsint.h"
26 #include "getopt.h"
27 
28 /*
29  * Prototypes for static functions.
30  */
31 
32 /*
33  * Open output file according to user options.  Void if output file
34  * has already been opened.
35  */
36 static void open_output_file ();
37 
38 /* Close output file. */
39 static void close_output_file ();
40 
41 /* Handle options from environment variable <var> */
42 static void handle_env_options ___P ((char *var));
43 
44 /* Handle options from <argv> array. */
45 static void handle_options ___P ((int argc, char *argv[]));
46 
47 /* Print usage info. */
48 static void usage ();
49 
50 /* Print version info. */
51 static void version ();
52 
53 
54 /*
55  * Global variables.
56  */
57 
58 char *program;			/* Program's name, used for messages. */
59 FILE *ofp = NULL;		/* Output file. */
60 void *printer_context;		/* Context for the printer. */
61 char *date_string = NULL;	/* Preformatted time string. */
62 struct tm run_tm;		/* Time when program is run. */
63 struct tm mod_tm;		/* Last modification time for current file. */
64 struct passwd *passwd;		/* Passwd entry for the user running this
65 				   program. */
66 
67 /* Path to our library. */
68 char *enscript_library = LIBRARY;
69 
70 /* Library lookup path. */
71 char *libpath = NULL;
72 
73 /* AFM library lookup path. */
74 char *afm_path = NULL;
75 
76 MediaEntry *media_names = NULL;	/* List of known media. */
77 MediaEntry *media = NULL;	/* Entry for used media. */
78 int bs = 8;			/* The backspace character. */
79 
80 /* Statistics. */
81 int total_pages = 0;		/* Total number of pages printed. */
82 int num_truncated_lines = 0;	/* Number of lines truncated. */
83 int num_missing_chars = 0;	/* Number of unknown characters. */
84 int missing_chars[256] = {0};	/* Table of unknown characters. */
85 int num_non_printable_chars = 0; /* Number of non-printable characters. */
86 int non_printable_chars[256] = {0}; /* Table of non-printable characters. */
87 
88 /* Output media dimensions that are used during PostScript emission. */
89 int d_page_w = 0;		/* page's width */
90 int d_page_h = 0;		/* page's height */
91 int d_header_w = 0;		/* fancy header's width */
92 int d_header_h = 0;		/* fancy header's height */
93 int d_footer_h = 0;		/* fancy footer's height */
94 int d_output_w = 0;		/* output area's width */
95 int d_output_h = 0;		/* output area's height  */
96 int d_output_x_margin = 5;	/* output area's x marginal */
97 int d_output_y_margin = 2;	/* output area's y marginal */
98 
99 /* Document needed resources. */
100 StringHashPtr res_fonts;	/* fonts */
101 
102 /* Fonts to download. */
103 StringHashPtr download_fonts;
104 
105 /* Additional key-value pairs, passed to the generated PostScript code. */
106 StringHashPtr pagedevice;	/* for setpagedevice */
107 StringHashPtr statusdict;	/* for statusdict */
108 
109 /* User defined strings. */
110 StringHashPtr user_strings;
111 
112 /* Cache for AFM files. */
113 StringHashPtr afm_cache = NULL;
114 StringHashPtr afm_info_cache = NULL;
115 
116 /* AFM library handle. */
117 AFMHandle afm = NULL;
118 
119 
120 /* Options. */
121 
122 /*
123  * Free single-letter options are: Q, x, y, Y
124  */
125 
126 /*
127  * -#
128  *
129  * An alias for -n, --copies.
130  */
131 
132 /*
133  * -1, -2, -3, -4, -5, -6, -7, -8, -9, --columns=NUM
134  *
135  * Number of columns per page.  The default is 1 column.
136  */
137 int num_columns = 1;
138 
139 /*
140  * -a PAGES, --pages=PAGES
141  *
142  * Specify which pages are printed.
143  */
144 PageRange *page_ranges = NULL;
145 
146 /*
147  * -A ALIGN, --file-align=ALIGN
148  *
149  * Align input files to start from ALIGN page count.  This is handy
150  * for two-side printings.
151  */
152 unsigned int file_align = 1;
153 
154 /*
155  * -b STRING, --header=STRING
156  *
157  * Set the string that is used as the page header.  As a default, page
158  * header is constructed from filename, date and page number.
159  */
160 char *page_header = NULL;
161 
162 /*
163  * -B, --no-header
164  *
165  * Do not print page headers.
166  */
167 
168 /*
169  * -c, --truncate-lines
170  *
171  * Truncate lines that are longer than the page width.  Default is character
172  * wrap.
173  */
174 LineEndType line_end = LE_CHAR_WRAP;
175 
176 /*
177  * -C [START], --line-numbers[=START]
178  *
179  * Precede each line with its line number.  As a default, do not mark
180  * line numbers.  If the optional argument START is given, it
181  * specifies the number from which the line numbers are assumed to
182  * start in the file.  This is useful if the file contains a region
183  * of a bigger file.
184  */
185 int line_numbers = 0;
186 unsigned int start_line_number = 1;
187 
188 /*
189  * -d, -P, --printer
190  *
191  * Name of the printer to which output is send.  Defaults to system's
192  * default printer.
193  */
194 char *printer = NULL;
195 
196 /*
197  * -e [CHAR], --escapes[=CHAR]
198  *
199  * Enable special escape ('\000') interpretation.  If option CHAR is given
200  * it is assumed to specify the escape character.
201  */
202 int special_escapes = 0;
203 int escape_char = '\0';
204 int default_escape_char;
205 
206 /*
207  * -E [LANG], --highlight=[LANG] (deprecated --pretty-print[=LANG])
208  *
209  * Highlight program source code.  Highlighting is handled by creating
210  * an input filter with the states-program.  States makes an educated
211  * guess about the start state but sometimes it fails, so the start
212  * state can also be specified to be LANG.  This option overwrites
213  * input filter and enables special escapes.
214  */
215 
216 int highlight = 0;
217 char *hl_start_state = NULL;
218 
219 /*
220  * -f, --font
221  *
222  * Select body font.
223  */
224 char *Fname = "Courier";
225 FontPoint Fpt = {10.0, 10.0};
226 FontPoint default_Fpt;		/* Point size of the original font. */
227 char *default_Fname;		/* Name of the original font. */
228 InputEncoding default_Fencoding; /* The encoding of the original font. */
229 int user_body_font_defined = 0;	/* Has user defined new body font? */
230 
231 double font_widths[256];	/* Width array for body font. */
232 char font_ctype[256];		/* Font character types. */
233 int font_is_fixed;		/* Is body font a fixed pitch font? */
234 double font_bbox_lly;		/* Font's bounding box's lly-coordinate. */
235 
236 /*
237  * -F, --header-font
238  *
239  * Select font to be used to print the standard simple header.
240  */
241 char *HFname = "Courier-Bold";
242 FontPoint HFpt = {10.0, 10.0};
243 
244 /*
245  * -g, --print-anyway
246  *
247  * Print document even it contains binary data.  This does nothing
248  * since enscript prints files anyway.
249  */
250 
251 /*
252  * -G, --fancy-header
253  *
254  * Add a fancy header to top of every page.  There are several header styles
255  * but the default is 'no fancy header'.
256  */
257 HeaderType header = HDR_SIMPLE;
258 char *fancy_header_name = NULL;
259 char *fancy_header_default = NULL;
260 
261 /*
262  * -h, --no-job-header
263  *
264  * Supress the job header page.
265  */
266 static int no_job_header = 0;
267 
268 /*
269  * -H num, --highlight-bars=num
270  *
271  * Print highlight bars under text.  Bars will be <num> lines high.
272  * As a default, do not print bars.
273  */
274 unsigned int highlight_bars = 0;
275 
276 /*
277  * -i, --indent
278  *
279  * Indent every line this many characters.
280  */
281 double line_indent = 0.0;
282 char *line_indent_spec = "0";
283 
284 /*
285  * -I CMD, --filter=CMD
286  *
287  * Read input files through input filter CMD.
288  */
289 char *input_filter = NULL;
290 
291 /*
292  * -j, --borders
293  *
294  * Print borders around columns.
295  */
296 int borders = 0;
297 
298 /*
299  * -J
300  *
301  * An alias for -t, --title.
302  */
303 
304 /*
305  * -k, --page-prefeed
306  * -K, --no-page-prefeed
307  *
308  * Control page prefeed.
309  */
310 int page_prefeed = 0;
311 
312 /*
313  * -l, --lineprinter
314  *
315  * Emulate lineprinter -  make pages 66 lines long and omit headers.
316  */
317 
318 /*
319  * -L, --lines-per-page
320  *
321  * Specify how many lines should be printed on a single page.  Normally
322  * enscript counts it from font point sizes.
323  */
324 unsigned int lines_per_page = (unsigned int) -1;
325 
326 /*
327  * -m, --mail
328  *
329  * Send mail notification to user after print job has been completed.
330  */
331 int mail = 0;
332 
333 /*
334  * -M, --media
335  *
336  * Name of the output media.  Default is A4.
337  */
338 char *media_name = NULL;
339 
340 /*
341  * -n, --copies
342  *
343  * Number of copies to print.
344  */
345 int num_copies = 1;
346 
347 /*
348  * -N, --newline
349  *
350  * Set the newline character: '\n' or '\r'.  As a default, the newline
351  * character is specified by the input encoding.
352  */
353 int nl = -1;
354 
355 /*
356  * -o, -p, --output
357  *
358  * Leave output to the specified file.  As a default result is spooled to
359  * printer.
360  */
361 char *output_file = OUTPUT_FILE_NONE;
362 
363 /*
364  * -O, --missing-characters
365  *
366  * List all missing characters.  Default is no listing.
367  */
368 int list_missing_characters = 0;
369 
370 /*
371  * -q, --quiet
372  *
373  * Do not tell what we are doing.  Default is to tell something but
374  * not --verbose.
375  */
376 int quiet = 0;
377 
378 /*
379  * -r, --landscape
380  * -R, --portrait
381  *
382  * Print with page rotated 90 degrees (landscape mode).  Default is
383  * portrait.
384  */
385 int landscape = 0;
386 
387 /*
388  * -s, --baselineskip
389  *
390  * Specify baselineskip value that is used when enscript moves to
391  * a new line.  Current point movement is font_point_size + baselineskip.
392  */
393 double baselineskip = 1.0;
394 
395 /*
396  * -t, --title
397  *
398  * Title which is printed to the banner page.  If this option is given
399  * from the command line, this sets also the name of the stdin which
400  * is by the default "".
401  */
402 char *title = "Enscript Output";
403 int title_given = 0;
404 
405 /*
406  * -T, --tabsize
407  *
408  * Specify tabulator size.
409  */
410 int tabsize = 8;
411 
412 /*
413  * -u, --underlay
414  *
415  * Place text under every page.  Default is no underlay.
416  */
417 double ul_gray = .8;
418 FontPoint ul_ptsize = {200.0, 200.0};
419 char *ul_font = "Times-Roman";
420 char *underlay = NULL;
421 char *ul_position = NULL;	/* Position info as a string. */
422 double ul_x;			/* Position x-coordinate. */
423 double ul_y;			/* Position y-coordinate. */
424 double ul_angle;
425 unsigned int ul_style = UL_STYLE_OUTLINE;
426 char *ul_style_str = NULL;
427 int ul_position_p = 0;		/* Is ul-position given? */
428 int ul_angle_p = 0;		/* Is ul-angle given? */
429 
430 /*
431  * -U NUM, --nup=NUM
432  *
433  * Print NUM PostScript pages on each output page (n-up printing).
434  */
435 unsigned int nup = 1;
436 unsigned int nup_exp = 0;
437 unsigned int nup_rows = 1;
438 unsigned int nup_columns = 1;
439 int nup_landscape = 0;
440 unsigned int nup_width;
441 unsigned int nup_height;
442 double nup_scale;
443 
444 /*
445  * -v, --verbose
446  *
447  * Tell what we are doing.  Default is no verbose outputs.
448  */
449 int verbose = 0;
450 
451 /*
452  * -V, --version
453  *
454  * Print version information.
455  */
456 
457 /*
458  * -w LANGUAGE, --language=LANGUAGE
459  *
460  * Generate output for language LANGUAGE.  The default is PostScript.
461  */
462 char *output_language = "PostScript";
463 int output_language_pass_through = 0;
464 
465 /*
466  * -W APP,option, --options=APP,OPTION
467  *
468  * Pass additional option to enscript's helper applications.  The
469  * first part of the option's argument (APP) specifies the
470  * helper application to which the options are added.  Currently the
471  * following helper application are defined:
472  *
473  *   s	states
474  */
475 Buffer *helper_options[256] = {0};
476 
477 /*
478  * -X, --encoding
479  *
480  * Specifies input encoding.  Default is ISO-8859.1.
481  */
482 InputEncoding encoding = ENC_ISO_8859_1;
483 char *encoding_name = NULL;
484 
485 /*
486  * -z, --no-formfeed
487  *
488  * Do not interpret form feed characters.  As a default, form feed
489  * characters are interpreted.
490  */
491 int interpret_formfeed = 1;
492 
493 /*
494  * -Z, --pass-through
495  *
496  * Pass through all PostScript and PCL files without any modifications.
497  * As a default, don't.
498  */
499 int pass_through = 0;
500 
501 /*
502  * --color[=bool]
503  *
504  * Create color output with states?
505  */
506 
507 /*
508  * --continuous-page-numbers
509  *
510  * Count page numbers across input files.  Don't restart numbering
511  * at beginning of each file.
512  */
513 int continuous_page_numbers = 0;
514 
515 /*
516  * --download-font=FONT
517  *
518  * Download font FONT to printer.
519  */
520 
521 /*
522  * --extended-return-values
523  *
524  * Enable extended return values.
525  */
526 int extended_return_values = 0;
527 
528 /*
529  * --filter-stdin=STR
530  *
531  * How stdin is shown to the filter command.  The default is "" but
532  * some utilities might want it as "-".
533  */
534 char *input_filter_stdin = "";
535 
536 /*
537  * --footer=STRING
538  *
539  * Set the string that is used as the page footer.  As a default, the
540  * page has no footer.  Setting this option does not necessary show
541  * any footer strings in the output.  It depends on the selected
542  * header (`.hdr' file) whether it supports footer strings or not.
543  */
544 char *page_footer = NULL;
545 
546 /*
547  * --h-column-height=HEIGHT
548  *
549  * Set the horizontal column (channel) height to be HEIGHT.  This option
550  * also sets the FormFeedType to `hcolumn'.  The default value is set to be
551  * big enough to cause a jump to the next vertical column (100m).
552  */
553 double horizontal_column_height = 283465.0;
554 
555 /*
556  * --help-highlight (deprecated --help-pretty-print)
557  *
558  * Descript all supported -E, --highlight languages and file formats.
559  */
560 int help_highlight = 0;
561 
562 /*
563  * --highlight-bar-gray=val
564  *
565  * Specify the gray level for highlight bars.
566  */
567 double highlight_bar_gray = .97;
568 
569 /*
570  * --list-media
571  *
572  * List all known media.  As a default do not list media names.
573  */
574 int list_media = 0;
575 
576 /*
577  * --margins=LEFT:RIGHT:TOP:BOTTOM
578  *
579  * Adjust page marginals.
580  */
581 char *margins_spec = NULL;
582 
583 /*
584  * --mark-wrapped-lines[=STYLE]
585  *
586  * Mark wrapped lines so that they can be easily detected from the printout.
587  * Optional parameter STYLE specifies the marking style, the system default
588  * is black box.
589  */
590 char *mark_wrapped_lines_style_name = NULL;
591 MarkWrappedLinesStyle mark_wrapped_lines_style = MWLS_NONE;
592 
593 /*
594  * --non-printable-format=FORMAT
595  *
596  * Format in which non-printable characters are printed.
597  */
598 char *npf_name = NULL;
599 NonPrintableFormat non_printable_format = NPF_OCTAL;
600 
601 /*
602  * --nup-columnwise
603  *
604  * Layout N-up pages colunwise instead of row-wise.
605  */
606 int nup_columnwise = 0;
607 
608 /*
609  * --nup-xpad=NUM
610  *
611  * The x-padding between N-up subpages.
612  */
613 unsigned int nup_xpad = 10;
614 
615 /*
616  * --nup-ypad=NUM
617  *
618  * The y-padding between N-up subpages.
619  */
620 unsigned int nup_ypad = 10;
621 
622 /*
623  * --page-label-format=FORMAT
624  *
625  * Format in which page labels are printed; the default is "short".
626  */
627 char *page_label_format = NULL;
628 PageLabelFormat page_label;
629 
630 /*
631  * --ps-level=LEVEL
632  *
633  * The PostScript language level that enscript should use; the default is 2.
634  */
635 unsigned int pslevel = 2;
636 
637 /*
638  * --printer-options=OPTIONS
639  *
640  * Pass extra options OPTIONS to the printer spooler.
641  */
642 char *printer_options = NULL;
643 
644 /*
645  * --rotate-even-pages
646  *
647  * Rotate each even-numbered page 180 degrees.  This might be handy in
648  * two-side printing when the resulting pages are bind from some side.
649  * Greetings to Jussi-Pekka Sairanen.
650  */
651 int rotate_even_pages = 0;
652 
653 /*
654  * --slice=NUM
655  *
656  * Horizontal input slicing.  Print only NUMth wrapped input pages.
657  */
658 int slicing = 0;
659 unsigned int slice = 1;
660 
661 /*
662  * --swap-even-page-margins
663  *
664  * Swap left and right side margins for each even numbered page.  This
665  * might be handy in two-side printing.
666  */
667 int swap_even_page_margins = 0;
668 
669 /*
670  * --toc
671  *
672  * Print Table of Contents page.
673  */
674 int toc = 0;
675 FILE *toc_fp;
676 char *toc_fmt_string;
677 
678 /*
679  * --word-wrap
680  *
681  * Wrap long lines from word boundaries.  The default is character wrap.
682  */
683 
684 /*
685  * AcceptCompositeCharacters: bool
686  *
687  * Specify whatever we accept composite characters or should them be
688  * considered as non-existent.  As a default, do not accept them.
689  */
690 int accept_composites = 0;
691 
692 /*
693  * AppendCtrlD: bool
694  *
695  * Append ^D character to the end of the output.  Some printers require this
696  * but the default is false.
697  */
698 int append_ctrl_D = 0;
699 
700 /*
701  * Clean7Bit: bool
702  *
703  * Specify how characters greater than 127 are printed.
704  */
705 int clean_7bit = 1;
706 
707 /*
708  * FormFeedType: type
709  *
710  * Specify what to do when a formfeed character is encountered from the
711  * input stream.  The default action is to jump to the beginning of the
712  * next column.
713  */
714 FormFeedType formfeed_type = FORMFEED_COLUMN;
715 
716 /*
717  * GeneratePageSize: bool
718  *
719  * Specify whether the `PageSize' pagedevice definitions should be
720  * generated to the output.
721  */
722 int generate_PageSize = 1;
723 
724 /*
725  * NoJobHeaderSwitch: switch
726  *
727  * Spooler switch to suppress the job header (-h).
728  */
729 char *no_job_header_switch = NULL;
730 
731 /*
732  * OutputFirstLine: line
733  *
734  * Set the PostScript output's first line to something your system can handle.
735  * The default is "%!PS-Adobe-3.0"
736  */
737 char *output_first_line = NULL;
738 
739 /*
740  * QueueParam: param
741  *
742  * The spooler command switch to select the printer queue (-P).
743  */
744 char *queue_param = NULL;
745 
746 /*
747  * Spooler: command
748  *
749  * The spooler command name (lpr).
750  */
751 char *spooler_command = NULL;
752 
753 /*
754  * StatesBinary: path
755  *
756  * An absolute path to the `states' binary.
757  */
758 
759 char *states_binary = NULL;
760 
761 /*
762  * StatesColor: bool
763  *
764  * Should the States program generate color outputs.
765  */
766 int states_color = 0;
767 
768 /*
769  * StatesConfigFile: file
770  *
771  * The name of the states' configuration file.
772  */
773 char *states_config_file = NULL;
774 
775 /*
776  * StatesHighlightStyle: style
777  *
778  * The highlight style.
779  */
780 char *states_highlight_style = NULL;
781 
782 /*
783  * StatesPath: path
784  *
785  * Define the path for the states program.  The states program will
786  * lookup its state definition files from this path.
787  */
788 char *states_path = NULL;
789 
790 /* ^@shade{GRAY}, set the line highlight gray. */
791 double line_highlight_gray = 1.0;
792 
793 /* ^@bggray{GRAY}, set the text background gray. */
794 double bggray = 1.0;
795 
796 EncodingRegistry encodings[] =
797 {
798   {{"88591", "latin1", NULL},		ENC_ISO_8859_1,		'\n', 8},
799   {{"88592", "latin2", NULL},		ENC_ISO_8859_2,		'\n', 8},
800   {{"88593", "latin3", NULL},		ENC_ISO_8859_3,		'\n', 8},
801   {{"88594", "latin4", NULL},		ENC_ISO_8859_4,		'\n', 8},
802   {{"88595", "cyrillic", NULL},		ENC_ISO_8859_5,		'\n', 8},
803   {{"88597", "greek", NULL},		ENC_ISO_8859_7,		'\n', 8},
804   {{"88599", "latin5", NULL},		ENC_ISO_8859_9,		'\n', 8},
805   {{"885910", "latin6", NULL},		ENC_ISO_8859_10,	'\n', 8},
806   {{"ascii", NULL, NULL},		ENC_ASCII, 		'\n', 8},
807   {{"asciifise", "asciifi", "asciise"},	ENC_ASCII_FISE,		'\n', 8},
808   {{"asciidkno", "asciidk", "asciino"},	ENC_ASCII_DKNO,		'\n', 8},
809   {{"ibmpc", "pc", "dos"},		ENC_IBMPC, 		'\n', 8},
810   {{"mac", NULL, NULL},			ENC_MAC, 		'\r', 8},
811   {{"vms", NULL, NULL},			ENC_VMS, 		'\n', 8},
812   {{"hp8", NULL, NULL},			ENC_HP8,		'\n', 8},
813   {{"koi8", NULL, NULL},		ENC_KOI8,		'\n', 8},
814   {{"ps", "PS", NULL},			ENC_PS, 		'\n', 8},
815   {{"pslatin1", "ISOLatin1Encoding", NULL},	ENC_ISO_8859_1,	'\n', 8},
816 
817   {{NULL, NULL, NULL}, 0, 0, 0},
818 };
819 
820 
821 /*
822  * Static variables.
823  */
824 
825 static struct option long_options[] =
826 {
827   {"columns",			required_argument,	0, 0},
828   {"pages",			required_argument,	0, 'a'},
829   {"file-align",		required_argument,	0, 'A'},
830   {"header",			required_argument,	0, 'b'},
831   {"no-header",			no_argument,		0, 'B'},
832   {"truncate-lines",		no_argument,		0, 'c'},
833   {"line-numbers",		optional_argument,	0, 'C'},
834   {"printer",			required_argument,	0, 'd'},
835   {"setpagedevice",		required_argument,	0, 'D'},
836   {"escapes",			optional_argument,	0, 'e'},
837   {"highlight",			optional_argument,	0, 'E'},
838   {"font",			required_argument,	0, 'f'},
839   {"header-font",		required_argument,	0, 'F'},
840   {"print-anyway",		no_argument,		0, 'g'},
841   {"fancy-header",		optional_argument,	0, 'G'},
842   {"no-job-header",		no_argument, 		0, 'h'},
843   {"highlight-bars",		optional_argument,	0, 'H'},
844   {"indent",			required_argument,	0, 'i'},
845   {"filter",			required_argument,	0, 'I'},
846   {"borders",			no_argument,		0, 'j'},
847   {"page-prefeed",		no_argument,		0, 'k'},
848   {"no-page-prefeed",		no_argument,		0, 'K'},
849   {"lineprinter",		no_argument,		0, 'l'},
850   {"lines-per-page",		required_argument,	0, 'L'},
851   {"mail",			no_argument,		0, 'm'},
852   {"media",			required_argument,	0, 'M'},
853   {"copies",			required_argument,	0, 'n'},
854   {"newline",			required_argument,	0, 'N'},
855   {"output",			required_argument,	0, 'p'},
856   {"missing-characters",	no_argument,		0, 'O'},
857   {"quiet",			no_argument,		0, 'q'},
858   {"silent",			no_argument,		0, 'q'},
859   {"landscape",			no_argument,		0, 'r'},
860   {"portrait",			no_argument,		0, 'R'},
861   {"baselineskip",		required_argument,	0, 's'},
862   {"statusdict",		required_argument,	0, 'S'},
863   {"title",			required_argument,	0, 't'},
864   {"tabsize",			required_argument,	0, 'T'},
865   {"underlay",			optional_argument,	0, 'u'},
866   {"nup",			required_argument,	0, 'U'},
867   {"verbose",			optional_argument,	0, 'v'},
868   {"version",			no_argument,		0, 'V'},
869   {"language",			required_argument,	0, 'w'},
870   {"option",			required_argument,	0, 'W'},
871   {"encoding",			required_argument,	0, 'X'},
872   {"no-formfeed",		no_argument,		0, 'z'},
873   {"pass-through",		no_argument,		0, 'Z'},
874 
875   /* Long options without short counterparts.  Next free is 157. */
876   {"color",			optional_argument,	0, 142},
877   {"continuous-page-numbers",	no_argument,		0, 156},
878   {"download-font",		required_argument,	0, 131},
879   {"extended-return-values",	no_argument,		0, 154},
880   {"filter-stdin",		required_argument,	0, 138},
881   {"footer",			required_argument,	0, 155},
882   {"h-column-height", 		required_argument,	0, 148},
883   {"help", 			no_argument, 		0, 135},
884   {"help-highlight",	 	no_argument, 		0, 141},
885   {"highlight-bar-gray",	required_argument, 	0, 136},
886   {"list-media",		no_argument,		&list_media, 1},
887   {"margins",			required_argument,	0, 144},
888   {"mark-wrapped-lines",	optional_argument,	0, 143},
889   {"non-printable-format",	required_argument,	0, 134},
890   {"nup-columnwise",		no_argument,		0, 152},
891   {"nup-xpad",			required_argument,	0, 145},
892   {"nup-ypad",			required_argument,	0, 146},
893   {"page-label-format",		required_argument,	0, 130},
894   {"ps-level",			required_argument,	0, 149},
895   {"printer-options",		required_argument,	0, 139},
896   {"rotate-even-pages",		no_argument,		0, 150},
897   {"slice",			required_argument,	0, 140},
898   {"style",			required_argument,	0, 151},
899   {"swap-even-page-margins",	no_argument,		0, 153},
900   {"toc",			no_argument,		&toc, 1},
901   {"word-wrap",			no_argument,		0, 147},
902   {"ul-angle",			required_argument,	0, 132},
903   {"ul-font",			required_argument,	0, 128},
904   {"ul-gray",			required_argument,	0, 129},
905   {"ul-position",		required_argument,	0, 133},
906   {"ul-style",			required_argument,	0, 137},
907 
908   /* Backwards compatiblity options. */
909   {"pretty-print",		optional_argument,	0, 'E'},
910   {"help-pretty-print", 	no_argument, 		0, 141},
911 
912   {NULL, 0, 0, 0},
913 };
914 
915 
916 /*
917  * Global functions.
918  */
919 
920 int
main(int argc,char * argv[])921 main (int argc, char *argv[])
922 {
923   InputStream is;
924   time_t tim;
925   struct tm *tm;
926   int i, j, found;
927   unsigned int ui;
928   MediaEntry *mentry;
929   AFMError afm_error;
930   char *cp, *cp2;
931   int retval = 0;
932   Buffer buffer;
933 
934   /* Init our dynamic memory buffer. */
935   buffer_init (&buffer);
936 
937   /* Get program's name. */
938   program = strrchr (argv[0], '/');
939   if (program == NULL)
940     program = argv[0];
941   else
942     program++;
943 
944   /* Make getopt_long() to use our modified programname. */
945   argv[0] = program;
946 
947   /* Create the default TOC format string.  Wow, this is cool! */
948   /* xgettext:no-c-format */
949   toc_fmt_string = _("$3v $-40N $3% pages $4L lines  $E $C");
950 
951   /* Internationalization. */
952 #if HAVE_SETLOCALE
953   /*
954    * We want to change only messages (gs do not like decimals in 0,1
955    * format ;)
956    */
957 #if HAVE_LC_MESSAGES
958   setlocale (LC_MESSAGES, "");
959 #endif
960 #endif
961 #if ENABLE_NLS
962   bindtextdomain (PACKAGE, LOCALEDIR);
963   textdomain (PACKAGE);
964 #endif
965 
966   /* Create date string. */
967 
968   tim = time (NULL);
969   tm = localtime (&tim);
970   memcpy (&run_tm, tm, sizeof (*tm));
971 
972   date_string = xstrdup (asctime (&run_tm));
973   i = strlen (date_string);
974   date_string[i - 1] = '\0';
975 
976   /* Get user's passwd entry. */
977   passwd = getpwuid (getuid ());
978   if (passwd == NULL)
979     FATAL ((stderr, _("couldn't get passwd entry for uid=%d: %s"), getuid (),
980 	    strerror (errno)));
981 
982   /* Defaults for some options. */
983   media_name 		= xstrdup ("A4");
984   encoding_name		= xstrdup ("88591");
985   npf_name		= xstrdup ("octal");
986   page_label_format	= xstrdup ("short");
987   ul_style_str		= xstrdup ("outline");
988   ul_position		= xstrdup ("+0-0");
989   spooler_command 	= xstrdup ("lpr");
990   queue_param 		= xstrdup ("-P");
991   no_job_header_switch 	= xstrdup ("-h");
992   fancy_header_default 	= xstrdup ("enscript");
993   output_first_line 	= xstrdup ("%!PS-Adobe-3.0");
994 
995   /* Check ENSCRIPT_LIBRARY for custom library location. */
996   cp = getenv ("ENSCRIPT_LIBRARY");
997   if (cp)
998     enscript_library = cp;
999 
1000   /* Fill up build-in libpath. */
1001 
1002   cp = getenv ("HOME");
1003   if (cp == NULL)
1004     cp = passwd->pw_dir;
1005 
1006   buffer_clear (&buffer);
1007   buffer_append (&buffer, enscript_library);
1008   buffer_append (&buffer, PATH_SEPARATOR_STR);
1009   buffer_append (&buffer, cp);
1010   buffer_append (&buffer, "/.enscript");
1011   libpath = buffer_copy (&buffer);
1012 
1013   /* Defaults for the states filter. */
1014 
1015   states_binary = xstrdup ("states"); /* Take it from the user path. */
1016 
1017   buffer_clear (&buffer);
1018   buffer_append (&buffer, enscript_library);
1019   buffer_append (&buffer, "/hl/enscript.st");
1020   states_config_file = buffer_copy (&buffer);
1021 
1022   states_highlight_style = xstrdup ("emacs");
1023 
1024   /* The <cp> holds the user's home directory. */
1025   buffer_clear (&buffer);
1026   buffer_append (&buffer, cp);
1027   buffer_append (&buffer, "/.enscript");
1028   buffer_append (&buffer, PATH_SEPARATOR_STR);
1029   buffer_append (&buffer, enscript_library);
1030   buffer_append (&buffer, "/hl");
1031   states_path = buffer_copy (&buffer);
1032 
1033   /* Initialize resource sets. */
1034   res_fonts = strhash_init ();
1035   download_fonts = strhash_init ();
1036   pagedevice = strhash_init ();
1037   statusdict = strhash_init ();
1038   user_strings = strhash_init ();
1039 
1040 
1041   /*
1042    * Read configuration files.
1043    */
1044 
1045   /* Global config. */
1046 #define CFG_FILE_NAME "enscript.cfg"
1047   if (!read_config (SYSCONFDIR, CFG_FILE_NAME))
1048     {
1049       int saved_errno = errno;
1050 
1051       /* Try to read it from our library directory.  This is mostly
1052 	 the case for the micro ports.  */
1053       if (!read_config (enscript_library, CFG_FILE_NAME))
1054 	{
1055 	  /* Try `enscript_library/../../etc/'.  This is the case for
1056              installations which set the prefix after the compilation
1057              and our SYSCONFDIR points to wrong directory. */
1058 
1059 	  buffer_clear (&buffer);
1060 	  buffer_append (&buffer, enscript_library);
1061 	  buffer_append (&buffer, "/../../etc");
1062 
1063 	  if (!read_config (buffer_ptr (&buffer), CFG_FILE_NAME))
1064 	    {
1065 	      /* Maybe we are not installed yet, let's try `../lib'
1066                  and `../../lib'. */
1067 	      if (!read_config ("../lib", CFG_FILE_NAME)
1068 		  && !read_config ("../../lib", CFG_FILE_NAME))
1069 		{
1070 		  /* No luck, report error from the original config file. */
1071 		  ERROR ((stderr, _("couldn't read config file \"%s/%s\": %s"),
1072 			  enscript_library, CFG_FILE_NAME,
1073 			  strerror (saved_errno)));
1074 		  ERROR ((stderr,
1075 			  _("I did also try the following directories:")));
1076 		  ERROR ((stderr, _("\t%s"), SYSCONFDIR));
1077 		  ERROR ((stderr, _("\t%s"), enscript_library));
1078 		  ERROR ((stderr, _("\t%s"), buffer_ptr (&buffer)));
1079 		  ERROR ((stderr, _("\t../lib")));
1080 		  ERROR ((stderr, _("\t../../lib")));
1081 		  ERROR ((stderr,
1082 _("This is probably an installation error.  Please, try to rebuild:")));
1083 		  ERROR ((stderr, _("\tmake distclean")));
1084 		  ERROR ((stderr, _("\t./configure --prefix=PREFIX")));
1085 		  ERROR ((stderr, _("\tmake")));
1086 		  ERROR ((stderr, _("\tmake check")));
1087 		  ERROR ((stderr, _("\tmake install")));
1088 		  ERROR ((stderr, _("or set the environment variable `ENSCRIPT_LIBRARY'"
1089 			" to point to your library directory.")));
1090 		  exit (1);
1091 		}
1092 
1093 	      /* Ok, we are not installed yet.  Here is a small kludge
1094 		 to conform the GNU coding standards: we must be able
1095 		 to run without being installed, so we must append the
1096 		 `../lib' and `../../lib' directories to the libpath.
1097 		 The later allows us to be run form the `src/tests'
1098 		 directory.  */
1099 	      buffer_clear (&buffer);
1100 	      buffer_append (&buffer, libpath);
1101 	      buffer_append (&buffer, PATH_SEPARATOR_STR);
1102 	      buffer_append (&buffer, "../lib");
1103 	      buffer_append (&buffer, PATH_SEPARATOR_STR);
1104 	      buffer_append (&buffer, "../../lib");
1105 
1106 	      xfree (libpath);
1107 	      libpath = buffer_copy (&buffer);
1108 	    }
1109 	}
1110     }
1111 
1112   /* Site config. */
1113   read_config (SYSCONFDIR, "enscriptsite.cfg");
1114 
1115   /* Personal config. */
1116   read_config (cp, ".enscriptrc");
1117 
1118   /*
1119    * Options.
1120    */
1121 
1122   /* Environment variables. */
1123   handle_env_options ("ENSCRIPT");
1124   handle_env_options ("GENSCRIPT");
1125 
1126   /* Command line arguments. */
1127   handle_options (argc, argv);
1128 
1129   /*
1130    * Check options which have some validity conditions.
1131    */
1132 
1133   /*
1134    * Save the user-specified escape char so ^@escape{default} knows
1135    * what to set.
1136    */
1137   default_escape_char = escape_char;
1138 
1139   /* Input encoding. */
1140 
1141   found = 0;
1142   for (i = 0; !found && encodings[i].names[0]; i++)
1143     for (j = 0; j < 3; j++)
1144       if (encodings[i].names[j] != NULL && MATCH (encodings[i].names[j],
1145 						  encoding_name))
1146 	{
1147 	  /* Found a match for this encoding.  Use the first
1148              "official" name. */
1149 
1150 	  encoding = encodings[i].encoding;
1151 	  xfree (encoding_name);
1152 	  encoding_name = xstrdup (encodings[i].names[0]);
1153 
1154 	  if (nl < 0)
1155 	    nl = encodings[i].nl;
1156 	  bs = encodings[i].bs;
1157 	  found = 1;
1158 	  break;
1159 	}
1160   if (!found)
1161     FATAL ((stderr, _("unknown encoding: %s"), encoding_name));
1162 
1163   /* Fonts. */
1164 
1165   /* Default font for landscape, 2 column printing is Courier 7. */
1166   if (!user_body_font_defined && landscape && num_columns > 1)
1167     Fpt.w = Fpt.h = 7.0;
1168 
1169   /* Cache for font AFM information. */
1170   afm_cache = strhash_init ();
1171   afm_info_cache = strhash_init ();
1172 
1173   /* Open AFM library. */
1174   afm_error = afm_create (afm_path, verbose, &afm);
1175   if (afm_error != AFM_SUCCESS)
1176     {
1177       char buf[256];
1178 
1179       afm_error_to_string (afm_error, buf);
1180       FATAL ((stderr, _("couldn't open AFM library: %s"), buf));
1181     }
1182 
1183   /*
1184    * Save default Fpt and Fname since special escape 'font' can change
1185    * it and later we might want to switch back to the "default" font.
1186    */
1187   default_Fpt.w = Fpt.w;
1188   default_Fpt.h = Fpt.h;
1189   default_Fname = Fname;
1190   default_Fencoding = encoding;
1191 
1192   /* Register that document uses at least these fonts. */
1193   strhash_put (res_fonts, Fname, strlen (Fname) + 1, NULL, NULL);
1194   strhash_put (res_fonts, HFname, strlen (HFname) + 1, NULL, NULL);
1195 
1196   /* As a default, download both named fonts. */
1197   strhash_put (download_fonts, Fname, strlen (Fname) + 1, NULL, NULL);
1198   strhash_put (download_fonts, HFname, strlen (HFname) + 1, NULL, NULL);
1199 
1200   /* Read font's character widths and character types. */
1201   read_font_info ();
1202 
1203   /* Count the line indentation. */
1204   line_indent = parse_float (line_indent_spec, 1, 1);
1205 
1206   /* List media names. */
1207   if (list_media)
1208     {
1209       printf (_("known media:\n\
1210 name             width\theight\tllx\tlly\turx\tury\n\
1211 ------------------------------------------------------------\n"));
1212       for (mentry = media_names; mentry; mentry = mentry->next)
1213 	printf ("%-16s %d\t%d\t%d\t%d\t%d\t%d\n",
1214 		mentry->name, mentry->w, mentry->h,
1215 		mentry->llx, mentry->lly, mentry->urx, mentry->ury);
1216       /* Exit after listing. */
1217       exit (0);
1218     }
1219 
1220   /* Output media. */
1221   for (mentry = media_names; mentry; mentry = mentry->next)
1222     if (strcmp (media_name, mentry->name) == 0)
1223       {
1224 	media = mentry;
1225 	break;
1226       }
1227   if (media == NULL)
1228     FATAL ((stderr, _("do not know anything about media \"%s\""), media_name));
1229 
1230   if (margins_spec)
1231     {
1232       /* Adjust marginals. */
1233       for (i = 0; i < 4; i++)
1234 	{
1235 	  if (*margins_spec == '\0')
1236 	    /* All done. */
1237 	    break;
1238 
1239 	  if (*margins_spec == ':')
1240 	    {
1241 	      margins_spec++;
1242 	      continue;
1243 	    }
1244 
1245 	  j = atoi (margins_spec);
1246 	  for (; *margins_spec != ':' && *margins_spec != '\0'; margins_spec++)
1247 	    ;
1248 	  if (*margins_spec == ':')
1249 	    margins_spec++;
1250 
1251 	  switch (i)
1252 	    {
1253 	    case 0:		/* left */
1254 	      media->llx = j;
1255 	      break;
1256 
1257 	    case 1:		/* right */
1258 	      media->urx = media->w - j;
1259 	      break;
1260 
1261 	    case 2:		/* top */
1262 	      media->ury = media->h - j;
1263 	      break;
1264 
1265 	    case 3:		/* bottom */
1266 	      media->lly = j;
1267 	      break;
1268 	    }
1269 	}
1270       MESSAGE (1,
1271 	       (stderr,
1272 		_("set new marginals for media `%s' (%dx%d): llx=%d, lly=%d, urx=%d, ury=%d\n"),
1273 		media->name, media->w, media->h, media->llx, media->lly,
1274 		media->urx, media->ury));
1275     }
1276 
1277   /* Page label format. */
1278   if (MATCH (page_label_format, "short"))
1279     page_label = LABEL_SHORT;
1280   else if (MATCH (page_label_format, "long"))
1281     page_label = LABEL_LONG;
1282   else
1283     FATAL ((stderr, _("illegal page label format \"%s\""), page_label_format));
1284 
1285   /* Non-printable format. */
1286   if (MATCH (npf_name, "space"))
1287     non_printable_format = NPF_SPACE;
1288   else if (MATCH (npf_name, "questionmark"))
1289     non_printable_format = NPF_QUESTIONMARK;
1290   else if (MATCH (npf_name, "caret"))
1291     non_printable_format = NPF_CARET;
1292   else if (MATCH (npf_name, "octal"))
1293     non_printable_format = NPF_OCTAL;
1294   else
1295     FATAL ((stderr, _("illegal non-printable format \"%s\""), npf_name));
1296 
1297   /* Mark wrapped lines style. */
1298   if (mark_wrapped_lines_style_name)
1299     {
1300       if (MATCH (mark_wrapped_lines_style_name, "none"))
1301 	mark_wrapped_lines_style = MWLS_NONE;
1302       else if (MATCH (mark_wrapped_lines_style_name, "plus"))
1303 	mark_wrapped_lines_style = MWLS_PLUS;
1304       else if (MATCH (mark_wrapped_lines_style_name, "box"))
1305 	mark_wrapped_lines_style = MWLS_BOX;
1306       else if (MATCH (mark_wrapped_lines_style_name, "arrow"))
1307 	mark_wrapped_lines_style = MWLS_ARROW;
1308       else
1309 	FATAL ((stderr, _("illegal style for wrapped line marker: \"%s\""),
1310 		mark_wrapped_lines_style_name));
1311     }
1312 
1313   /* Count N-up stuffs. */
1314   for (i = 0; ; i++)
1315     {
1316       ui = nup >> i;
1317 
1318       if (ui == 0)
1319 	FATAL ((stderr, _("illegal N-up argument: %d"), nup));
1320 
1321       if (ui & 0x1)
1322 	{
1323 	  if (ui != 1)
1324 	    FATAL ((stderr, _("N-up argument must be power of 2: %d"), nup));
1325 
1326 	  nup_exp = i;
1327 	  break;
1328 	}
1329     }
1330 
1331   nup_rows = nup_exp / 2 * 2;
1332   if (nup_rows == 0)
1333     nup_rows = 1;
1334   nup_columns = (nup_exp + 1) / 2 * 2;
1335   if (nup_columns == 0)
1336     nup_columns = 1;
1337 
1338   nup_landscape = nup_exp & 0x1;
1339 
1340 
1341   /*
1342    * Count output media dimensions.
1343    */
1344 
1345   if (landscape)
1346     {
1347       d_page_w = media->ury - media->lly;
1348       d_page_h = media->urx - media->llx;
1349     }
1350   else
1351     {
1352       d_page_w = media->urx - media->llx;
1353       d_page_h = media->ury - media->lly;
1354     }
1355 
1356   /*
1357    * Count N-up page width, height and scale.
1358    */
1359 
1360   if (nup_landscape)
1361     {
1362       nup_width = media->ury - media->lly;
1363       nup_height = media->urx - media->llx;
1364     }
1365   else
1366     {
1367       nup_width = media->urx - media->llx;
1368       nup_height = media->ury - media->lly;
1369     }
1370 
1371   {
1372     double w, h;
1373 
1374     w = ((double) nup_width - (nup_columns - 1) * nup_xpad) / nup_columns;
1375     h = ((double) nup_height - (nup_rows - 1) * nup_ypad) / nup_rows;
1376 
1377     nup_width = w;
1378     nup_height = h;
1379 
1380     w = w / (media->urx - media->llx);
1381     h = h / (media->ury - media->lly);
1382 
1383     nup_scale = w < h ? w : h;
1384   }
1385 
1386   /*
1387    * Underlay (this must come after output media dimensions, because
1388    * `underlay position' needs them).
1389    */
1390   if (underlay != NULL)
1391     {
1392       strhash_put (res_fonts, ul_font, strlen (ul_font) + 1, NULL, NULL);
1393       underlay = escape_string (underlay);
1394     }
1395 
1396   /* Underlay X-coordinate. */
1397   ul_x = strtod (ul_position, &cp);
1398   if (cp == ul_position)
1399     {
1400     malformed_position:
1401       FATAL ((stderr, _("malformed underlay position: %s"), ul_position));
1402     }
1403   if (ul_position[0] == '-')
1404     ul_x += d_page_w;
1405 
1406   /* Underlay Y-coordinate. */
1407   ul_y = strtod (cp, &cp2);
1408   if (cp2 == cp)
1409     goto malformed_position;
1410   if (cp[0] == '-')
1411     ul_y += d_page_h;
1412 
1413   /* Underlay Angle. */
1414   if (!ul_angle_p)
1415     /* No angle given, count the default. */
1416     ul_angle = (atan2 (-d_page_h, d_page_w) / 3.14159265 * 180);
1417 
1418   /* Underlay style. */
1419   if (strcmp (ul_style_str, "outline") == 0)
1420     ul_style = UL_STYLE_OUTLINE;
1421   else if (strcmp (ul_style_str, "filled") == 0)
1422     ul_style = UL_STYLE_FILLED;
1423   else
1424     FATAL ((stderr, _("illegal underlay style: %s"), ul_style_str));
1425 
1426   /*
1427    * Header.  Note! The header attributes can be changed from
1428    * the `.hdr' files, these are only the defaults.
1429    */
1430 
1431   d_header_w = d_page_w;
1432   switch (header)
1433     {
1434     case HDR_NONE:
1435       d_header_h = 0;
1436       break;
1437 
1438     case HDR_SIMPLE:
1439       d_header_h = HFpt.h * 1.5;
1440       break;
1441 
1442     case HDR_FANCY:
1443       d_header_h = 36;
1444       break;
1445     }
1446 
1447   /* Help highlight. */
1448   if (help_highlight)
1449     {
1450       /* Create description with states. */
1451       printf (_("Highlighting is supported for the following languages and file formats:\n\n"));
1452       fflush (stdout);
1453 
1454       buffer_clear (&buffer);
1455       buffer_append (&buffer, states_binary);
1456       buffer_append (&buffer, " -f \"");
1457       buffer_append (&buffer, states_config_file);
1458       buffer_append (&buffer, "\" -p \"");
1459       buffer_append (&buffer, states_path);
1460       buffer_append (&buffer, "\" -s describe_languages ");
1461       buffer_append (&buffer, enscript_library);
1462       buffer_append (&buffer, "/hl/*.st");
1463 
1464       system (buffer_ptr (&buffer));
1465       exit (0);
1466     }
1467 
1468   /*
1469    * And now to the main business.  The actual input file processing
1470    * is divided to two parts: PostScript generation and everything else.
1471    * The PostScript generation is handled in the conventional way, we
1472    * process the input and generate PostScript.  However all other input
1473    * languages will be handled with States, we only pass enscript's
1474    * options to the states pre-filter and dump output.
1475    */
1476   if (output_language_pass_through)
1477     {
1478       char *start_state;
1479       Buffer cmd;
1480       char intbuf[256];
1481 
1482       /* The States output generation. */
1483 
1484       /* Resolve the start state. */
1485       if (hl_start_state)
1486 	start_state = hl_start_state;
1487       else if (highlight)
1488 	start_state = NULL;
1489       else
1490 	start_state = "passthrough";
1491 
1492       /* Create the states command. */
1493 
1494       buffer_init (&cmd);
1495 
1496       buffer_append (&cmd, states_binary);
1497       buffer_append (&cmd, " -f \"");
1498       buffer_append (&cmd, states_config_file);
1499       buffer_append (&cmd, "\" -p \"");
1500       buffer_append (&cmd, states_path);
1501       buffer_append (&cmd, "\" ");
1502 
1503       if (verbose > 0)
1504 	buffer_append (&cmd, "-v ");
1505 
1506       if (start_state)
1507 	{
1508 	  buffer_append (&cmd, "-s");
1509 	  buffer_append (&cmd, start_state);
1510 	  buffer_append (&cmd, " ");
1511 	}
1512 
1513       buffer_append (&cmd, "-Dcolor=");
1514       buffer_append (&cmd, states_color ? "1" : "0");
1515       buffer_append (&cmd, " ");
1516 
1517       buffer_append (&cmd, "-Dstyle=");
1518       buffer_append (&cmd, states_highlight_style);
1519       buffer_append (&cmd, " ");
1520 
1521       buffer_append (&cmd, "-Dlanguage=");
1522       buffer_append (&cmd, output_language);
1523       buffer_append (&cmd, " ");
1524 
1525       buffer_append (&cmd, "-Dnum_input_files=");
1526       sprintf (intbuf, "%d", optind == argc ? 1 : argc - optind);
1527       buffer_append (&cmd, intbuf);
1528       buffer_append (&cmd, " ");
1529 
1530       buffer_append (&cmd, "-Ddocument_title=\'");
1531       if ((cp = shell_escape (title)) != NULL)
1532 	{
1533 	  buffer_append (&cmd, cp);
1534 	  free (cp);
1535 	}
1536       buffer_append (&cmd, "\' ");
1537 
1538       buffer_append (&cmd, "-Dtoc=");
1539       buffer_append (&cmd, toc ? "1" : "0");
1540 
1541       /* Additional options for states? */
1542       if (helper_options['s'])
1543 	{
1544 	  Buffer *opts = helper_options['s'];
1545 
1546 	  buffer_append (&cmd, " ");
1547 	  buffer_append_len (&cmd, buffer_ptr (opts), buffer_len (opts));
1548 	}
1549 
1550       /* Append input files. */
1551       for (i = optind; i < argc; i++)
1552 	{
1553 	  char *cp;
1554 	  if ((cp = shell_escape (argv[i])) != NULL)
1555 	    {
1556 	      buffer_append (&cmd, " \'");
1557 	      buffer_append (&cmd, cp);
1558 	      buffer_append (&cmd, "\'");
1559 	      free (cp);
1560 	    }
1561 	}
1562 
1563       /* And do the job. */
1564       if (is_open (&is, stdin, NULL, buffer_ptr (&cmd)))
1565 	{
1566 	  open_output_file ();
1567 	  process_file ("unused", &is, 0);
1568 	  is_close (&is);
1569 	}
1570 
1571       buffer_uninit (&cmd);
1572     }
1573   else
1574     {
1575       /* The conventional way. */
1576 
1577       /* Highlighting. */
1578       if (highlight)
1579 	{
1580 	  char fbuf[256];
1581 
1582 	  /* Create a highlight input filter. */
1583 	  buffer_clear (&buffer);
1584 	  buffer_append (&buffer, states_binary);
1585 	  buffer_append (&buffer, " -f \"");
1586 	  buffer_append (&buffer, states_config_file);
1587 	  buffer_append (&buffer, "\" -p \"");
1588 	  buffer_append (&buffer, states_path);
1589 	  buffer_append (&buffer, "\"");
1590 
1591 	  if (verbose > 0)
1592 	    buffer_append (&buffer, " -v");
1593 
1594 	  if (hl_start_state)
1595 	    {
1596 	      buffer_append (&buffer, " -s ");
1597 	      buffer_append (&buffer, hl_start_state);
1598 	    }
1599 
1600 	  buffer_append (&buffer, " -Dcolor=");
1601 	  buffer_append (&buffer, states_color ? "1" : "0");
1602 
1603 	  buffer_append (&buffer, " -Dstyle=");
1604 	  buffer_append (&buffer, states_highlight_style);
1605 
1606 	  buffer_append (&buffer, " -Dfont_spec=");
1607 	  buffer_append (&buffer, Fname);
1608 	  sprintf (fbuf, "@%g/%g", Fpt.w, Fpt.h);
1609 	  buffer_append (&buffer, fbuf);
1610 
1611 	  /* Additional options for states? */
1612 	  if (helper_options['s'])
1613 	    {
1614 	      Buffer *opts = helper_options['s'];
1615 
1616 	      buffer_append (&buffer, " ");
1617 	      buffer_append_len (&buffer,
1618 				 buffer_ptr (opts), buffer_len (opts));
1619 	    }
1620 
1621 	  buffer_append (&buffer, " \'%s\'");
1622 
1623 	  input_filter = buffer_copy (&buffer);
1624 	  input_filter_stdin = "-";
1625 	}
1626 
1627       /* Table of Contents. */
1628       if (toc)
1629 	{
1630 	  toc_fp = tmpfile ();
1631 	  if (toc_fp == NULL)
1632 	    FATAL ((stderr, _("couldn't create temporary toc file: %s"),
1633 		    strerror (errno)));
1634 	}
1635 
1636 
1637       /*
1638        * Process files.
1639        */
1640 
1641       if (optind == argc)
1642 	{
1643 	  /* stdin's modification time is the current time. */
1644 	  memcpy (&mod_tm, &run_tm, sizeof (run_tm));
1645 
1646 	  if (is_open (&is, stdin, NULL, input_filter))
1647 	    {
1648 	      /* Open output file. */
1649 	      open_output_file ();
1650 	      process_file (title_given ? title : "", &is, 0);
1651 	      is_close (&is);
1652 	    }
1653 	}
1654       else
1655 	{
1656 	  for (; optind < argc; optind++)
1657 	    {
1658 	      if (is_open (&is, NULL, argv[optind], input_filter))
1659 		{
1660 		  struct stat stat_st;
1661 
1662 		  /* Get modification time. */
1663 		  if (stat (argv[optind], &stat_st) == 0)
1664 		    {
1665 		      tim = stat_st.st_mtime;
1666 		      tm = localtime (&tim);
1667 		      memcpy (&mod_tm, tm, sizeof (*tm));
1668 
1669 		      /*
1670 		       * Open output file.  Output file opening is delayed to
1671 		       * this point so we can optimize the case when a
1672 		       * non-existing input file is printed => we do nothing.
1673 		       */
1674 		      open_output_file ();
1675 
1676 		      process_file (argv[optind], &is, 0);
1677 		    }
1678 		  else
1679 		    ERROR ((stderr, _("couldn't stat input file \"%s\": %s"),
1680 			    argv[optind],
1681 			    strerror (errno)));
1682 
1683 		  is_close (&is);
1684 		}
1685 	    }
1686 	}
1687 
1688       /* Table of Contents. */
1689       if (toc)
1690 	{
1691 	  /* This is really cool... */
1692 
1693 	  /* Set the printing options for toc. */
1694 	  toc = 0;
1695 	  special_escapes = 1;
1696 	  line_numbers = 0;
1697 
1698 	  if (fseek (toc_fp, 0, SEEK_SET) != 0)
1699 	    FATAL ((stderr, _("couldn't rewind toc file: %s"),
1700 		    strerror (errno)));
1701 
1702 	  memcpy (&mod_tm, &run_tm, sizeof (run_tm));
1703 	  if (is_open (&is, toc_fp, NULL, NULL))
1704 	    {
1705 	      process_file (_("Table of Contents"), &is, 1);
1706 	      is_close (&is);
1707 	    }
1708 	}
1709 
1710       /* Give trailer a chance to dump itself. */
1711       dump_ps_trailer ();
1712 
1713       /*
1714        * Append ^D to the end of the output?  Note! It must be ^D followed
1715        * by a newline.
1716        */
1717       if (ofp != NULL && append_ctrl_D)
1718 	fprintf (ofp, "\004\n");
1719     }
1720 
1721   /* Close output file. */
1722   close_output_file ();
1723 
1724   /* Tell how things went. */
1725   if (ofp == NULL)
1726     {
1727       /*
1728        * The value of <ofp> is not reset in close_output_file(),
1729        * this is ugly but it saves one flag.
1730        */
1731       MESSAGE (0, (stderr, _("no output generated\n")));
1732     }
1733   else if (output_language_pass_through)
1734     {
1735       if (output_file == OUTPUT_FILE_NONE)
1736 	MESSAGE (0, (stderr, _("output sent to %s\n"),
1737 		     printer ? printer : _("printer")));
1738       else
1739 	MESSAGE (0, (stderr, _("output left in %s\n"),
1740 		     output_file == OUTPUT_FILE_STDOUT ? "-" : output_file));
1741     }
1742   else
1743     {
1744       unsigned int real_total_pages;
1745 
1746       if (nup > 1)
1747 	{
1748 	  if (total_pages > 0)
1749 	    real_total_pages = (total_pages - 1) / nup + 1;
1750 	  else
1751 	    real_total_pages = 0;
1752 	}
1753       else
1754 	real_total_pages = total_pages;
1755 
1756       /* We did something, tell what.  */
1757       char message[80];
1758       snprintf(message, sizeof message, "%s%s%s%s%s",
1759 	       "[ ",
1760 	       ngettext("%d page", "%d pages", real_total_pages),
1761 	       " * ",
1762 	       ngettext("%d copy", "%d copies", num_copies),
1763 	       " ]");
1764       MESSAGE (0, (stderr, message, real_total_pages, num_copies));
1765 
1766       if (output_file == OUTPUT_FILE_NONE)
1767 	MESSAGE (0, (stderr, _(" sent to %s\n"),
1768 		     printer ? printer : _("printer")));
1769       else
1770 	MESSAGE (0, (stderr, _(" left in %s\n"),
1771 		     output_file == OUTPUT_FILE_STDOUT ? "-" : output_file));
1772       if (num_truncated_lines)
1773 	{
1774 	  retval |= 2;
1775 	  MESSAGE (0, (stderr,
1776 		       ngettext("%d line was %s\n",
1777 				"%d lines were %s\n",
1778 				num_truncated_lines),
1779 		       num_truncated_lines,
1780 		       line_end == LE_TRUNCATE
1781 		       ? _("truncated") : _("wrapped")));
1782 	}
1783 
1784       if (num_missing_chars)
1785 	{
1786 	  retval |= 4;
1787 	  MESSAGE (0, (stderr,
1788 		       ngettext("%d character was missing\n",
1789 				"%d characters were missing\n",
1790 				num_missing_chars),
1791 		       num_missing_chars));
1792 	  if (list_missing_characters)
1793 	    {
1794 	      MESSAGE (0, (stderr, _("missing character codes (decimal):\n")));
1795 	      do_list_missing_characters (missing_chars);
1796 	    }
1797 	}
1798 
1799       if (num_non_printable_chars)
1800 	{
1801 	  retval |= 8;
1802 	  MESSAGE (0, (stderr,
1803 		       ngettext("%d non-printable character\n",
1804 				"%d non-printable characters\n",
1805 				num_non_printable_chars),
1806 		       num_non_printable_chars));
1807 	  if (list_missing_characters)
1808 	    {
1809 	      MESSAGE (0, (stderr,
1810 			   _("non-printable character codes (decimal):\n")));
1811 	      do_list_missing_characters (non_printable_chars);
1812 	    }
1813 	}
1814     }
1815 
1816   /* Uninit our dynamic memory buffer. */
1817   buffer_uninit (&buffer);
1818 
1819   /* Return the extended return values only if requested. */
1820   if (!extended_return_values)
1821     retval = 0;
1822 
1823   /* This is the end. */
1824   return retval;
1825 }
1826 
1827 
1828 /*
1829  * Static functions.
1830  */
1831 
1832 static void
open_output_file()1833 open_output_file ()
1834 {
1835   if (ofp)
1836     /* Output file has already been opened, do nothing. */
1837     return;
1838 
1839   if (output_file == OUTPUT_FILE_NONE)
1840     {
1841       char spooler_options[512];
1842 
1843       /* Format spooler options. */
1844       spooler_options[0] = '\0';
1845       if (mail)
1846 	strcat (spooler_options, "-m ");
1847       if (no_job_header)
1848 	{
1849 	  strcat (spooler_options, no_job_header_switch);
1850 	  strcat (spooler_options, " ");
1851 	}
1852       if (printer_options)
1853 	strcat (spooler_options, printer_options);
1854 
1855       /* Open printer. */
1856       ofp = printer_open (spooler_command, spooler_options, queue_param,
1857 			  printer, &printer_context);
1858       if (ofp == NULL)
1859 	FATAL ((stderr, _("couldn't open printer `%s': %s"), printer,
1860 		strerror (errno)));
1861     }
1862   else if (output_file == OUTPUT_FILE_STDOUT)
1863     ofp = stdout;
1864   else
1865     {
1866       ofp = fopen (output_file, "w");
1867       if (ofp == NULL)
1868 	FATAL ((stderr, _("couldn't create output file \"%s\": %s"),
1869 		output_file, strerror (errno)));
1870     }
1871 }
1872 
1873 
1874 static void
close_output_file()1875 close_output_file ()
1876 {
1877   if (ofp == NULL)
1878     /* Output file hasn't been opened, we are done. */
1879     return;
1880 
1881   if (output_file == OUTPUT_FILE_NONE)
1882     printer_close (printer_context);
1883   else if (output_file != OUTPUT_FILE_STDOUT)
1884     if (fclose (ofp))
1885       FATAL ((stderr, _("couldn't close output file \"%s\": %s"),
1886 	      output_file, strerror (errno)));
1887 
1888   /* We do not reset <ofp> since its value is needed in diagnostigs. */
1889 }
1890 
1891 
1892 static void
handle_env_options(char * var)1893 handle_env_options (char *var)
1894 {
1895   int argc;
1896   char **argv;
1897   char *string;
1898   char *str;
1899   int i;
1900 
1901   string = getenv (var);
1902   if (string == NULL)
1903     return;
1904 
1905   MESSAGE (2, (stderr, "handle_env_options(): %s=\"%s\"\n", var, string));
1906 
1907   /* Copy string so we can modify it in place. */
1908   str = xstrdup (string);
1909 
1910   /*
1911    * We can count this, each option takes at least 1 character and one
1912    * space.  We also need one for program's name and one for the
1913    * trailing NULL.
1914    */
1915   argc = (strlen (str) + 1) / 2 + 2;
1916   argv = xcalloc (argc, sizeof (char *));
1917 
1918   /* Set program name. */
1919   argc = 0;
1920   argv[argc++] = program;
1921 
1922   /* Split string and set arguments to argv array. */
1923   i = 0;
1924   while (str[i])
1925     {
1926       /* Skip leading whitespace. */
1927       for (; str[i] && isspace (str[i]); i++)
1928 	;
1929       if (!str[i])
1930 	break;
1931 
1932       /* Check for quoted arguments. */
1933       if (str[i] == '"' || str[i] == '\'')
1934 	{
1935 	  int endch = str[i++];
1936 
1937 	  argv[argc++] = str + i;
1938 
1939 	  /* Skip until we found the end of the quotation. */
1940 	  for (; str[i] && str[i] != endch; i++)
1941 	    ;
1942 	  if (!str[i])
1943 	    FATAL ((stderr, _("syntax error in option string %s=\"%s\":\n\
1944 missing end of quotation: %c"), var, string, endch));
1945 
1946 	  str[i++] = '\0';
1947 	}
1948       else
1949 	{
1950 	  argv[argc++] = str + i;
1951 
1952 	  /* Skip until whitespace if found. */
1953 	  for (; str[i] && !isspace (str[i]); i++)
1954 	    ;
1955 	  if (str[i])
1956 	    str[i++] = '\0';
1957 	}
1958     }
1959 
1960   /* argv[argc] must be NULL. */
1961   argv[argc] = NULL;
1962 
1963   MESSAGE (2, (stderr, "found following options (argc=%d):\n", argc));
1964   for (i = 0; i < argc; i++)
1965     MESSAGE (2, (stderr, "%3d = \"%s\"\n", i, argv[i]));
1966 
1967   /* Process options. */
1968   handle_options (argc, argv);
1969 
1970   /* Check that all got processed. */
1971   if (optind != argc)
1972     {
1973       MESSAGE (0,
1974 	       (stderr,
1975 		_("warning: didn't process following options from \
1976 environment variable %s:\n"),
1977 		var));
1978       for (; optind < argc; optind++)
1979 	MESSAGE (0, (stderr, _("  option %d = \"%s\"\n"), optind,
1980 		     argv[optind]));
1981     }
1982 
1983   /* Cleanup. */
1984   xfree (argv);
1985 
1986   /*
1987    * <str> must not be freed, since some global variables can point to
1988    * its elements
1989    */
1990 }
1991 
1992 
1993 static void
handle_options(int argc,char * argv[])1994 handle_options (int argc, char *argv[])
1995 {
1996   int c;
1997   PageRange *prange;
1998 
1999   /* Reset optind. */
2000   optind = 0;
2001 
2002   while (1)
2003     {
2004       int option_index = 0;
2005       const char *cp;
2006       int i;
2007 
2008       c = getopt_long (argc, argv,
2009 		       "#:123456789a:A:b:BcC::d:D:e::E::f:F:gGhH::i:I:jJ:kKlL:mM:n:N:o:Op:P:qrRs:S:t:T:u::U:vVw:W:X:zZ",
2010 		       long_options, &option_index);
2011 
2012       if (c == -1)
2013 	break;
2014 
2015       switch (c)
2016 	{
2017 	case 0:			/* Long option found. */
2018 	  cp = long_options[option_index].name;
2019 
2020 	  if (strcmp (cp, "columns") == 0)
2021 	    {
2022 	      num_columns = atoi (optarg);
2023 	      if (num_columns < 1)
2024 		FATAL ((stderr,
2025 			_("number of columns must be larger than zero")));
2026 	    }
2027 	  break;
2028 
2029 	  /* Short options. */
2030 
2031 	case '1':		/* 1 column */
2032 	case '2':		/* 2 columns */
2033 	case '3':		/* 3 columns */
2034 	case '4':		/* 4 columns */
2035 	case '5':		/* 5 columns */
2036 	case '6':		/* 6 columns */
2037 	case '7':		/* 7 columns */
2038 	case '8':		/* 8 columns */
2039 	case '9':		/* 9 columns */
2040 	  num_columns = c - '0';
2041 	  break;
2042 
2043 	case 'a':		/* pages */
2044 	  prange = (PageRange *) xcalloc (1, sizeof (PageRange));
2045 
2046 	  if (strcmp (optarg, "odd") == 0)
2047 	    prange->odd = 1;
2048 	  else if (strcmp (optarg, "even") == 0)
2049 	    prange->even = 1;
2050 	  else
2051 	    {
2052 	      cp = strchr (optarg, '-');
2053 	      if (cp)
2054 		{
2055 		  if (optarg[0] == '-')
2056 		    /* -end */
2057 		    prange->end = atoi (optarg + 1);
2058 		  else if (cp[1] == '\0')
2059 		    {
2060 		      /* begin- */
2061 		      prange->start = atoi (optarg);
2062 		      prange->end = (unsigned int) -1;
2063 		    }
2064 		  else
2065 		    {
2066 		      /* begin-end */
2067 		      prange->start = atoi (optarg);
2068 		      prange->end = atoi (cp + 1);
2069 		    }
2070 		}
2071 	      else
2072 		/* pagenumber */
2073 		prange->start = prange->end = atoi (optarg);
2074 	    }
2075 
2076 	  prange->next = page_ranges;
2077 	  page_ranges = prange;
2078 	  break;
2079 
2080 	case 'A':		/* file alignment */
2081 	  file_align = atoi (optarg);
2082 	  if (file_align == 0)
2083 	    FATAL ((stderr, _("file alignment must be larger than zero")));
2084 	  break;
2085 
2086 	case 'b':		/* page header */
2087 	  page_header = optarg;
2088 	  break;
2089 
2090 	case 'B':		/* no page headers */
2091 	  header = HDR_NONE;
2092 	  break;
2093 
2094 	case 'c':		/* truncate (cut) long lines */
2095 	  line_end = LE_TRUNCATE;
2096 	  break;
2097 
2098 	case 'C':		/* line numbers */
2099 	  line_numbers = 1;
2100 	  if (optarg)
2101 	    start_line_number = atoi (optarg);
2102 	  break;
2103 
2104 	case 'd':		/* specify printer */
2105 	case 'P':
2106 	  xfree (printer);
2107 	  printer = xstrdup (optarg);
2108 	  output_file = OUTPUT_FILE_NONE;
2109 	  break;
2110 
2111 	case 'D':		/* setpagedevice */
2112 	  parse_key_value_pair (pagedevice, optarg);
2113 	  break;
2114 
2115 	case 'e':		/* special escapes */
2116 	  special_escapes = 1;
2117 	  if (optarg)
2118 	    {
2119 	      /* Specify the escape character. */
2120 	      if (isdigit (optarg[0]))
2121 		/* As decimal, octal, or hexadicimal number. */
2122 		escape_char = (int) strtoul (optarg, NULL, 0);
2123 	      else
2124 		/* As character directly. */
2125 		escape_char = ((unsigned char *) optarg)[0];
2126 	    }
2127 	  break;
2128 
2129 	case 'E':		/* highlight */
2130 	  highlight = 1;
2131 	  special_escapes = 1;
2132 	  escape_char = '\0';
2133 	  hl_start_state = optarg;
2134 	  break;
2135 
2136 	case 'f':		/* font */
2137 	  if (!parse_font_spec (optarg, &Fname, &Fpt, NULL))
2138 	    FATAL ((stderr, _("malformed font spec: %s"), optarg));
2139 	  user_body_font_defined = 1;
2140 	  break;
2141 
2142 	case 'F':		/* header font */
2143 	  if (!parse_font_spec (optarg, &HFname, &HFpt, NULL))
2144 	    FATAL ((stderr, _("malformed font spec: %s"), optarg));
2145 	  break;
2146 
2147 	case 'g':		/* print anyway */
2148 	  /* nothing. */
2149 	  break;
2150 
2151 	case 'G':		/* fancy header */
2152 	  header = HDR_FANCY;
2153 	  if (optarg)
2154 	    fancy_header_name = optarg;
2155 	  else
2156 	    fancy_header_name = fancy_header_default;
2157 
2158 	  if (!file_existsp (fancy_header_name, ".hdr"))
2159 	    FATAL ((stderr,
2160 		    _("couldn't find header definition file \"%s.hdr\""),
2161 		    fancy_header_name));
2162 	  break;
2163 
2164 	case 'h':		/* no job header */
2165 	  no_job_header = 1;
2166 	  break;
2167 
2168 	case 'H':		/* highlight bars */
2169 	  if (optarg)
2170 	    highlight_bars = atoi (optarg);
2171 	  else
2172 	    highlight_bars = 2;
2173 	  break;
2174 
2175 	case 'i':		/* line indent */
2176 	  line_indent_spec = optarg;
2177 	  break;
2178 
2179 	case 'I':		/* input filter */
2180 	  input_filter = optarg;
2181 	  break;
2182 
2183 	case 'j':		/* borders */
2184 	  borders = 1;
2185 	  break;
2186 
2187 	case 'k':		/* enable page prefeed */
2188 	  page_prefeed = 1;
2189 	  break;
2190 
2191 	case 'K':		/* disable page prefeed */
2192 	  page_prefeed = 0;
2193 	  break;
2194 
2195 	case 'l':		/* emulate lineprinter */
2196 	  lines_per_page = 66;
2197 	  header = HDR_NONE;
2198 	  break;
2199 
2200 	case 'L':		/* lines per page */
2201 	  lines_per_page = atoi (optarg);
2202 	  if (lines_per_page <= 0)
2203 	    FATAL ((stderr,
2204 		    _("must print at least one line per each page: %s"),
2205 		    argv[optind]));
2206 	  break;
2207 
2208 	case 'm':		/* send mail upon completion */
2209 	  mail = 1;
2210 	  break;
2211 
2212 	case 'M':		/* select output media */
2213 	  media_name = xstrdup (optarg);
2214 	  break;
2215 
2216 	case 'n':		/* num copies */
2217 	case '#':
2218 	  num_copies = atoi (optarg);
2219 	  break;
2220 
2221 	case 'N':		/* newline character */
2222 	  if (!(optarg[0] == 'n' || optarg[0] == 'r') || optarg[1] != '\0')
2223 	    {
2224 	      fprintf (stderr, _("%s: illegal newline character specifier: \
2225 '%s': expected 'n' or 'r'\n"),
2226 		       program, optarg);
2227 	      goto option_error;
2228 	    }
2229 	  if (optarg[0] == 'n')
2230 	    nl = '\n';
2231 	  else
2232 	    nl = '\r';
2233 	  break;
2234 
2235 	case 'o':
2236 	case 'p':		/* output file */
2237 	  /* Check output file "-". */
2238 	  if (strcmp (optarg, "-") == 0)
2239 	    output_file = OUTPUT_FILE_STDOUT;
2240 	  else
2241 	    output_file = optarg;
2242 	  break;
2243 
2244 	case 'O':		/* list missing characters */
2245 	  list_missing_characters = 1;
2246 	  break;
2247 
2248 	case 'q':		/* quiet */
2249 	  quiet = 1;
2250 	  verbose = 0;
2251 	  break;
2252 
2253 	case 'r':		/* landscape */
2254 	  landscape = 1;
2255 	  break;
2256 
2257 	case 'R':		/* portrait */
2258 	  landscape = 0;
2259 	  break;
2260 
2261 	case 's':		/* baselineskip */
2262 	  baselineskip = atof (optarg);
2263 	  break;
2264 
2265 	case 'S':		/* statusdict */
2266 	  parse_key_value_pair (statusdict, optarg);
2267 	  break;
2268 
2269 	case 't':		/* title */
2270 	case 'J':
2271 	  title = optarg;
2272 	  title_given = 1;
2273 	  break;
2274 
2275 	case 'T':		/* tabulator size */
2276 	  tabsize = atoi (optarg);
2277 	  if (tabsize <= 0)
2278 	    tabsize = 1;
2279 	  break;
2280 
2281 	case 'u':		/* underlay */
2282 	  underlay = optarg;
2283 	  break;
2284 
2285 	case 'U':		/* nup */
2286 	  nup = atoi (optarg);
2287 	  break;
2288 
2289 	case 'v':		/* verbose */
2290 	  if (optarg)
2291 	    verbose = atoi (optarg);
2292 	  else
2293 	    verbose++;
2294 	  quiet = 0;
2295 	  break;
2296 
2297 	case 'V':		/* version */
2298 	  version ();
2299 	  exit (0);
2300 	  break;
2301 
2302 	case 'w':		/* output language */
2303 	  output_language = optarg;
2304 	  if (strcmp (output_language, "PostScript") != 0)
2305 	    /* Other output languages are handled with states. */
2306 	    output_language_pass_through = 1;
2307 	  break;
2308 
2309 	case 'W':		/* a helper application option */
2310 	  cp = strchr (optarg, ',');
2311 	  if (cp == NULL)
2312 	    FATAL ((stderr,
2313 		    _("malformed argument `%s' for option -W, --option: \
2314 no comma found"),
2315 		      optarg));
2316 
2317 	  if (cp - optarg != 1)
2318 	    FATAL ((stderr, _("helper application specification must be \
2319 single character: %s"),
2320 			      optarg));
2321 
2322 	  /* Take the index of the helper application and update `cp'
2323              to point to the beginning of the option. */
2324 	  i = *optarg;
2325 	  cp++;
2326 
2327 	  if (helper_options[i] == NULL)
2328 	    helper_options[i] = buffer_alloc ();
2329 	  else
2330 	    {
2331 	      /* We already had some options for this helper
2332                  application.  Let's separate these arguments. */
2333 	      buffer_append (helper_options[i], " ");
2334 	    }
2335 
2336 	  /* Add this new option. */
2337 	  buffer_append (helper_options[i], cp);
2338 	  break;
2339 
2340 	case 'X':		/* input encoding */
2341 	  xfree (encoding_name);
2342 	  encoding_name = xstrdup (optarg);
2343 	  break;
2344 
2345 	case 'z':		/* no form feeds */
2346 	  interpret_formfeed = 0;
2347 	  break;
2348 
2349 	case 'Z':		/* pass through */
2350 	  pass_through = 1;
2351 	  break;
2352 
2353 	case 128:		/* underlay font */
2354 	  if (!parse_font_spec (optarg, &ul_font, &ul_ptsize, NULL))
2355 	    FATAL ((stderr, _("malformed font spec: %s"), optarg));
2356 	  break;
2357 
2358 	case 129:		/* underlay gray */
2359 	  ul_gray = atof (optarg);
2360 	  break;
2361 
2362 	case 130:		/* page label format */
2363 	  xfree (page_label_format);
2364 	  page_label_format = xstrdup (optarg);
2365 	  break;
2366 
2367 	case 131:		/* download font */
2368 	  strhash_put (download_fonts, optarg, strlen (optarg) + 1, NULL,
2369 		       NULL);
2370 	  break;
2371 
2372 	case 132:		/* underlay angle */
2373 	  ul_angle = atof (optarg);
2374 	  ul_angle_p = 1;
2375 	  break;
2376 
2377 	case 133:		/* underlay position */
2378 	  xfree (ul_position);
2379 	  ul_position = xstrdup (optarg);
2380 	  ul_position_p = 1;
2381 	  break;
2382 
2383 	case 134:		/* non-printable format */
2384 	  xfree (npf_name);
2385 	  npf_name = xstrdup (optarg);
2386 	  break;
2387 
2388 	case 135:		/* help */
2389 	  usage ();
2390 	  exit (0);
2391 	  break;
2392 
2393 	case 136:		/* highlight bar gray */
2394 	  highlight_bar_gray = atof (optarg);
2395 	  break;
2396 
2397 	case 137:		/* underlay style */
2398 	  xfree (ul_style_str);
2399 	  ul_style_str = xstrdup (optarg);
2400 	  break;
2401 
2402 	case 138:		/* filter stdin */
2403 	  input_filter_stdin = optarg;
2404 	  break;
2405 
2406 	case 139:		/* extra options for the printer spooler */
2407 	  printer_options = optarg;
2408 	  break;
2409 
2410 	case 140:		/* slicing */
2411 	  slicing = 1;
2412 	  slice = atoi (optarg);
2413 	  if (slice <= 0)
2414 	    FATAL ((stderr, _("slice must be greater than zero")));
2415 	  break;
2416 
2417 	case 141:		/* help-highlight */
2418 	  help_highlight = 1;
2419 	  break;
2420 
2421 	case 142:		/* States color? */
2422 	  if (optarg == NULL)
2423 	    states_color = 1;
2424 	  else
2425 	    states_color = atoi (optarg);
2426 	  break;
2427 
2428 	case 143:		/* mark-wrapped-lines */
2429 	  if (optarg)
2430 	    {
2431 	      xfree (mark_wrapped_lines_style_name);
2432 	      mark_wrapped_lines_style_name = xstrdup (optarg);
2433 	    }
2434 	  else
2435 	    /* Set the system default. */
2436 	    mark_wrapped_lines_style = MWLS_BOX;
2437 	  break;
2438 
2439 	case 144:		/* adjust margins */
2440 	  margins_spec = optarg;
2441 	  break;
2442 
2443 	case 145:		/* N-up x-pad */
2444 	  nup_xpad = atoi (optarg);
2445 	  break;
2446 
2447 	case 146:		/* N-up y-pad */
2448 	  nup_ypad = atoi (optarg);
2449 	  break;
2450 
2451 	case 147:		/* word wrap */
2452 	  line_end = LE_WORD_WRAP;
2453 	  break;
2454 
2455 	case 148:		/* horizontal column height */
2456 	  horizontal_column_height = atof (optarg);
2457 	  formfeed_type = FORMFEED_HCOLUMN;
2458 	  break;
2459 
2460 	case 149:		/* PostScript language level */
2461 	  pslevel = atoi (optarg);
2462 	  break;
2463 
2464 	case 150:		/* rotate even-numbered pages */
2465 	  rotate_even_pages = 1;
2466 	  break;
2467 
2468 	case 151:		/* highlight style */
2469 	  xfree (states_highlight_style);
2470 	  states_highlight_style = xstrdup (optarg);
2471 	  break;
2472 
2473 	case 152:		/* N-up colunwise */
2474 	  nup_columnwise = 1;
2475 	  break;
2476 
2477 	case 153:		/* swap even page margins */
2478 	  swap_even_page_margins = 1;
2479 	  break;
2480 
2481 	case 154:		/* extended return values */
2482 	  extended_return_values = 1;
2483 	  break;
2484 
2485 	case 155:		/* footer */
2486 	  page_footer = optarg;
2487 	  break;
2488 
2489 	case 156:		/* continuous page numbers */
2490 	  continuous_page_numbers = 1;
2491 	  break;
2492 
2493 	case '?':		/* Errors found during getopt_long(). */
2494 	option_error:
2495 	  fprintf (stderr, _("Try `%s --help' for more information.\n"),
2496 		   program);
2497 	  exit (1);
2498 	  break;
2499 
2500 	default:
2501 	  printf ("Hey!  main() didn't handle option \"%c\" (%d)", c, c);
2502 	  if (optarg)
2503 	    printf (" with arg %s", optarg);
2504 	  printf ("\n");
2505 	  FATAL ((stderr, "This is a bug!"));
2506 	  break;
2507 	}
2508     }
2509 }
2510 
2511 
2512 static void
usage()2513 usage ()
2514 {
2515   printf (_("\
2516 Usage: %s [OPTION]... [FILE]...\n\
2517 Mandatory arguments to long options are mandatory for short options too.\n\
2518   -#                         an alias for option -n, --copies\n\
2519   -1                         same as --columns=1\n\
2520   -2                         same as --columns=2\n\
2521       --columns=NUM          specify the number of columns per page\n\
2522   -a, --pages=PAGES          specify which pages are printed\n\
2523   -A, --file-align=ALIGN     align separate input files to ALIGN\n\
2524   -b, --header=HEADER        set page header\n\
2525   -B, --no-header            no page headers\n\
2526   -c, --truncate-lines       cut long lines (default is to wrap)\n\
2527   -C[START], --line-numbers[=START]\n\
2528                              precede each line with its line number\n\
2529   -d                         an alias for option --printer\n\
2530   -D, --setpagedevice=KEY[:VALUE]\n\
2531                              pass a page device definition to output\n\
2532   -e[CHAR], --escapes[=CHAR]       enable special escape interpretation\n"),
2533           program);
2534 
2535   printf (_("\
2536   -E[LANG], --highlight[=LANG]     highlight source code\n"));
2537 
2538   printf (_("\
2539   -f, --font=NAME            use font NAME for body text\n\
2540   -F, --header-font=NAME     use font NAME for header texts\n\
2541   -g, --print-anyway         nothing (compatibility option)\n\
2542   -G                         same as --fancy-header\n\
2543       --fancy-header[=NAME]  select fancy page header\n\
2544   -h, --no-job-header        suppress the job header page\n\
2545   -H[NUM], --highlight-bars[=NUM]  specify how high highlight bars are\n\
2546   -i, --indent=NUM           set line indent to NUM characters\n\
2547   -I, --filter=CMD           read input files through input filter CMD\n\
2548   -j, --borders              print borders around columns\n\
2549   -J,                        an alias for option --title\n\
2550   -k, --page-prefeed         enable page prefeed\n\
2551   -K, --no-page-prefeed      disable page prefeed\n\
2552   -l, --lineprinter          simulate lineprinter, this is an alias for:\n\
2553                                --lines-per-page=66, --no-header, --portrait,\n\
2554                                --columns=1\n"));
2555 
2556   printf (_("\
2557   -L, --lines-per-page=NUM   specify how many lines are printed on each page\n\
2558   -m, --mail                 send mail upon completion\n\
2559   -M, --media=NAME           use output media NAME\n\
2560   -n, --copies=NUM           print NUM copies of each page\n\
2561   -N, --newline=NL           select the newline character.  Possible\n\
2562                              values for NL are: n (`\\n') and r (`\\r').\n\
2563   -o                         an alias for option --output\n\
2564   -O, --missing-characters   list missing characters\n\
2565   -p, --output=FILE          leave output to file FILE.  If FILE is `-',\n\
2566                              leave output to stdout.\n\
2567   -P, --printer=NAME         print output to printer NAME\n\
2568   -q, --quiet, --silent      be really quiet\n\
2569   -r, --landscape            print in landscape mode\n\
2570   -R, --portrait             print in portrait mode\n"));
2571 
2572   printf (_("\
2573   -s, --baselineskip=NUM     set baselineskip to NUM\n\
2574   -S, --statusdict=KEY[:VALUE]\n\
2575                              pass a statusdict definition to the output\n\
2576   -t, --title=TITLE          set banner page's job title to TITLE.  Option\n\
2577                              sets also the name of the input file stdin.\n\
2578   -T, --tabsize=NUM          set tabulator size to NUM\n\
2579   -u[TEXT], --underlay[=TEXT]      print TEXT under every page\n\
2580   -U, --nup=NUM              print NUM logical pages on each output page\n\
2581   -v, --verbose              tell what we are doing\n\
2582   -V, --version              print version number\n\
2583   -w, --language=LANG        set output language to LANG\n\
2584   -W, --options=APP,OPTION   pass option OPTION to helper application APP\n\
2585   -X, --encoding=NAME        use input encoding NAME\n\
2586   -z, --no-formfeed          do not interpret form feed characters\n\
2587   -Z, --pass-through         pass through PostScript and PCL files\n\
2588                              without any modifications\n"));
2589 
2590   printf (_("Long-only options:\n\
2591   --color[=bool]             create color outputs with states\n\
2592   --continuous-page-numbers  count page numbers across input files.  Don't\n\
2593                              restart numbering at beginning of each file.\n\
2594   --download-font=NAME       download font NAME\n\
2595   --extended-return-values   enable extended return values\n\
2596   --filter-stdin=NAME        specify how stdin is shown to the input filter\n\
2597   --footer=FOOTER            set page footer\n\
2598   --h-column-height=HEIGHT   set the horizontal column height to HEIGHT\n\
2599   --help                     print this help and exit\n"));
2600 
2601   printf (_("\
2602   --help-highlight           describe all supported --highlight languages\n\
2603                              and file formats\n\
2604   --highlight-bar-gray=NUM   print highlight bars with gray NUM (0 - 1)\n\
2605   --list-media               list names of all known media\n\
2606   --margins=LEFT:RIGHT:TOP:BOTTOM\n\
2607                              adjust page marginals\n\
2608   --mark-wrapped-lines[STYLE]\n\
2609                              mark wrapped lines in the output with STYLE\n\
2610   --non-printable-format=FMT specify how non-printable chars are printed\n"));
2611 
2612   printf (_("\
2613   --nup-columnwise           layout pages in the N-up printing columnwise\n\
2614   --nup-xpad=NUM             set the page x-padding of N-up printing to NUM\n\
2615   --nup-ypad=NUM             set the page y-padding of N-up printing to NUM\n\
2616   --page-label-format=FMT    set page label format to FMT\n\
2617   --ps-level=LEVEL           set the PostScript language level that enscript\n\
2618                              should use\n\
2619   --printer-options=OPTIONS  pass extra options to the printer command\n\
2620   --rotate-even-pages        rotate even-numbered pages 180 degrees\n"));
2621 
2622   printf (_("\
2623   --slice=NUM                print vertical slice NUM\n\
2624   --style=STYLE              use highlight style STYLE\n\
2625   --swap-even-page-margins   swap left and right side margins for each even\n\
2626                              numbered page\n\
2627   --toc                      print table of contents\n\
2628   --ul-angle=ANGLE           set underlay text's angle to ANGLE\n\
2629   --ul-font=NAME             print underlays with font NAME\n\
2630   --ul-gray=NUM              print underlays with gray value NUM\n\
2631   --ul-position=POS          set underlay's starting position to POS\n\
2632   --ul-style=STYLE           print underlays with style STYLE\n\
2633   --word-wrap                wrap long lines from word boundaries\n\
2634 "));
2635 
2636   printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
2637 }
2638 
2639 
2640 static void
version()2641 version ()
2642 {
2643   printf ("%s\n\
2644 Copyright (C) 1995-2003, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.\n\
2645 %s comes with NO WARRANTY, to the extent permitted by law.\n\
2646 You may redistribute copies of %s under the terms of the GNU\n\
2647 General Public License, version 3 or, at your option, any later version.\n\
2648 For more information about these matters, see the files named COPYING.\n",
2649 	  PACKAGE_STRING,
2650 	  PACKAGE_NAME,
2651 	  PACKAGE_NAME);
2652 }
2653