Apply patch from Debian Security Team for CAN-2004-1184.
[enscript.git] / src / main.c
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 *version_string = NULL;    /* Enscript's version string. */
62 char *ps_version_string = NULL; /* Version string for PS procsets. */
63 char *date_string = NULL;       /* Preformatted time string. */
64 struct tm run_tm;               /* Time when program is run. */
65 struct tm mod_tm;               /* Last modification time for current file. */
66 struct passwd *passwd;          /* Passwd entry for the user running this
67                                    program. */
68
69 /* Path to our library. */
70 char *enscript_library = LIBRARY;
71
72 /* Library lookup path. */
73 char *libpath = NULL;
74
75 /* AFM library lookup path. */
76 char *afm_path = NULL;
77
78 MediaEntry *media_names = NULL; /* List of known media. */
79 MediaEntry *media = NULL;       /* Entry for used media. */
80 int bs = 8;                     /* The backspace character. */
81
82 /* Statistics. */
83 int total_pages = 0;            /* Total number of pages printed. */
84 int num_truncated_lines = 0;    /* Number of lines truncated. */
85 int num_missing_chars = 0;      /* Number of unknown characters. */
86 int missing_chars[256] = {0};   /* Table of unknown characters. */
87 int num_non_printable_chars = 0; /* Number of non-printable characters. */
88 int non_printable_chars[256] = {0}; /* Table of non-printable characters. */
89
90 /* Output media dimensions that are used during PostScript emission. */
91 int d_page_w = 0;               /* page's width */
92 int d_page_h = 0;               /* page's height */
93 int d_header_w = 0;             /* fancy header's width */
94 int d_header_h = 0;             /* fancy header's height */
95 int d_footer_h = 0;             /* fancy footer's height */
96 int d_output_w = 0;             /* output area's width */
97 int d_output_h = 0;             /* output area's height  */
98 int d_output_x_margin = 5;      /* output area's x marginal */
99 int d_output_y_margin = 2;      /* output area's y marginal */
100
101 /* Document needed resources. */
102 StringHashPtr res_fonts;        /* fonts */
103
104 /* Fonts to download. */
105 StringHashPtr download_fonts;
106
107 /* Additional key-value pairs, passed to the generated PostScript code. */
108 StringHashPtr pagedevice;       /* for setpagedevice */
109 StringHashPtr statusdict;       /* for statusdict */
110
111 /* User defined strings. */
112 StringHashPtr user_strings;
113
114 /* Cache for AFM files. */
115 StringHashPtr afm_cache = NULL;
116 StringHashPtr afm_info_cache = NULL;
117
118 /* AFM library handle. */
119 AFMHandle afm = NULL;
120
121
122 /* Options. */
123
124 /*
125  * Free single-letter options are: Q, x, y, Y
126  */
127
128 /*
129  * -#
130  *
131  * An alias for -n, --copies.
132  */
133
134 /*
135  * -1, -2, -3, -4, -5, -6, -7, -8, -9, --columns=NUM
136  *
137  * Number of columns per page.  The default is 1 column.
138  */
139 int num_columns = 1;
140
141 /*
142  * -a PAGES, --pages=PAGES
143  *
144  * Specify which pages are printed.
145  */
146 PageRange *page_ranges = NULL;
147
148 /*
149  * -A ALIGN, --file-align=ALIGN
150  *
151  * Align input files to start from ALIGN page count.  This is handy
152  * for two-side printings.
153  */
154 unsigned int file_align = 1;
155
156 /*
157  * -b STRING, --header=STRING
158  *
159  * Set the string that is used as the page header.  As a default, page
160  * header is constructed from filename, date and page number.
161  */
162 char *page_header = NULL;
163
164 /*
165  * -B, --no-header
166  *
167  * Do not print page headers.
168  */
169
170 /*
171  * -c, --truncate-lines
172  *
173  * Truncate lines that are longer than the page width.  Default is character
174  * wrap.
175  */
176 LineEndType line_end = LE_CHAR_WRAP;
177
178 /*
179  * -C [START], --line-numbers[=START]
180  *
181  * Precede each line with its line number.  As a default, do not mark
182  * line numbers.  If the optional argument START is given, it
183  * specifies the number from which the line numbers are assumed to
184  * start in the file.  This is useful if the file contains a region
185  * of a bigger file.
186  */
187 int line_numbers = 0;
188 unsigned int start_line_number = 1;
189
190 /*
191  * -d, -P, --printer
192  *
193  * Name of the printer to which output is send.  Defaults to system's
194  * default printer.
195  */
196 char *printer = NULL;
197
198 /*
199  * -e [CHAR], --escapes[=CHAR]
200  *
201  * Enable special escape ('\000') interpretation.  If option CHAR is given
202  * it is assumed to specify the escape character.
203  */
204 int special_escapes = 0;
205 int escape_char = '\0';
206 int default_escape_char;
207
208 /*
209  * -E [LANG], --highlight=[LANG] (deprecated --pretty-print[=LANG])
210  *
211  * Highlight program source code.  Highlighting is handled by creating
212  * an input filter with the states-program.  States makes an educated
213  * guess about the start state but sometimes it fails, so the start
214  * state can also be specified to be LANG.  This option overwrites
215  * input filter and enables special escapes.
216  */
217
218 int highlight = 0;
219 char *hl_start_state = NULL;
220
221 /*
222  * -f, --font
223  *
224  * Select body font.
225  */
226 char *Fname = "Courier";
227 FontPoint Fpt = {10.0, 10.0};
228 FontPoint default_Fpt;          /* Point size of the original font. */
229 char *default_Fname;            /* Name of the original font. */
230 InputEncoding default_Fencoding; /* The encoding of the original font. */
231 int user_body_font_defined = 0; /* Has user defined new body font? */
232
233 double font_widths[256];        /* Width array for body font. */
234 char font_ctype[256];           /* Font character types. */
235 int font_is_fixed;              /* Is body font a fixed pitch font? */
236 double font_bbox_lly;           /* Font's bounding box's lly-coordinate. */
237
238 /*
239  * -F, --header-font
240  *
241  * Select font to be used to print the standard simple header.
242  */
243 char *HFname = "Courier-Bold";
244 FontPoint HFpt = {10.0, 10.0};
245
246 /*
247  * -g, --print-anyway
248  *
249  * Print document even it contains binary data.  This does nothing
250  * since enscript prints files anyway.
251  */
252
253 /*
254  * -G, --fancy-header
255  *
256  * Add a fancy header to top of every page.  There are several header styles
257  * but the default is 'no fancy header'.
258  */
259 HeaderType header = HDR_SIMPLE;
260 char *fancy_header_name = NULL;
261 char *fancy_header_default = NULL;
262
263 /*
264  * -h, --no-job-header
265  *
266  * Supress the job header page.
267  */
268 static int no_job_header = 0;
269
270 /*
271  * -H num, --highlight-bars=num
272  *
273  * Print highlight bars under text.  Bars will be <num> lines high.
274  * As a default, do not print bars.
275  */
276 unsigned int highlight_bars = 0;
277
278 /*
279  * -i, --indent
280  *
281  * Indent every line this many characters.
282  */
283 double line_indent = 0.0;
284 char *line_indent_spec = "0";
285
286 /*
287  * -I CMD, --filter=CMD
288  *
289  * Read input files through input filter CMD.
290  */
291 char *input_filter = NULL;
292
293 /*
294  * -j, --borders
295  *
296  * Print borders around columns.
297  */
298 int borders = 0;
299
300 /*
301  * -J
302  *
303  * An alias for -t, --title.
304  */
305
306 /*
307  * -k, --page-prefeed
308  * -K, --no-page-prefeed
309  *
310  * Control page prefeed.
311  */
312 int page_prefeed = 0;
313
314 /*
315  * -l, --lineprinter
316  *
317  * Emulate lineprinter -  make pages 66 lines long and omit headers.
318  */
319
320 /*
321  * -L, --lines-per-page
322  *
323  * Specify how many lines should be printed on a single page.  Normally
324  * enscript counts it from font point sizes.
325  */
326 unsigned int lines_per_page = (unsigned int) -1;
327
328 /*
329  * -m, --mail
330  *
331  * Send mail notification to user after print job has been completed.
332  */
333 int mail = 0;
334
335 /*
336  * -M, --media
337  *
338  * Name of the output media.  Default is A4.
339  */
340 char *media_name = NULL;
341
342 /*
343  * -n, --copies
344  *
345  * Number of copies to print.
346  */
347 int num_copies = 1;
348
349 /*
350  * -N, --newline
351  *
352  * Set the newline character: '\n' or '\r'.  As a default, the newline
353  * character is specified by the input encoding.
354  */
355 int nl = -1;
356
357 /*
358  * -o, -p, --output
359  *
360  * Leave output to the specified file.  As a default result is spooled to
361  * printer.
362  */
363 char *output_file = OUTPUT_FILE_NONE;
364
365 /*
366  * -O, --missing-characters
367  *
368  * List all missing characters.  Default is no listing.
369  */
370 int list_missing_characters = 0;
371
372 /*
373  * -q, --quiet
374  *
375  * Do not tell what we are doing.  Default is to tell something but
376  * not --verbose.
377  */
378 int quiet = 0;
379
380 /*
381  * -r, --landscape
382  * -R, --portrait
383  *
384  * Print with page rotated 90 degrees (landscape mode).  Default is
385  * portrait.
386  */
387 int landscape = 0;
388
389 /*
390  * -s, --baselineskip
391  *
392  * Specify baselineskip value that is used when enscript moves to
393  * a new line.  Current point movement is font_point_size + baselineskip.
394  */
395 double baselineskip = 1.0;
396
397 /*
398  * -t, --title
399  *
400  * Title which is printed to the banner page.  If this option is given
401  * from the command line, this sets also the name of the stdin which
402  * is by the default "".
403  */
404 char *title = "Enscript Output";
405 int title_given = 0;
406
407 /*
408  * -T, --tabsize
409  *
410  * Specify tabulator size.
411  */
412 int tabsize = 8;
413
414 /*
415  * -u, --underlay
416  *
417  * Place text under every page.  Default is no underlay.
418  */
419 double ul_gray = .8;
420 FontPoint ul_ptsize = {200.0, 200.0};
421 char *ul_font = "Times-Roman";
422 char *underlay = NULL;
423 char *ul_position = NULL;       /* Position info as a string. */
424 double ul_x;                    /* Position x-coordinate. */
425 double ul_y;                    /* Position y-coordinate. */
426 double ul_angle;
427 unsigned int ul_style = UL_STYLE_OUTLINE;
428 char *ul_style_str = NULL;
429 int ul_position_p = 0;          /* Is ul-position given? */
430 int ul_angle_p = 0;             /* Is ul-angle given? */
431
432 /*
433  * -U NUM, --nup=NUM
434  *
435  * Print NUM PostScript pages on each output page (n-up printing).
436  */
437 unsigned int nup = 1;
438 unsigned int nup_exp = 0;
439 unsigned int nup_rows = 1;
440 unsigned int nup_columns = 1;
441 int nup_landscape = 0;
442 unsigned int nup_width;
443 unsigned int nup_height;
444 double nup_scale;
445
446 /*
447  * -v, --verbose
448  *
449  * Tell what we are doing.  Default is no verbose outputs.
450  */
451 int verbose = 0;
452
453 /*
454  * -V, --version
455  *
456  * Print version information.
457  */
458
459 /*
460  * -w LANGUAGE, --language=LANGUAGE
461  *
462  * Generate output for language LANGUAGE.  The default is PostScript.
463  */
464 char *output_language = "PostScript";
465 int output_language_pass_through = 0;
466
467 /*
468  * -W APP,option, --options=APP,OPTION
469  *
470  * Pass additional option to enscript's helper applications.  The
471  * first part of the option's argument (APP) specifies the
472  * helper application to which the options are added.  Currently the
473  * following helper application are defined:
474  *
475  *   s  states
476  */
477 Buffer *helper_options[256] = {0};
478
479 /*
480  * -X, --encoding
481  *
482  * Specifies input encoding.  Default is ISO-8859.1.
483  */
484 InputEncoding encoding = ENC_ISO_8859_1;
485 char *encoding_name = NULL;
486
487 /*
488  * -z, --no-formfeed
489  *
490  * Do not interpret form feed characters.  As a default, form feed
491  * characters are interpreted.
492  */
493 int interpret_formfeed = 1;
494
495 /*
496  * -Z, --pass-through
497  *
498  * Pass through all PostScript and PCL files without any modifications.
499  * As a default, don't.
500  */
501 int pass_through = 0;
502
503 /*
504  * --color[=bool]
505  *
506  * Create color output with states?
507  */
508
509 /*
510  * --continuous-page-numbers
511  *
512  * Count page numbers across input files.  Don't restart numbering
513  * at beginning of each file.
514  */
515 int continuous_page_numbers = 0;
516
517 /*
518  * --download-font=FONT
519  *
520  * Download font FONT to printer.
521  */
522
523 /*
524  * --extended-return-values
525  *
526  * Enable extended return values.
527  */
528 int extended_return_values = 0;
529
530 /*
531  * --filter-stdin=STR
532  *
533  * How stdin is shown to the filter command.  The default is "" but
534  * some utilities might want it as "-".
535  */
536 char *input_filter_stdin = "";
537
538 /*
539  * --footer=STRING
540  *
541  * Set the string that is used as the page footer.  As a default, the
542  * page has no footer.  Setting this option does not necessary show
543  * any footer strings in the output.  It depends on the selected
544  * header (`.hdr' file) whether it supports footer strings or not.
545  */
546 char *page_footer = NULL;
547
548 /*
549  * --h-column-height=HEIGHT
550  *
551  * Set the horizontal column (channel) height to be HEIGHT.  This option
552  * also sets the FormFeedType to `hcolumn'.  The default value is set to be
553  * big enough to cause a jump to the next vertical column (100m).
554  */
555 double horizontal_column_height = 283465.0;
556
557 /*
558  * --help-highlight (deprecated --help-pretty-print)
559  *
560  * Descript all supported -E, --highlight languages and file formats.
561  */
562 int help_highlight = 0;
563
564 /*
565  * --highlight-bar-gray=val
566  *
567  * Specify the gray level for highlight bars.
568  */
569 double highlight_bar_gray = .97;
570
571 /*
572  * --list-media
573  *
574  * List all known media.  As a default do not list media names.
575  */
576 int list_media = 0;
577
578 /*
579  * --margins=LEFT:RIGHT:TOP:BOTTOM
580  *
581  * Adjust page marginals.
582  */
583 char *margins_spec = NULL;
584
585 /*
586  * --mark-wrapped-lines[=STYLE]
587  *
588  * Mark wrapped lines so that they can be easily detected from the printout.
589  * Optional parameter STYLE specifies the marking style, the system default
590  * is black box.
591  */
592 char *mark_wrapped_lines_style_name = NULL;
593 MarkWrappedLinesStyle mark_wrapped_lines_style = MWLS_NONE;
594
595 /*
596  * --non-printable-format=FORMAT
597  *
598  * Format in which non-printable characters are printed.
599  */
600 char *npf_name = NULL;
601 NonPrintableFormat non_printable_format = NPF_OCTAL;
602
603 /*
604  * --nup-columnwise
605  *
606  * Layout N-up pages colunwise instead of row-wise.
607  */
608 int nup_columnwise = 0;
609
610 /*
611  * --nup-xpad=NUM
612  *
613  * The x-padding between N-up subpages.
614  */
615 unsigned int nup_xpad = 10;
616
617 /*
618  * --nup-ypad=NUM
619  *
620  * The y-padding between N-up subpages.
621  */
622 unsigned int nup_ypad = 10;
623
624 /*
625  * --page-label-format=FORMAT
626  *
627  * Format in which page labels are printed; the default is "short".
628  */
629 char *page_label_format = NULL;
630 PageLabelFormat page_label;
631
632 /*
633  * --ps-level=LEVEL
634  *
635  * The PostScript language level that enscript should use; the default is 2.
636  */
637 unsigned int pslevel = 2;
638
639 /*
640  * --printer-options=OPTIONS
641  *
642  * Pass extra options OPTIONS to the printer spooler.
643  */
644 char *printer_options = NULL;
645
646 /*
647  * --rotate-even-pages
648  *
649  * Rotate each even-numbered page 180 degrees.  This might be handy in
650  * two-side printing when the resulting pages are bind from some side.
651  * Greetings to Jussi-Pekka Sairanen.
652  */
653 int rotate_even_pages = 0;
654
655 /*
656  * --slice=NUM
657  *
658  * Horizontal input slicing.  Print only NUMth wrapped input pages.
659  */
660 int slicing = 0;
661 unsigned int slice = 1;
662
663 /*
664  * --swap-even-page-margins
665  *
666  * Swap left and right side margins for each even numbered page.  This
667  * might be handy in two-side printing.
668  */
669 int swap_even_page_margins = 0;
670
671 /*
672  * --toc
673  *
674  * Print Table of Contents page.
675  */
676 int toc = 0;
677 FILE *toc_fp;
678 char *toc_fmt_string;
679
680 /*
681  * --word-wrap
682  *
683  * Wrap long lines from word boundaries.  The default is character wrap.
684  */
685
686 /*
687  * AcceptCompositeCharacters: bool
688  *
689  * Specify whatever we accept composite characters or should them be
690  * considered as non-existent.  As a default, do not accept them.
691  */
692 int accept_composites = 0;
693
694 /*
695  * AppendCtrlD: bool
696  *
697  * Append ^D character to the end of the output.  Some printers require this
698  * but the default is false.
699  */
700 int append_ctrl_D = 0;
701
702 /*
703  * Clean7Bit: bool
704  *
705  * Specify how characters greater than 127 are printed.
706  */
707 int clean_7bit = 1;
708
709 /*
710  * FormFeedType: type
711  *
712  * Specify what to do when a formfeed character is encountered from the
713  * input stream.  The default action is to jump to the beginning of the
714  * next column.
715  */
716 FormFeedType formfeed_type = FORMFEED_COLUMN;
717
718 /*
719  * GeneratePageSize: bool
720  *
721  * Specify whether the `PageSize' pagedevice definitions should be
722  * generated to the output.
723  */
724 int generate_PageSize = 1;
725
726 /*
727  * NoJobHeaderSwitch: switch
728  *
729  * Spooler switch to suppress the job header (-h).
730  */
731 char *no_job_header_switch = NULL;
732
733 /*
734  * OutputFirstLine: line
735  *
736  * Set the PostScript output's first line to something your system can handle.
737  * The default is "%!PS-Adobe-3.0"
738  */
739 char *output_first_line = NULL;
740
741 /*
742  * QueueParam: param
743  *
744  * The spooler command switch to select the printer queue (-P).
745  */
746 char *queue_param = NULL;
747
748 /*
749  * Spooler: command
750  *
751  * The spooler command name (lpr).
752  */
753 char *spooler_command = NULL;
754
755 /*
756  * StatesBinary: path
757  *
758  * An absolute path to the `states' binary.
759  */
760
761 char *states_binary = NULL;
762
763 /*
764  * StatesColor: bool
765  *
766  * Should the States program generate color outputs.
767  */
768 int states_color = 0;
769
770 /*
771  * StatesConfigFile: file
772  *
773  * The name of the states' configuration file.
774  */
775 char *states_config_file = NULL;
776
777 /*
778  * StatesHighlightStyle: style
779  *
780  * The highlight style.
781  */
782 char *states_highlight_style = NULL;
783
784 /*
785  * StatesPath: path
786  *
787  * Define the path for the states program.  The states program will
788  * lookup its state definition files from this path.
789  */
790 char *states_path = NULL;
791
792 /* ^@shade{GRAY}, set the line highlight gray. */
793 double line_highlight_gray = 1.0;
794
795 /* ^@bggray{GRAY}, set the text background gray. */
796 double bggray = 1.0;
797
798 EncodingRegistry encodings[] =
799 {
800   {{"88591", "latin1", NULL},           ENC_ISO_8859_1,         '\n', 8},
801   {{"88592", "latin2", NULL},           ENC_ISO_8859_2,         '\n', 8},
802   {{"88593", "latin3", NULL},           ENC_ISO_8859_3,         '\n', 8},
803   {{"88594", "latin4", NULL},           ENC_ISO_8859_4,         '\n', 8},
804   {{"88595", "cyrillic", NULL},         ENC_ISO_8859_5,         '\n', 8},
805   {{"88597", "greek", NULL},            ENC_ISO_8859_7,         '\n', 8},
806   {{"88599", "latin5", NULL},           ENC_ISO_8859_9,         '\n', 8},
807   {{"885910", "latin6", NULL},          ENC_ISO_8859_10,        '\n', 8},
808   {{"ascii", NULL, NULL},               ENC_ASCII,              '\n', 8},
809   {{"asciifise", "asciifi", "asciise"}, ENC_ASCII_FISE,         '\n', 8},
810   {{"asciidkno", "asciidk", "asciino"}, ENC_ASCII_DKNO,         '\n', 8},
811   {{"ibmpc", "pc", "dos"},              ENC_IBMPC,              '\n', 8},
812   {{"mac", NULL, NULL},                 ENC_MAC,                '\r', 8},
813   {{"vms", NULL, NULL},                 ENC_VMS,                '\n', 8},
814   {{"hp8", NULL, NULL},                 ENC_HP8,                '\n', 8},
815   {{"koi8", NULL, NULL},                ENC_KOI8,               '\n', 8},
816   {{"ps", "PS", NULL},                  ENC_PS,                 '\n', 8},
817   {{"pslatin1", "ISOLatin1Encoding", NULL},     ENC_ISO_8859_1, '\n', 8},
818
819   {{NULL, NULL, NULL}, 0, 0, 0},
820 };
821
822
823 /*
824  * Static variables.
825  */
826
827 static struct option long_options[] =
828 {
829   {"columns",                   required_argument,      0, 0},
830   {"pages",                     required_argument,      0, 'a'},
831   {"file-align",                required_argument,      0, 'A'},
832   {"header",                    required_argument,      0, 'b'},
833   {"no-header",                 no_argument,            0, 'B'},
834   {"truncate-lines",            no_argument,            0, 'c'},
835   {"line-numbers",              optional_argument,      0, 'C'},
836   {"printer",                   required_argument,      0, 'd'},
837   {"setpagedevice",             required_argument,      0, 'D'},
838   {"escapes",                   optional_argument,      0, 'e'},
839   {"highlight",                 optional_argument,      0, 'E'},
840   {"font",                      required_argument,      0, 'f'},
841   {"header-font",               required_argument,      0, 'F'},
842   {"print-anyway",              no_argument,            0, 'g'},
843   {"fancy-header",              optional_argument,      0, 'G'},
844   {"no-job-header",             no_argument,            0, 'h'},
845   {"highlight-bars",            optional_argument,      0, 'H'},
846   {"indent",                    required_argument,      0, 'i'},
847   {"filter",                    required_argument,      0, 'I'},
848   {"borders",                   no_argument,            0, 'j'},
849   {"page-prefeed",              no_argument,            0, 'k'},
850   {"no-page-prefeed",           no_argument,            0, 'K'},
851   {"lineprinter",               no_argument,            0, 'l'},
852   {"lines-per-page",            required_argument,      0, 'L'},
853   {"mail",                      no_argument,            0, 'm'},
854   {"media",                     required_argument,      0, 'M'},
855   {"copies",                    required_argument,      0, 'n'},
856   {"newline",                   required_argument,      0, 'N'},
857   {"output",                    required_argument,      0, 'p'},
858   {"missing-characters",        no_argument,            0, 'O'},
859   {"quiet",                     no_argument,            0, 'q'},
860   {"silent",                    no_argument,            0, 'q'},
861   {"landscape",                 no_argument,            0, 'r'},
862   {"portrait",                  no_argument,            0, 'R'},
863   {"baselineskip",              required_argument,      0, 's'},
864   {"statusdict",                required_argument,      0, 'S'},
865   {"title",                     required_argument,      0, 't'},
866   {"tabsize",                   required_argument,      0, 'T'},
867   {"underlay",                  optional_argument,      0, 'u'},
868   {"nup",                       required_argument,      0, 'U'},
869   {"verbose",                   optional_argument,      0, 'v'},
870   {"version",                   no_argument,            0, 'V'},
871   {"language",                  required_argument,      0, 'w'},
872   {"option",                    required_argument,      0, 'W'},
873   {"encoding",                  required_argument,      0, 'X'},
874   {"no-formfeed",               no_argument,            0, 'z'},
875   {"pass-through",              no_argument,            0, 'Z'},
876
877   /* Long options without short counterparts.  Next free is 157. */
878   {"color",                     optional_argument,      0, 142},
879   {"continuous-page-numbers",   no_argument,            0, 156},
880   {"download-font",             required_argument,      0, 131},
881   {"extended-return-values",    no_argument,            0, 154},
882   {"filter-stdin",              required_argument,      0, 138},
883   {"footer",                    required_argument,      0, 155},
884   {"h-column-height",           required_argument,      0, 148},
885   {"help",                      no_argument,            0, 135},
886   {"help-highlight",            no_argument,            0, 141},
887   {"highlight-bar-gray",        required_argument,      0, 136},
888   {"list-media",                no_argument,            &list_media, 1},
889   {"margins",                   required_argument,      0, 144},
890   {"mark-wrapped-lines",        optional_argument,      0, 143},
891   {"non-printable-format",      required_argument,      0, 134},
892   {"nup-columnwise",            no_argument,            0, 152},
893   {"nup-xpad",                  required_argument,      0, 145},
894   {"nup-ypad",                  required_argument,      0, 146},
895   {"page-label-format",         required_argument,      0, 130},
896   {"ps-level",                  required_argument,      0, 149},
897   {"printer-options",           required_argument,      0, 139},
898   {"rotate-even-pages",         no_argument,            0, 150},
899   {"slice",                     required_argument,      0, 140},
900   {"style",                     required_argument,      0, 151},
901   {"swap-even-page-margins",    no_argument,            0, 153},
902   {"toc",                       no_argument,            &toc, 1},
903   {"word-wrap",                 no_argument,            0, 147},
904   {"ul-angle",                  required_argument,      0, 132},
905   {"ul-font",                   required_argument,      0, 128},
906   {"ul-gray",                   required_argument,      0, 129},
907   {"ul-position",               required_argument,      0, 133},
908   {"ul-style",                  required_argument,      0, 137},
909
910   /* Backwards compatiblity options. */
911   {"pretty-print",              optional_argument,      0, 'E'},
912   {"help-pretty-print",         no_argument,            0, 141},
913
914   {NULL, 0, 0, 0},
915 };
916
917
918 /*
919  * Global functions.
920  */
921
922 int
923 main (int argc, char *argv[])
924 {
925   InputStream is;
926   time_t tim;
927   struct tm *tm;
928   int i, j, found;
929   unsigned int ui;
930   MediaEntry *mentry;
931   AFMError afm_error;
932   char *cp, *cp2;
933   int retval = 0;
934   Buffer buffer;
935
936   /* Init our dynamic memory buffer. */
937   buffer_init (&buffer);
938
939   /* Get program's name. */
940   program = strrchr (argv[0], '/');
941   if (program == NULL)
942     program = argv[0];
943   else
944     program++;
945
946   /* Make getopt_long() to use our modified programname. */
947   argv[0] = program;
948
949   /* Create version strings. */
950
951   buffer_clear (&buffer);
952   buffer_append (&buffer, "GNU ");
953   buffer_append (&buffer, PACKAGE);
954   buffer_append (&buffer, " ");
955   buffer_append (&buffer, VERSION);
956   version_string = buffer_copy (&buffer);
957
958   ps_version_string = xstrdup (VERSION);
959   cp = strrchr (ps_version_string, '.');
960   *cp = ' ';
961
962   /* Create the default TOC format string.  Wow, this is cool! */
963   /* xgettext:no-c-format */
964   toc_fmt_string = _("$3v $-40N $3% pages $4L lines  $E $C");
965
966   /* Internationalization. */
967 #if HAVE_SETLOCALE
968   /*
969    * We want to change only messages (gs do not like decimals in 0,1
970    * format ;)
971    */
972 #if HAVE_LC_MESSAGES
973   setlocale (LC_MESSAGES, "");
974 #endif
975 #endif
976 #if ENABLE_NLS
977   bindtextdomain (PACKAGE, LOCALEDIR);
978   textdomain (PACKAGE);
979 #endif
980
981   /* Create date string. */
982
983   tim = time (NULL);
984   tm = localtime (&tim);
985   memcpy (&run_tm, tm, sizeof (*tm));
986
987   date_string = xstrdup (asctime (&run_tm));
988   i = strlen (date_string);
989   date_string[i - 1] = '\0';
990
991   /* Get user's passwd entry. */
992   passwd = getpwuid (getuid ());
993   if (passwd == NULL)
994     FATAL ((stderr, _("couldn't get passwd entry for uid=%d: %s"), getuid (),
995             strerror (errno)));
996
997   /* Defaults for some options. */
998   media_name            = xstrdup ("A4");
999   encoding_name         = xstrdup ("88591");
1000   npf_name              = xstrdup ("octal");
1001   page_label_format     = xstrdup ("short");
1002   ul_style_str          = xstrdup ("outline");
1003   ul_position           = xstrdup ("+0-0");
1004   spooler_command       = xstrdup ("lpr");
1005   queue_param           = xstrdup ("-P");
1006   no_job_header_switch  = xstrdup ("-h");
1007   fancy_header_default  = xstrdup ("enscript");
1008   output_first_line     = xstrdup ("%!PS-Adobe-3.0");
1009
1010   /* Check ENSCRIPT_LIBRARY for custom library location. */
1011   cp = getenv ("ENSCRIPT_LIBRARY");
1012   if (cp)
1013     enscript_library = cp;
1014
1015   /* Fill up build-in libpath. */
1016
1017   cp = getenv ("HOME");
1018   if (cp == NULL)
1019     cp = passwd->pw_dir;
1020
1021   buffer_clear (&buffer);
1022   buffer_append (&buffer, enscript_library);
1023   buffer_append (&buffer, PATH_SEPARATOR_STR);
1024   buffer_append (&buffer, cp);
1025   buffer_append (&buffer, "/.enscript");
1026   libpath = buffer_copy (&buffer);
1027
1028   /* Defaults for the states filter. */
1029
1030   states_binary = xstrdup ("states"); /* Take it from the user path. */
1031
1032   buffer_clear (&buffer);
1033   buffer_append (&buffer, enscript_library);
1034   buffer_append (&buffer, "/hl/enscript.st");
1035   states_config_file = buffer_copy (&buffer);
1036
1037   states_highlight_style = xstrdup ("emacs");
1038
1039   /* The <cp> holds the user's home directory. */
1040   buffer_clear (&buffer);
1041   buffer_append (&buffer, cp);
1042   buffer_append (&buffer, "/.enscript");
1043   buffer_append (&buffer, PATH_SEPARATOR_STR);
1044   buffer_append (&buffer, enscript_library);
1045   buffer_append (&buffer, "/hl");
1046   states_path = buffer_copy (&buffer);
1047
1048   /* Initialize resource sets. */
1049   res_fonts = strhash_init ();
1050   download_fonts = strhash_init ();
1051   pagedevice = strhash_init ();
1052   statusdict = strhash_init ();
1053   user_strings = strhash_init ();
1054
1055
1056   /*
1057    * Read configuration files.
1058    */
1059
1060   /* Global config. */
1061 #define CFG_FILE_NAME "enscript.cfg"
1062   if (!read_config (SYSCONFDIR, CFG_FILE_NAME))
1063     {
1064       int saved_errno = errno;
1065
1066       /* Try to read it from our library directory.  This is mostly
1067          the case for the micro ports.  */
1068       if (!read_config (enscript_library, CFG_FILE_NAME))
1069         {
1070           /* Try `enscript_library/../../etc/'.  This is the case for
1071              installations which set the prefix after the compilation
1072              and our SYSCONFDIR points to wrong directory. */
1073
1074           buffer_clear (&buffer);
1075           buffer_append (&buffer, enscript_library);
1076           buffer_append (&buffer, "/../../etc");
1077
1078           if (!read_config (buffer_ptr (&buffer), CFG_FILE_NAME))
1079             {
1080               /* Maybe we are not installed yet, let's try `../lib'
1081                  and `../../lib'. */
1082               if (!read_config ("../lib", CFG_FILE_NAME)
1083                   && !read_config ("../../lib", CFG_FILE_NAME))
1084                 {
1085                   /* No luck, report error from the original config file. */
1086                   ERROR ((stderr, _("couldn't read config file \"%s/%s\": %s"),
1087                           enscript_library, CFG_FILE_NAME,
1088                           strerror (saved_errno)));
1089                   ERROR ((stderr,
1090                           _("I did also try the following directories:")));
1091                   ERROR ((stderr, _("\t%s"), SYSCONFDIR));
1092                   ERROR ((stderr, _("\t%s"), enscript_library));
1093                   ERROR ((stderr, _("\t%s"), buffer_ptr (&buffer)));
1094                   ERROR ((stderr, _("\t../lib")));
1095                   ERROR ((stderr, _("\t../../lib")));
1096                   ERROR ((stderr,
1097 _("This is probably an installation error.  Please, try to rebuild:")));
1098                   ERROR ((stderr, _("\tmake distclean")));
1099                   ERROR ((stderr, _("\t./configure --prefix=PREFIX")));
1100                   ERROR ((stderr, _("\tmake")));
1101                   ERROR ((stderr, _("\tmake check")));
1102                   ERROR ((stderr, _("\tmake install")));
1103                   ERROR ((stderr,
1104 _("or set the environment variable `ENSCRIPT_LIBRARY' to point to your")));
1105                   ERROR ((stderr,
1106 _("library directory.")));
1107                   exit (1);
1108                 }
1109
1110               /* Ok, we are not installed yet.  Here is a small kludge
1111                  to conform the GNU coding standards: we must be able
1112                  to run without being installed, so we must append the
1113                  `../lib' and `../../lib' directories to the libpath.
1114                  The later allows us to be run form the `src/tests'
1115                  directory.  */
1116               buffer_clear (&buffer);
1117               buffer_append (&buffer, libpath);
1118               buffer_append (&buffer, PATH_SEPARATOR_STR);
1119               buffer_append (&buffer, "../lib");
1120               buffer_append (&buffer, PATH_SEPARATOR_STR);
1121               buffer_append (&buffer, "../../lib");
1122
1123               xfree (libpath);
1124               libpath = buffer_copy (&buffer);
1125             }
1126         }
1127     }
1128
1129   /* Site config. */
1130   read_config (SYSCONFDIR, "enscriptsite.cfg");
1131
1132   /* Personal config. */
1133   read_config (cp, ".enscriptrc");
1134
1135   /*
1136    * Options.
1137    */
1138
1139   /* Environment variables. */
1140   handle_env_options ("ENSCRIPT");
1141   handle_env_options ("GENSCRIPT");
1142
1143   /* Command line arguments. */
1144   handle_options (argc, argv);
1145
1146   /*
1147    * Check options which have some validity conditions.
1148    */
1149
1150   /*
1151    * Save the user-specified escape char so ^@escape{default} knows
1152    * what to set.
1153    */
1154   default_escape_char = escape_char;
1155
1156   /* Input encoding. */
1157
1158   found = 0;
1159   for (i = 0; !found && encodings[i].names[0]; i++)
1160     for (j = 0; j < 3; j++)
1161       if (encodings[i].names[j] != NULL && MATCH (encodings[i].names[j],
1162                                                   encoding_name))
1163         {
1164           /* Found a match for this encoding.  Use the first
1165              "official" name. */
1166
1167           encoding = encodings[i].encoding;
1168           xfree (encoding_name);
1169           encoding_name = xstrdup (encodings[i].names[0]);
1170
1171           if (nl < 0)
1172             nl = encodings[i].nl;
1173           bs = encodings[i].bs;
1174           found = 1;
1175           break;
1176         }
1177   if (!found)
1178     FATAL ((stderr, _("unknown encoding: %s"), encoding_name));
1179
1180   /* Fonts. */
1181
1182   /* Default font for landscape, 2 column printing is Courier 7. */
1183   if (!user_body_font_defined && landscape && num_columns > 1)
1184     Fpt.w = Fpt.h = 7.0;
1185
1186   /* Cache for font AFM information. */
1187   afm_cache = strhash_init ();
1188   afm_info_cache = strhash_init ();
1189
1190   /* Open AFM library. */
1191   afm_error = afm_create (afm_path, verbose, &afm);
1192   if (afm_error != AFM_SUCCESS)
1193     {
1194       char buf[256];
1195
1196       afm_error_to_string (afm_error, buf);
1197       FATAL ((stderr, _("couldn't open AFM library: %s"), buf));
1198     }
1199
1200   /*
1201    * Save default Fpt and Fname since special escape 'font' can change
1202    * it and later we might want to switch back to the "default" font.
1203    */
1204   default_Fpt.w = Fpt.w;
1205   default_Fpt.h = Fpt.h;
1206   default_Fname = Fname;
1207   default_Fencoding = encoding;
1208
1209   /* Register that document uses at least these fonts. */
1210   strhash_put (res_fonts, Fname, strlen (Fname) + 1, NULL, NULL);
1211   strhash_put (res_fonts, HFname, strlen (HFname) + 1, NULL, NULL);
1212
1213   /* As a default, download both named fonts. */
1214   strhash_put (download_fonts, Fname, strlen (Fname) + 1, NULL, NULL);
1215   strhash_put (download_fonts, HFname, strlen (HFname) + 1, NULL, NULL);
1216
1217   /* Read font's character widths and character types. */
1218   read_font_info ();
1219
1220   /* Count the line indentation. */
1221   line_indent = parse_float (line_indent_spec, 1, 1);
1222
1223   /* List media names. */
1224   if (list_media)
1225     {
1226       printf (_("known media:\n\
1227 name             width\theight\tllx\tlly\turx\tury\n\
1228 ------------------------------------------------------------\n"));
1229       for (mentry = media_names; mentry; mentry = mentry->next)
1230         printf ("%-16s %d\t%d\t%d\t%d\t%d\t%d\n",
1231                 mentry->name, mentry->w, mentry->h,
1232                 mentry->llx, mentry->lly, mentry->urx, mentry->ury);
1233       /* Exit after listing. */
1234       exit (0);
1235     }
1236
1237   /* Output media. */
1238   for (mentry = media_names; mentry; mentry = mentry->next)
1239     if (strcmp (media_name, mentry->name) == 0)
1240       {
1241         media = mentry;
1242         break;
1243       }
1244   if (media == NULL)
1245     FATAL ((stderr, _("do not know anything about media \"%s\""), media_name));
1246
1247   if (margins_spec)
1248     {
1249       /* Adjust marginals. */
1250       for (i = 0; i < 4; i++)
1251         {
1252           if (*margins_spec == '\0')
1253             /* All done. */
1254             break;
1255
1256           if (*margins_spec == ':')
1257             {
1258               margins_spec++;
1259               continue;
1260             }
1261
1262           j = atoi (margins_spec);
1263           for (; *margins_spec != ':' && *margins_spec != '\0'; margins_spec++)
1264             ;
1265           if (*margins_spec == ':')
1266             margins_spec++;
1267
1268           switch (i)
1269             {
1270             case 0:             /* left */
1271               media->llx = j;
1272               break;
1273
1274             case 1:             /* right */
1275               media->urx = media->w - j;
1276               break;
1277
1278             case 2:             /* top */
1279               media->ury = media->h - j;
1280               break;
1281
1282             case 3:             /* bottom */
1283               media->lly = j;
1284               break;
1285             }
1286         }
1287       MESSAGE (1,
1288                (stderr,
1289                 _("set new marginals for media `%s' (%dx%d): llx=%d, lly=%d, urx=%d, ury=%d\n"),
1290                 media->name, media->w, media->h, media->llx, media->lly,
1291                 media->urx, media->ury));
1292     }
1293
1294   /* Page label format. */
1295   if (MATCH (page_label_format, "short"))
1296     page_label = LABEL_SHORT;
1297   else if (MATCH (page_label_format, "long"))
1298     page_label = LABEL_LONG;
1299   else
1300     FATAL ((stderr, _("illegal page label format \"%s\""), page_label_format));
1301
1302   /* Non-printable format. */
1303   if (MATCH (npf_name, "space"))
1304     non_printable_format = NPF_SPACE;
1305   else if (MATCH (npf_name, "questionmark"))
1306     non_printable_format = NPF_QUESTIONMARK;
1307   else if (MATCH (npf_name, "caret"))
1308     non_printable_format = NPF_CARET;
1309   else if (MATCH (npf_name, "octal"))
1310     non_printable_format = NPF_OCTAL;
1311   else
1312     FATAL ((stderr, _("illegal non-printable format \"%s\""), npf_name));
1313
1314   /* Mark wrapped lines style. */
1315   if (mark_wrapped_lines_style_name)
1316     {
1317       if (MATCH (mark_wrapped_lines_style_name, "none"))
1318         mark_wrapped_lines_style = MWLS_NONE;
1319       else if (MATCH (mark_wrapped_lines_style_name, "plus"))
1320         mark_wrapped_lines_style = MWLS_PLUS;
1321       else if (MATCH (mark_wrapped_lines_style_name, "box"))
1322         mark_wrapped_lines_style = MWLS_BOX;
1323       else if (MATCH (mark_wrapped_lines_style_name, "arrow"))
1324         mark_wrapped_lines_style = MWLS_ARROW;
1325       else
1326         FATAL ((stderr, _("illegal style for wrapped line marker: \"%s\""),
1327                 mark_wrapped_lines_style_name));
1328     }
1329
1330   /* Count N-up stuffs. */
1331   for (i = 0; ; i++)
1332     {
1333       ui = nup >> i;
1334
1335       if (ui == 0)
1336         FATAL ((stderr, _("illegal N-up argument: %d"), nup));
1337
1338       if (ui & 0x1)
1339         {
1340           if (ui != 1)
1341             FATAL ((stderr, _("N-up argument must be power of 2: %d"), nup));
1342
1343           nup_exp = i;
1344           break;
1345         }
1346     }
1347
1348   nup_rows = nup_exp / 2 * 2;
1349   if (nup_rows == 0)
1350     nup_rows = 1;
1351   nup_columns = (nup_exp + 1) / 2 * 2;
1352   if (nup_columns == 0)
1353     nup_columns = 1;
1354
1355   nup_landscape = nup_exp & 0x1;
1356
1357
1358   /*
1359    * Count output media dimensions.
1360    */
1361
1362   if (landscape)
1363     {
1364       d_page_w = media->ury - media->lly;
1365       d_page_h = media->urx - media->llx;
1366     }
1367   else
1368     {
1369       d_page_w = media->urx - media->llx;
1370       d_page_h = media->ury - media->lly;
1371     }
1372
1373   /*
1374    * Count N-up page width, height and scale.
1375    */
1376
1377   if (nup_landscape)
1378     {
1379       nup_width = media->ury - media->lly;
1380       nup_height = media->urx - media->llx;
1381     }
1382   else
1383     {
1384       nup_width = media->urx - media->llx;
1385       nup_height = media->ury - media->lly;
1386     }
1387
1388   {
1389     double w, h;
1390
1391     w = ((double) nup_width - (nup_columns - 1) * nup_xpad) / nup_columns;
1392     h = ((double) nup_height - (nup_rows - 1) * nup_ypad) / nup_rows;
1393
1394     nup_width = w;
1395     nup_height = h;
1396
1397     w = w / (media->urx - media->llx);
1398     h = h / (media->ury - media->lly);
1399
1400     nup_scale = w < h ? w : h;
1401   }
1402
1403   /*
1404    * Underlay (this must come after output media dimensions, because
1405    * `underlay position' needs them).
1406    */
1407   if (underlay != NULL)
1408     {
1409       strhash_put (res_fonts, ul_font, strlen (ul_font) + 1, NULL, NULL);
1410       underlay = escape_string (underlay);
1411     }
1412
1413   /* Underlay X-coordinate. */
1414   ul_x = strtod (ul_position, &cp);
1415   if (cp == ul_position)
1416     {
1417     malformed_position:
1418       FATAL ((stderr, _("malformed underlay position: %s"), ul_position));
1419     }
1420   if (ul_position[0] == '-')
1421     ul_x += d_page_w;
1422
1423   /* Underlay Y-coordinate. */
1424   ul_y = strtod (cp, &cp2);
1425   if (cp2 == cp)
1426     goto malformed_position;
1427   if (cp[0] == '-')
1428     ul_y += d_page_h;
1429
1430   /* Underlay Angle. */
1431   if (!ul_angle_p)
1432     /* No angle given, count the default. */
1433     ul_angle = (atan2 (-d_page_h, d_page_w) / 3.14159265 * 180);
1434
1435   /* Underlay style. */
1436   if (strcmp (ul_style_str, "outline") == 0)
1437     ul_style = UL_STYLE_OUTLINE;
1438   else if (strcmp (ul_style_str, "filled") == 0)
1439     ul_style = UL_STYLE_FILLED;
1440   else
1441     FATAL ((stderr, _("illegal underlay style: %s"), ul_style_str));
1442
1443   /*
1444    * Header.  Note! The header attributes can be changed from
1445    * the `.hdr' files, these are only the defaults.
1446    */
1447
1448   d_header_w = d_page_w;
1449   switch (header)
1450     {
1451     case HDR_NONE:
1452       d_header_h = 0;
1453       break;
1454
1455     case HDR_SIMPLE:
1456       d_header_h = HFpt.h * 1.5;
1457       break;
1458
1459     case HDR_FANCY:
1460       d_header_h = 36;
1461       break;
1462     }
1463
1464   /* Help highlight. */
1465   if (help_highlight)
1466     {
1467       /* Create description with states. */
1468       printf (_("Highlighting is supported for the following languages and file formats:\n\n"));
1469       fflush (stdout);
1470
1471       buffer_clear (&buffer);
1472       buffer_append (&buffer, states_binary);
1473       buffer_append (&buffer, " -f \"");
1474       buffer_append (&buffer, states_config_file);
1475       buffer_append (&buffer, "\" -p \"");
1476       buffer_append (&buffer, states_path);
1477       buffer_append (&buffer, "\" -s describe_languages ");
1478       buffer_append (&buffer, enscript_library);
1479       buffer_append (&buffer, "/hl/*.st");
1480
1481       system (buffer_ptr (&buffer));
1482       exit (0);
1483     }
1484
1485   /*
1486    * And now to the main business.  The actual input file processing
1487    * is divided to two parts: PostScript generation and everything else.
1488    * The PostScript generation is handled in the conventional way, we
1489    * process the input and generate PostScript.  However all other input
1490    * languages will be handled with States, we only pass enscript's
1491    * options to the states pre-filter and dump output.
1492    */
1493   if (output_language_pass_through)
1494     {
1495       char *start_state;
1496       Buffer cmd;
1497       char intbuf[256];
1498
1499       /* The States output generation. */
1500
1501       /* Resolve the start state. */
1502       if (hl_start_state)
1503         start_state = hl_start_state;
1504       else if (highlight)
1505         start_state = NULL;
1506       else
1507         start_state = "passthrough";
1508
1509       /* Create the states command. */
1510
1511       buffer_init (&cmd);
1512
1513       buffer_append (&cmd, states_binary);
1514       buffer_append (&cmd, " -f \"");
1515       buffer_append (&cmd, states_config_file);
1516       buffer_append (&cmd, "\" -p \"");
1517       buffer_append (&cmd, states_path);
1518       buffer_append (&cmd, "\" ");
1519
1520       if (verbose > 0)
1521         buffer_append (&cmd, "-v ");
1522
1523       if (start_state)
1524         {
1525           buffer_append (&cmd, "-s");
1526           buffer_append (&cmd, start_state);
1527           buffer_append (&cmd, " ");
1528         }
1529
1530       buffer_append (&cmd, "-Dcolor=");
1531       buffer_append (&cmd, states_color ? "1" : "0");
1532       buffer_append (&cmd, " ");
1533
1534       buffer_append (&cmd, "-Dstyle=");
1535       buffer_append (&cmd, states_highlight_style);
1536       buffer_append (&cmd, " ");
1537
1538       buffer_append (&cmd, "-Dlanguage=");
1539       buffer_append (&cmd, output_language);
1540       buffer_append (&cmd, " ");
1541
1542       buffer_append (&cmd, "-Dnum_input_files=");
1543       sprintf (intbuf, "%d", optind == argc ? 1 : argc - optind);
1544       buffer_append (&cmd, intbuf);
1545       buffer_append (&cmd, " ");
1546
1547       buffer_append (&cmd, "-Ddocument_title=\'");
1548       if ((cp = shell_escape (title)) != NULL)
1549         {
1550           buffer_append (&cmd, cp);
1551           free (cp);
1552         }
1553       buffer_append (&cmd, "\' ");
1554
1555       buffer_append (&cmd, "-Dtoc=");
1556       buffer_append (&cmd, toc ? "1" : "0");
1557
1558       /* Additional options for states? */
1559       if (helper_options['s'])
1560         {
1561           Buffer *opts = helper_options['s'];
1562
1563           buffer_append (&cmd, " ");
1564           buffer_append_len (&cmd, buffer_ptr (opts), buffer_len (opts));
1565         }
1566
1567       /* Append input files. */
1568       for (i = optind; i < argc; i++)
1569         {
1570           char *cp;
1571           if ((cp = shell_escape (argv[i])) != NULL)
1572             {
1573               buffer_append (&cmd, " \'");
1574               buffer_append (&cmd, cp);
1575               buffer_append (&cmd, "\'");
1576               free (cp);
1577             }
1578         }
1579
1580       /* And do the job. */
1581       if (is_open (&is, stdin, NULL, buffer_ptr (&cmd)))
1582         {
1583           open_output_file ();
1584           process_file ("unused", &is, 0);
1585           is_close (&is);
1586         }
1587
1588       buffer_uninit (&cmd);
1589     }
1590   else
1591     {
1592       /* The conventional way. */
1593
1594       /* Highlighting. */
1595       if (highlight)
1596         {
1597           char fbuf[256];
1598
1599           /* Create a highlight input filter. */
1600           buffer_clear (&buffer);
1601           buffer_append (&buffer, states_binary);
1602           buffer_append (&buffer, " -f \"");
1603           buffer_append (&buffer, states_config_file);
1604           buffer_append (&buffer, "\" -p \"");
1605           buffer_append (&buffer, states_path);
1606           buffer_append (&buffer, "\"");
1607
1608           if (verbose > 0)
1609             buffer_append (&buffer, " -v");
1610
1611           if (hl_start_state)
1612             {
1613               buffer_append (&buffer, " -s ");
1614               buffer_append (&buffer, hl_start_state);
1615             }
1616
1617           buffer_append (&buffer, " -Dcolor=");
1618           buffer_append (&buffer, states_color ? "1" : "0");
1619
1620           buffer_append (&buffer, " -Dstyle=");
1621           buffer_append (&buffer, states_highlight_style);
1622
1623           buffer_append (&buffer, " -Dfont_spec=");
1624           buffer_append (&buffer, Fname);
1625           sprintf (fbuf, "@%g/%g", Fpt.w, Fpt.h);
1626           buffer_append (&buffer, fbuf);
1627
1628           /* Additional options for states? */
1629           if (helper_options['s'])
1630             {
1631               Buffer *opts = helper_options['s'];
1632
1633               buffer_append (&buffer, " ");
1634               buffer_append_len (&buffer,
1635                                  buffer_ptr (opts), buffer_len (opts));
1636             }
1637
1638           buffer_append (&buffer, " \'%s\'");
1639
1640           input_filter = buffer_copy (&buffer);
1641           input_filter_stdin = "-";
1642         }
1643
1644       /* Table of Contents. */
1645       if (toc)
1646         {
1647           toc_fp = tmpfile ();
1648           if (toc_fp == NULL)
1649             FATAL ((stderr, _("couldn't create temporary toc file: %s"),
1650                     strerror (errno)));
1651         }
1652
1653
1654       /*
1655        * Process files.
1656        */
1657
1658       if (optind == argc)
1659         {
1660           /* stdin's modification time is the current time. */
1661           memcpy (&mod_tm, &run_tm, sizeof (run_tm));
1662
1663           if (is_open (&is, stdin, NULL, input_filter))
1664             {
1665               /* Open output file. */
1666               open_output_file ();
1667               process_file (title_given ? title : "", &is, 0);
1668               is_close (&is);
1669             }
1670         }
1671       else
1672         {
1673           for (; optind < argc; optind++)
1674             {
1675               if (is_open (&is, NULL, argv[optind], input_filter))
1676                 {
1677                   struct stat stat_st;
1678
1679                   /* Get modification time. */
1680                   if (stat (argv[optind], &stat_st) == 0)
1681                     {
1682                       tim = stat_st.st_mtime;
1683                       tm = localtime (&tim);
1684                       memcpy (&mod_tm, tm, sizeof (*tm));
1685
1686                       /*
1687                        * Open output file.  Output file opening is delayed to
1688                        * this point so we can optimize the case when a
1689                        * non-existing input file is printed => we do nothing.
1690                        */
1691                       open_output_file ();
1692
1693                       process_file (argv[optind], &is, 0);
1694                     }
1695                   else
1696                     ERROR ((stderr, _("couldn't stat input file \"%s\": %s"),
1697                             argv[optind],
1698                             strerror (errno)));
1699
1700                   is_close (&is);
1701                 }
1702             }
1703         }
1704
1705       /* Table of Contents. */
1706       if (toc)
1707         {
1708           /* This is really cool... */
1709
1710           /* Set the printing options for toc. */
1711           toc = 0;
1712           special_escapes = 1;
1713           line_numbers = 0;
1714
1715           if (fseek (toc_fp, 0, SEEK_SET) != 0)
1716             FATAL ((stderr, _("couldn't rewind toc file: %s"),
1717                     strerror (errno)));
1718
1719           memcpy (&mod_tm, &run_tm, sizeof (run_tm));
1720           if (is_open (&is, toc_fp, NULL, NULL))
1721             {
1722               process_file (_("Table of Contents"), &is, 1);
1723               is_close (&is);
1724             }
1725         }
1726
1727       /* Give trailer a chance to dump itself. */
1728       dump_ps_trailer ();
1729
1730       /*
1731        * Append ^D to the end of the output?  Note! It must be ^D followed
1732        * by a newline.
1733        */
1734       if (ofp != NULL && append_ctrl_D)
1735         fprintf (ofp, "\004\n");
1736     }
1737
1738   /* Close output file. */
1739   close_output_file ();
1740
1741   /* Tell how things went. */
1742   if (ofp == NULL)
1743     {
1744       /*
1745        * The value of <ofp> is not reset in close_output_file(),
1746        * this is ugly but it saves one flag.
1747        */
1748       MESSAGE (0, (stderr, _("no output generated\n")));
1749     }
1750   else if (output_language_pass_through)
1751     {
1752       if (output_file == OUTPUT_FILE_NONE)
1753         MESSAGE (0, (stderr, _("output sent to %s\n"),
1754                      printer ? printer : _("printer")));
1755       else
1756         MESSAGE (0, (stderr, _("output left in %s\n"),
1757                      output_file == OUTPUT_FILE_STDOUT ? "-" : output_file));
1758     }
1759   else
1760     {
1761       unsigned int real_total_pages;
1762
1763       if (nup > 1)
1764         {
1765           if (total_pages > 0)
1766             real_total_pages = (total_pages - 1) / nup + 1;
1767           else
1768             real_total_pages = 0;
1769         }
1770       else
1771         real_total_pages = total_pages;
1772
1773       /* We did something, tell what.  */
1774       MESSAGE (0, (stderr, _("[ %d pages * %d copy ]"), real_total_pages,
1775                    num_copies));
1776       if (output_file == OUTPUT_FILE_NONE)
1777         MESSAGE (0, (stderr, _(" sent to %s\n"),
1778                      printer ? printer : _("printer")));
1779       else
1780         MESSAGE (0, (stderr, _(" left in %s\n"),
1781                      output_file == OUTPUT_FILE_STDOUT ? "-" : output_file));
1782       if (num_truncated_lines)
1783         {
1784           retval |= 2;
1785           MESSAGE (0, (stderr, _("%d lines were %s\n"), num_truncated_lines,
1786                        line_end == LE_TRUNCATE
1787                        ? _("truncated") : _("wrapped")));
1788         }
1789
1790       if (num_missing_chars)
1791         {
1792           retval |= 4;
1793           MESSAGE (0, (stderr, _("%d characters were missing\n"),
1794                        num_missing_chars));
1795           if (list_missing_characters)
1796             {
1797               MESSAGE (0, (stderr, _("missing character codes (decimal):\n")));
1798               do_list_missing_characters (missing_chars);
1799             }
1800         }
1801
1802       if (num_non_printable_chars)
1803         {
1804           retval |= 8;
1805           MESSAGE (0, (stderr, _("%d non-printable characters\n"),
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
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
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
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
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: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
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, --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, --escapes[=CHAR]       enable special escape interpretation\n"),
2533           program);
2534
2535   printf (_("\
2536   -E, --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, --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, --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
2641 version ()
2642 {
2643   printf ("%s\n\
2644 Copyright (C) 2003 Markku Rossi.\n\
2645 GNU enscript comes with NO WARRANTY, to the extent permitted by law.\n\
2646 You may redistribute copies of GNU enscript under the terms of the GNU\n\
2647 General Public License.  For more information about these matters, see\n\
2648 the files named COPYING.\n",
2649           version_string);
2650 }